@lookingfor

Как сделать правильную организацию «напоминаний» на уровне БД?

Добрый день! Ребят, кто делал напоминания(задания по расписанию) в своих проектах!? поделитесь опытом, как лучше все организовать, хранить в БД, для того чтобы потом было удобно и быстро сделать выборку по записям.

Вот к примеру есть таблица TASK, и нужно чтобы он выполнялся по определенному расписанию. Соответственно нужна например выборка тасков которые будут выполнятся за сегодня или например за неделю в общем в зависимости от того как указал юзер.

Варианты которые с ходу пришли на ум:

1) Добавляем поле-строку в формате cron к примеру "0 0 * * *" - (каждый день в полночь)

Очень удобно, но как потом делать выборку, нужно средствами бд парсить вначале эту строку потом по результату составлять запрос, но самое главное когда таблица разрастется парсин плохо скажется на скорости выборки.

2) Тоже самое, но только каждый параметр выносим в отдельное поле и я так понимаю, если нужно указывать несколько месяцев или несколько дней, то храним их как битовую маску. Плюс в том что избавляемся от парсинга, но получаем новую проблему выборка по маске, на сколько она быстро и удобно будет работать?

3) Делаем полноценную структуру таблиц, под месяцы, дни, часы свою таблицу со связями.

4) Отдаем все серверу приложения, держим отдельный кэш для этих целей и синхронизируем его с бд в принципе можно использовать формат cron как в первом варианте

Ни один способ мне интуитивно не нравится. Посоветуйте, как лучше сделать те кто уже сталкивался с подобными задачами. Может есть более красивый и правильный способ.

База у меня postgresql.
  • Вопрос задан
  • 2957 просмотров
Решения вопроса 1
leahch
@leahch
3D специалист. Dолго, Dорого, Dерьмово.
У меня это так реализовано. Все даты храним в формате таймстампа, это проще для хранения и вычислений. Исключения храним в произвольной форме ключ/значение, у меня реализованы только часы ключами from to. Задания бывают в нескольких состояниях: активная, неактивная, просроченная, будущая, бесконечная.
Само задание выглядит примерно так (это как раз и есть запись в таблице):
- старт - дата старта (таймстамп)
- дельта - смещение реального старта от начала старта (это особенность моего приложения, нафиг не нужна, просто чтобы код не портить)
- период - период повторения (таймстамп в виде timedelta)
- продолжительность (должна быть меньше или равна периодутаймстамп в виде timedelta)
- стоп - дата окончания (может отсутствовать, таймстамп в виде timedelta)
- count - число повторений от даты старта (если есть, то обязана ставиться дата окончания, int )
- исключения (например без понедельников или только чётные часы, или время с 10 до 19)
- активна (да/нет)

Работает так.
- Выбираем все активные задачи, где старт меньше нужной даты и стоп равен нулю или больше нужной даты.
- Пробегаемся по всем заданиям и для каждого рассчитываем попадание в нашу дату.
- Если есть исключения, смотрим, не попадаем ли в них.
- Если есть count, рассчитываем его
- Если вместе с count стоят исключения, то пересчитываем count для каждого периода - самый ресурсоёмкий процесс, приходится пробегать по всем интервалам с начала старта.

Базовый класс (на питоне):
# функция преобразования питоновского класса datetime в timestamp
# возвращает timestamp (int)
def total_seconds(td):
    seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.**6
    ret = 0 if seconds < 0 else seconds
    return ret

class interval(object):
# здесь должно быть все понятно
# self  - указатель на сам класс, особенность питона, на другом языке этот параметр опускаем
# вместо self на других языках используем this или аналог
    def __init__(self, start, delta, duration, period):
        self.start = start
        self.delta = delta
        self.duration = duration
        self.period = period

# вычисление последнего интервала от нужной даты
# возвращает две даты от и до
    def last(self, date):
        _start = self.start+self.delta 
        mul = int(total_seconds(date - _start)/total_seconds(self.period))
        at = _start+(self.period*mul)
        to = at+self.duration
        return (at,to)

# является ли интервал активный на нужную дату
# возвращает true/false
    def isLive(self, date):
        at, to = self.last(date)
        f1 = date >= at
        f2 = date < to
        return f1 and f2

# вычисление следующего интервала от нужной даты
# возвращает две даты от и до
    def next(self, date):
        at, to = self.last(date)
        return (at + self.period, to + self.period)


Остальное, не привожу, так как много и надеюсь, допишите сами.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
DmitriyEntelis
@DmitriyEntelis
Думаю за деньги
Как бы я это реализовал:
В произвольном, удобном для Вас виде храним периодичность (можно как Вы написали в первом варианте, в формате крона). Дополнительно, отдельным полем храним дату следующего наступления события. Соответственно все выборки ведем просто по дате.
Это для случая если у задачи нет cтатуса выполнения, не нужно смотреть на прошлые/будущие задачи, итд.

Если хочется сделать как в google calendar / icloud - где каждый экземпляр этой повторяющейся задачи по сути достаточно независим - становится чуть сложнее.
Получается 2 таблицы: задача-прототип где хранится периодичность, дата первого запуска и опционально дата последнего, - и задача-экземпляр где хранится конкретная дата и ссылка на прототип.

Сумбурно получилось, не проснулся еще :)
Если что - давайте обсудим :)
Ответ написан
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы