Pull to refresh
174
Karma
0
Rating
Денис @ur001

Пользователь

Как считать счётчики и не сбиться со счёта

Ну хорошо :) Вы используете очередь. В очереди пересчитываете счётчик полностью, т.к. инкрементное обновление считаете ненадёжным. Благодаря «схлопыванию» удаётся снизить накладные расходы, т.к. «при реактивном изменении счетчика (100500 хомячков в секунду) расчет производится только 1 раз на over 9000 фактических изменений». Я правильно вас понял?

Как считать счётчики и не сбиться со счёта

Откатывать транзакцию или нет — зависит от бизнес-требований. Можем ли мы совершить действие если обновлние сётчика не гарантировано? Можем ли мы позволить себе иметь задержку при рассчёте счётчика? Можем ли мы не менять значение счётчика, если было подряд +1 и -1, или, например нам важно получить рейтинг 100, а потом обратно 99, т.к. при достижении 100 срабатывает триггер и пост становится золотым. Очереди — это замечательно, очень удобно и, во многих случаях незаменимо. Они просто не имеют, ИМХО, прямого отношения к теме топика.

Как считать счётчики и не сбиться со счёта

Логично что отправка письма в фоне с повтором при неудаче, т.к. уведомление тут вторично. Во всех проектах отправка email/sms/push и прочих уведомлений делается так. С утвердительным ответом от платёжной системы наоборот, т.к. оплата важнее. А к чему это вы?

Как считать счётчики и не сбиться со счёта

Рейтинг пользователя на хабе скорее всего да, не вызовет большого перебора, немного ступил. Но одновременно с ним нужно пересчитывать ещё рейтинг хаба. Там будет перебор всех постов хаба. Но мне всё же странно, почему вы наставиваете на полном пересчёте вместо инкрементного обновления, когда оно намного производительнее. Вам не верится что инкрементное обновление может не сбиться? :) Думаете что 1+1+1 в какой-то момент может стать 2 или 4, если повторять эту операцию много раз?

Как считать счётчики и не сбиться со счёта

Если схлопывать несколько обновлений в один, то готов с натяжкой согласиться. Тем не менее, два SELECT SUM() имеет сложность O(N1) + O(N2), где N1 и N2 число постов в старом и новом хабе (это при наличии индексов, при отсутствии это число всех постов на Хабре). Т.е. SQL, каким бы волшебным он не казался, честно пробежится по всем постам хаба и просуммирует рейтинг. Если схлопывания, про который вы писали, нет (а это иногда ограничение бизнес-требований), при большой соц. сети с кучей показателей, то полный пересчёт на производительности скажется драматически.

Как считать счётчики и не сбиться со счёта

Ок. Нам в очередь пришёл id поста и мы посмотрели по changelog-у, или получили сразу в виде параметров помимо post_id следующие поля: old_hub_id, old_is_published, old_is_deleted, old_user_id, и из базы (которая к этому моменту кстати могла опять измениться) или как-то ещё вычислили актуальные на момент срабатывания счётчика hub_id, is_published, is_deleted, user_id. Да, может это звучит необычно, но на dirty реализована передача черновика другому пользователю, по этому предположим что автор тоже может измениться. Какой вы напишите обработчик для обновления вашего «кеша» рейтинга пользователя на хабе?

Как считать счётчики и не сбиться со счёта

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

Ну тут мы кажется сошлись во мнениях, я в комментарии написал то же самое.


В случае изменения рейтинга/удаления/отправки в черновик в очередь отправится id поста.

Ну допустим пользователь отредактировал пост сменив хаб и скрыв в черновики. Как мы узнаем что хаб был сменён (а значит нужно вычесть рейтинг поста из рейтинга в старом хабе) и что пост не был в черновиках до этого?


кешем будет таблице счётчиков [hub_id, user_id] --> rating с сортировкой по убыванию рейтинга.

Ну если я потом смогу по этому кешу сделать запрос


SELECT hub_id, rating
FROM user_hub_rating
ORDER BY rating DESC
LIMIT 10

То у нас просто разное понимание терминологии что считать кешом.

Как считать счётчики и не сбиться со счёта

А чем очередь помогает? Считать в очереди или не нет это вопрос нужен ли вам синхронный счётчик или можно иметь значение с задержкой. В некоторых случаях при отказе в обновлении счётчика нужно откатить транзакцию оменить всё действие. Иногда это вопрос скорости отклика, если на какое-то действие (лайк, к примеру) обновляется слишком много счётчиков и это приведёт к ощутимой для пользователя задержке при синхронном обновлении, то нужно перенести часть рассчётов в очередь.


Допустим у вас в профиле список хабов, в которые вы внесли максимальный вклад. Это запрос по таблице счётчиков [hub_id, user_id] --> rating с сортировкой по убыванию рейтинга. Рейтинг на хабе это, к примеру, сумма рейтингов опубликованных и неудалённых постов пользователя на хабе. Что вы будете класть в очередь? Как на основе этого будете обновлять счётчик? Чем поможет в этом случае кеш?

Как считать счётчики и не сбиться со счёта

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


Идея ловить тригерами изменения и отправлять на обработку в очередь на той же БД (PGQ) мне, в принципе понравилась. Этот подход позволял отлавливать только изменения, при этом писать логику их обработки на нормальном питоне. Но сама PGQ по сравнению с Celery отвратительна — это раз. Триггеры отлавливающие изменения приходилось обновлять вместе с миграцией БД, короче не слишком приятно выходило. Но потенциал в этом подходе есть.

Как считать счётчики и не сбиться со счёта

А, понятно. Я сначала не понял, что вы про хранение счётчиков только в кеше. Я тут ответил.

Как считать счётчики и не сбиться со счёта

Иногда такой подход оправдан. Но для некоторых вещей просто неприемлем. Например, из Трипстера: запись на экскурсию с ограниченным числом участников. После того, как максимальное число людей оплатило, регистрация на событие закрывается, неоплаченные заказы отменяются, всем участникам и гиду приходят уведомления.


Второй вариант — значения счётчиков вам постоянно нужны для выборок.

Как считать счётчики и не сбиться со счёта

Да, я имел в виду именно это. И да, дело, по большей части, в дороговизне полного пересчёта на каждое изменение. Никакое кеширование, тут помочь не может — что именно вы будете кешировать? :)

Как считать счётчики и не сбиться со счёта

А понял, спасибо

Как считать счётчики и не сбиться со счёта

Не, не могу помочь. На Хабре я давно не работаю

Как считать счётчики и не сбиться со счёта

У нас тоже финансовые операции присутствуют, и, некоторые напрямую зависят от счётчиков. По этому когда я пришёл на проект и обнаружил MyISAM, первым делом бросился переводить на InnoDB и заворачивать в транзакции. Но к тому моменту накопилось столько мусорного кода, что банально не найдены и необеззаражены все места где что-то типа


order = Order.objects.get(pk=order_id)
order.status = Order.STATUS_PENDING_PAYMENT
order.save()

Т.е. без транзакций и SELECT FOR UPDATE, без, хотя бы save(update_fields=['status']) и прочее. И, конечно, это ломает счётчики.


А кто у вас добавляет в очередь пометку о необходимости обновить счётчик?

Как считать счётчики и не сбиться со счёта

Ну так я же старался проще :) Подскажите что кажется сложным. Я надеялся, что самым понятным будет код, кроме которого можно особо ничего и не читать

Как считать счётчики и не сбиться со счёта

Хм… Не до конца понял. В одном из проектов я тоже делал возможность пересчёта одного или нескольких счётчиков. Счётчики задаются декларативными правилами типа:


class Experience(models.Model):
    review_count = models.PositiveSmallIntegerField(u"Число отзывов", default=0, db_index=True)
    review_rated_count = models.PositiveSmallIntegerField(u"Число отзывов с оценками", default=0)
    review_rating_sum = models.FloatField(u"Сумма оценок", default=0)

    class Counters(Counters):
        review_count = Counter('Review.experience', lambda review: review.published)

        review_rated_count = Counter(
            'Review.experience', 
            lambda review: review.published and bool(review.rate)
        )

        review_rating_sum = Counter(
            'Review.experience',
            lambda review: review.rate if review.published and review.rate is not None else 0
        )

class Review(models.Model):
        experience = models.ForeignKey(Experience, related_name='reviews')

Т.е. тут тоже обычные счётчики даже не в отдельной таблице, а прямо в основных моделях.


В вашем случае increment( counterName, ±1) вызывается вручную в методах публикации/распубликации или это происходит автоматически на основе конфигурации? Как примерно устроена конфигурация? Как работает пересчёт?

Как считать счётчики и не сбиться со счёта

А расскажете? Как было, что было проще? И да, какие из моих рассуждений показались вам заумными, может я переформулирую? :)

Как считать счётчики и не сбиться со счёта

На картинке счётчик постов, если приглядеться :)

Emotiv Insight — нейроинтерфейс за 229$

Со сроками они конечно облажались, я уже почти было перестал надеяться, но вчера, наконец-то коробка поступила на склад LiteMF, и через 1-2 недели должна быть у меня :) Правда обошлось в 3 раза дороже (включая пересылку LiteFM $60, выяснилось что эта фигня может быть доставлена только USPS Express Mail).

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity