Задать вопрос
@matperez

Как реализовать устаревание документов в MogonDB?

Есть коллекция документов в монге, которые представляют из себя, скажем, заказы в интернет магазине.
{"_id": ..., "status": "new", "expiring_at": 2018-02-04 23:40:40.493Z}


Каждый заказ, в отсутствии оплаты, должен через определенное время протухнуть. При протухании ему нужно установить соответствующий статус (expired) и сделать запись в логе об этом. Допустимо помечать заказы протухшими чуть позже, чем указано в его свойствах, но никогда раньше.

Как это правильно реализовать?

Свои мысли:
1. Есть ttl indexes, которые, за счет фонового процесса, позволяют задавать время жизни записям, но мне не нужно их удалять, только менять статус. Кроме того, нечего будет записать в лог.
2. Каждый раз при обращении к записи, проверять не истекло ли ее время жизни (т.е. делать статус вычисляемым значением от текущего времени и свойств заказа) тоже не хочется.
3. Использовать оптимистическую блокировку. При записи данных пытаться обновлять документ с заданной версией, периодически искать и помечать устаревшие записи через findAndUpdate($set status, $inc vesion, new true). Тут смущает, что это операция над одной записью. Просто update(), который умеет обновлять документы пачками не сообщает на выходе какие именно документы он обновил.

// upd если логи хранить в БД в той же коллекции, то к третьему варианту еще можно кроме увеличения версии и смены статуса, делать $push в поле с логами. это позволит забить на единичные обновления через findAndUpdate и пользоваться просто update. + optimistic lock.

// upd 2
остановился все же на фоновом процессе и optimistic lock

if err := col.Find(query).All(items); err != nil {
		return fmt.Errorf("unable to search expired orders: %s", err)
	}

	for _, item := range *items {
		if err := item.Expire(); err != nil {
			return fmt.Errorf("unable to mark the order as expired: %s", err)
		}
		item.Version++
		if err := col.Update(bson.M{"_id": item.ID, "version": item.Version - 1}, &item); err != nil {
			return fmt.Errorf("unable to store expired order: %s. possible a concurrent access issue", err)
		}
	}
	return nil


Поскольку каждая сущность сохраняется отдельно, проблем с выяснением их итогового состояния нет.
  • Вопрос задан
  • 105 просмотров
Подписаться 2 Средний Комментировать
Решения вопроса 2
@RidgeA
"экспирацию" - это ж надо такое слово придумать

По теме - еще есть вариант - самостоятельный фоновый крон/демон что бы делать то что нужно.
Ответ написан
DarkRaven
@DarkRaven
разработка программного обеспечения
Устаревание, истечение срока.
А по теме - можно сделать внешнюю задачу, поставить ее в крон, чтобы она обновляла записи, как предложили выше. Можно накрутить интервал обновления, в зависимости от нагрузки на БД.

А вообще, почему бы не рассчитывать данное поле при получение данных, т.е. expired это now() - expiring_at, где now() - функция, отдающая текущее время. Данный вариант скорее более удобный, он гарантированно отсечет устаревшие.

В общем, тут вариантов масса, какой Вам удобнее.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

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