@reqww

Как связать данные баз данных PostgreSQL, TimescaleDB?

Решил хранить некоторые данные с временной меткой в TmmescaleDB, так как постгрес при большом количестве строк начинает ужасно тормозить.

Сделал все по доке Django и Timescale.
Вытянул образ в докере, запустил, базу создал.
Написал роутеры для баз данных, прописал данные в конфиге.

settings.py
DATABASES = {
    'default': {
        'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.postgresql'),
        'NAME': os.environ.get('SQL_DATABASE', 'postgres'),
        'USER': os.environ.get('SQL_USER', 'postgres'),
        'PASSWORD': os.environ.get('SQL_PASSWORD', 'pass'),
        'HOST': os.environ.get('SQL_HOST', '127.0.0.1'),
        'PORT': os.environ.get('SQL_PORT', '5432'),
    },
    'data_db': {
        'ENGINE': 'timescale.db.backends.postgresql',
        'NAME': 'data_db',
        'USER': 'postgres',
        'PASSWORD': 'pass',
        'HOST': '127.0.0.1',
        'PORT': '5432'
    }
}

DATABASE_ROUTERS = ['detector_data.dbRouter.Router', 'client.dbRouter.Router']


detector_data.dbRouter
class Router:
    """
    A router to control all database operations on models in the
    auth application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to data_db.
        """
        if model._meta.app_label == 'detector_data':
            return 'data_db'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write data models go to data_db.
        """
        if model._meta.app_label == 'detector_data':
            return 'data_db'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the auth app is involved.
        """
        return True

    def allow_migrate(self, db, app_label, model=None, **hints):
        """
        Make sure the auth app only appears in the 'data_db'
        database.
        """
        if app_label == 'detector_data':
            return db == 'data_db'
        return None


client.dbRouter
class Router:
    """
    A router to control all database operations on models in the
    auth application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to default.
        """
        if model._meta.app_label != 'detector_data':
            return 'default'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write data models go to default.
        """
        if model._meta.app_label != 'detector_data':
            return 'default'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the auth app is involved.
        """
        return True

    def allow_migrate(self, db, app_label, model=None, **hints):
        """
        Make sure the auth app only appears in the 'default'
        database.
        """
        if app_label == 'detector_data':
            return db == 'default'
        return None


В общем, тут все по стандарту.
Решил я уже радоваться, потому что миграции в обеих базах сработали, но тут при создании объекта класса DetectorData

from django.db import models
from django.contrib.postgres.indexes import BrinIndex

from timescale.db.models.fields import TimescaleDateTimeField
from timescale.db.models.managers import TimescaleManager

from detector.models import Detector

class DetectorData(models.Model):
    detector = models.ForeignKey(
        Detector, 
        verbose_name='Привязанный датчик',
        on_delete=models.DO_NOTHING, 
        related_name='data'
    )
    first_temp = models.DecimalField('Первая температура', max_digits=4, decimal_places=2)
    second_temp = models.DecimalField('Вторая температура', max_digits=4, decimal_places=2)
    third_temp = models.DecimalField('Третья температура', max_digits=4, decimal_places=2)
    humidity = models.DecimalField('Влажность', max_digits=4, decimal_places=2)
    lightning = models.DecimalField('Освещенность', max_digits=4, decimal_places=2)
    pH = models.DecimalField('Кислотность', max_digits=4, decimal_places=2)
    timestamp = TimescaleDateTimeField('Время сбор данных', interval='1 day', auto_now_add=True)

    objects = models.Manager()
    timescale = TimescaleManager()

    def __str__(self):
        return f'Отчет в {self.timestamp} от {self.detector}'

    class Meta:
        verbose_name = 'Данные датчика'
        verbose_name_plural = 'Данные датчиков'
        indexes = [BrinIndex(fields=['timestamp'])]

    @classmethod
    def create_random(cls, detector, days=None):
        data = cls.objects.create(
            detector=detector,
            first_temp=round(uniform(0, 20), 2),
            second_temp=round(uniform(0, 20), 2),
            third_temp=round(uniform(0, 20), 2),
            humidity=round(uniform(0, 20), 2),
            lightning=round(uniform(0, 20), 2),
            pH=round(uniform(0, 20), 2),
        )

        if days:
            data.timestamp = datetime.now().date()+timedelta(days=days)
            data.save()

        return data


Появилось это
6005b6001f569424626972.png

Я понимаю, что тут написано, что таблица не та.
Но что с этим делать.
А еще - как все-таки сделать так, чтобы time-series модели находились в Timescale, а все остальные - в Postgres
Для справки: модель DetectorData имеет внешний ключ на Detector (они находятся в разных базах)

Буду благодарен за любую помощь!
  • Вопрос задан
  • 239 просмотров
Решения вопроса 1
Melkij
@Melkij
PostgreSQL DBA
модель DetectorData имеет внешний ключ на Detector (они находятся в разных базах)

И кто, по вашему, будет обеспечивать работу этого FK?
Судя по всему вы неожиданно для себя получили пустую таблицу в базе, где стоит TimescaleDB. На эту таблицу и ссылается ваш FK, и, конечно, не даёт ничего писать в ссылающуюся таблицу.

Зачем вы вообще пилите на две базы? TimescaleDB лишь extension к всё тому же самому postgresql, а не отдельная СУБД. Вот и добавьте его к используемой базе напрямую.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы