- 1
- 2
- 3
- 4
//Сегодня у меня вопрос по говнокоду.
//Как у нас на говнокодике реализовано сколько непросмотренных комментариев в каждом треде?
//Неужели на каждого юзера заводится счётчик и инкрементится при добавлении в каждой теме?
//Неужели на каждого юзера на каждый комментарий заводится флаг, сообщающий какой комментарий прочитан, а какой нет?
LispGovno 03.06.2016 11:03 # 0
inkanus-gray 03.06.2016 11:19 # 0
Лень сейчас копаться в коричневом золоте исходников, но на всяких CMS делают по-другому: на каждого юзера заводится счётчик, сколько комментариев было в каждой теме на момент предыдущего просмотра. Количество непрочитанных — это разница между текущим состоянием и запомненным значением счётчика. Таких счётчиков понадобится не более, чем 20 к × 14 к = 280 к.
Если на каждый комментарий для каждого юзера заводить флаг, то всего флагов будет 330 к × 14 к ≈ 4,6 млрд.
Миллиардов, Карл!
j123123 03.06.2016 15:32 # 0
defecate-plusplus 03.06.2016 15:36 # +4
желтые все комментарии, которые моложе твоего last_visit_time
на каждый камент хранить видел ты его или нет не надо
а вот +/- каментов реально хранится в логической модели { comment_id, user_id, mark, unique(comment_id, user_id) }
chtulhu 03.06.2016 11:41 # +6
что мешает хранить дату последнего обращения к треду и сравнивать с датой создания комментария
>Количество непрочитанных — это разница между текущим состоянием и запомненным значением счётчика.
для подстветки новых комментариев не сработает
PS: исходники не смотрел, но осуждаю
inkanus-gray 03.06.2016 11:43 # 0
Действительно, запоминаем дату предыдущего просмотра. Подсвечиваем те, которые новее этой даты.
turbosnail 03.06.2016 11:46 # +5
SELECT count(id) FROM comments WHERE thread_id = $thread_id AND id > $last_user_comment_id
Dummy00001 03.06.2016 11:54 # +1
guest 03.06.2016 11:58 # 0
turbosnail 03.06.2016 11:58 # +2
roman-kashitsyn 03.06.2016 12:13 # +4
Для этого нужно гарантировать, что ID комментариев строго возрастающие, а не просто уникальные. Т.е. нужна гарантия
Id1 < Id2 <=> TimeOf(Id1) < TimeOf(Id2).
Если для небольшого форума это ОК, т.к. сиквенсы в RDBMS такое гарантируют, то в системе чуть побольше с каким-нибудь шардированием это довольно трудно гарантировать.
В принципе, с неудачным таймингом даже на одной машине такой подход может привести к неправильному поведению:
Если одновременно происходит вставка двух комментариев и чтение поста, может произойти следующая последовательность операций (псевдокод):
Так непрочитанный комментарий при следующей загрузке будет помечен прочитанным. Благо, цена ошибки невелика.
roman-kashitsyn 03.06.2016 12:31 # 0
INSERT AND COMMIT COMMENT cid1
Dummy00001 03.06.2016 14:52 # 0
Uhm... я не уверен про какой NEXT() идет речь, но типично: если текущий номер лежит в какой таблице, то его увеличение будет покрыто транзакцией. Поэтому `cid2 = NEXT(id_seq)` подвиснет и будет ждать пока результат `cid1 = NEXT(id_seq)` не будет закомиттен.
> [...] с каким-нибудь шардированием это довольно трудно гарантировать.
почти классическая проблема распределенного программирования. без синхронизации не решается.
roman-kashitsyn 03.06.2016 14:57 # +2
Если бы сиквенсы были покрыты транзакциями, подозреваю, что всё бы нереально тормозило.
Открываем доку, к примеру, постгреса:
nextval
Advance the sequence object to its next value and return that value. This is done atomically: even if multiple sessions execute nextval concurrently, each will safely receive a distinct sequence value.
Important: To avoid blocking concurrent transactions that obtain numbers from the same sequence, a nextval operation is never rolled back; that is, once a value has been fetched it is considered used and will not be returned again. This is true even if the surrounding transaction later aborts, or if the calling query ends up not using the value.
-- https://www.postgresql.org/docs/current/static/functions-sequence.html
Ну вы понели.
Dummy00001 03.06.2016 15:05 # 0
roman-kashitsyn 03.06.2016 15:12 # 0
А как ты записи в таблицы вставляешь? У тебя есть естественные уникальные ключи у всех объектов?
Мускульный AUTO_INCREMENT также работает, вот выдержка про оракловый сиквенс:
When a sequence number is generated, the sequence is incremented, independent of the transaction committing or rolling back. If two users concurrently increment the same sequence, then the sequence numbers each user acquires may have gaps, because sequence numbers are being generated by the other user.
-- https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6015.htm
Dummy00001 03.06.2016 17:03 # 0
Да. Типично. Я давно такого сам не делал. Там где я больше всего с базами работал, там модели и прочее были уже давно сделаны. (Но ни одного CREATE SEQUENCE я не видел, к слову.)
defecate-plusplus 03.06.2016 17:35 # +1
если не говорить про случаи, когда айдишник генерится арифметикой (например, ууид, или источник записи сам прекрасно следит за уникальностью (например, там арифметика его личного счетчика и его айдишника)), то:
в оракле до 11 включительно не было никакого autogenerate, поэтому для вставки генерирующегося айдишника тебе надо написать триггер, работающий before insert when (new.id is null), который будет запрашивать nextval у сиквенса
сиквенс, понятное дело, надо создать до этого триггера
меня в свое время это очень быстро заебло, и я написал процедуру, которая под заданное имя таблицы делает все вышеописанное (и сиквенс генерит, и триггер, и даже продолжает сиквенс следующим значением после максимального - ну вдруг какая ситуация случилась)
в каком-нибудь хайбейрнейте ты в аннотациях предлагаешь делать примерно то же самое (например, триггер тебе уже как бы не нужен)
иногда люди ленятся и используют один сиквенс на всю схему, сквозно нумеруя все таблицы (на мой взгляд это ад - на ровном месте получили узкое горлышко инсертов всей схемы)
иногда люди настолько охуевают от безнаказанности, что делают таблицу my_fuckin_sequence(name, value), откуда до вставки в нужную таблицу сначала читают value, а потом обратно сбрасывают value+1 (а после уже ебутся с проблемами почему у меня айдишник дублируется - ну и похуй, значит надо сделать ещё одну попытку)
ну и особенно ебанутые мускулеводы могут ебошить insert into foo ..select max(id)+1 as id...from foo
тем тоже сиквенс не нужен
LispGovno 03.06.2016 18:11 # 0
> иногда люди ленятся и используют один сиквенс на всю схему, сквозно нумеруя все таблицы (на мой взгляд это ад - на ровном месте получили узкое горлышко инсертов всей схемы)
Каюсь. А если это гуиды, то чем-нибудь это грозит? Я правда не сиквенс использовал, а встроенную функцию генерации гуида нашел.
defecate-plusplus 03.06.2016 18:34 # 0
если у тебя возникновение чего-то (рождение сущности) реально требует асинхронности с централизованной системой (кстати, не такая и редкая вещь), когда ты событие генеришь и дальше идёшь по логике, а потом оно уже по своему транспорту поедет с задержкой в базоньку - то гуид тебе очень в тему
а с другой стороны, если у тебя айди сущностей в рестах используются, и у тебя
GET /api/foos/{fooId}/subfoos/{subfooId}/bars/{barId}, то видеть эти невъебенные гуиды в урлах, невъебенные гуиды в жсонах (когда они там друг на друга ссылаются, когда обмажутся этими гуидами и ябутца окаянныя, особенно, если выгрузить массив дочерних айдей!) и просто при отладке, когда приходится всматриватся, что же там за значение - тогда гуиды нахуй не интересны
это не говоря о том, что хранить 8 байтовый инт в 2 раза дешевле, чем 16 байтовое поле
defecate-plusplus 03.06.2016 18:56 # 0
он уникальный не просто в пределах твоей системы, он уникален в принципе
его можно отдавать в чужие системы и им будет норм
LispGovno 03.06.2016 15:07 # 0
roman-kashitsyn 03.06.2016 15:14 # 0
Кстати, лол. С подходом выше каждое чтение должно ходить в мастер, т.к. меняет базу :)
defecate-plusplus 03.06.2016 15:27 # +3
для комментов треда есть created_time
для юзера для треда есть last_visit_time
время прекрасно "шардится" и ничего не блокирует в части вопроса "дай мне моё"
ntp хорошо синкает ноды с апп серверами и инстансы баз
наносекундная точность в этом вопросе лишняя
остается та же проблема
> Так непрочитанный комментарий при следующей загрузке будет помечен прочитанным
которую и решать не особо надо
roman-kashitsyn 03.06.2016 15:42 # 0
roman-kashitsyn 03.06.2016 16:18 # 0
Чего-то я туплю, сама постановка задачи к этому ведёт. Вообщем, время рулит: так можно (когда-нибудь) пошардить по постам, и на каждой шарде хранить время, когда кто пост смотрел.
И не надо никакой глобальной синхронизации для айдишников комментов, шарды независимые.
LispGovno 03.06.2016 17:29 # 0
Ну для говнофорума это может и подойдет, а для тех случаев когда надо точно определить список, например в транзакционной памяти в многопоточности или в самой реализации бд это бы уже не подошло. На время ориентироваться нельзя.
> время рулит
> пошардить
А потом на разных серверах время синхронизировалось в разное время))) Да что там, просто время синхронизировалось даже в рамках даже одной машины без шардов и уже неразбериха в постах.
Dummy00001 03.06.2016 15:10 # 0
Необязательно. Можно попытаться развести вставку коммента (с внутренним IDом) и генерацию его публичного IDа в разные транзакции.
Или просто не парится: вероятность этого race condition достаточно низкая.
guestinho 03.06.2016 16:05 # 0
даже если транзакция откатилась, то все равно next увеличился
и слава богу)
guest 03.06.2016 12:48 # +2
Вставили с 10ку комментариев одновременно на большом ресурсе и вот уже дата начнет врать что из этого ты просмотрел, а что нет.
guest 03.06.2016 19:42 # −6
Комменты не читал. Все кто выше - пидоры.
guest 04.06.2016 17:20 # +7
guestinho 04.06.2016 00:00 # 0
bormand 04.06.2016 17:41 # +1
LispGovno 04.06.2016 21:57 # 0
guestinho 04.06.2016 22:42 # 0
guestinho 04.06.2016 22:43 # 0
bormand 04.06.2016 22:46 # +1
borrnand