Для ваших вариантов:
- создать основную таблицу с помощью PRIMARY KEY (сервис, пользователь) и создать материализованное представление с помощью PRIMARY KEY (сервис, пользователь, updated_at). Но
это повредит производительности.
Материализованные представления на самом деле не сильно влияют на производительность, и путь записи очень быстрый, так что я бы не волновался об этом, но в настоящее время есть много проблем с MV, и они помечены как экспериментальные по причине - я бы не рекомендовал их, или вы столкнетесь множество проблем согласованности в текущих версиях.
- создать таблицу с ПЕРВИЧНЫМ КЛЮЧОМ (сервис, пользователь) и прочитать с полным
согласованность перед записью, чтобы проверить, что старое обновление не записано.
Но это оставляет доступность и анти-паттерн для Кассандры.
Может быть, я пропускаю некоторые требования, которые вы не объяснили, но вам не нужно читать перед записью. Это кажется мне лучшим решением для меня. Если у вас есть обновление, нажмите на изменение таблицы (служба, пользователь), а затем при чтении из таблицы вы получите последнее обновление для каждого пользователя. На вашей вставке / обновлении всегда есть IF EXISTS
или предложения IF, также с использованием paxos.
Если вам нужна история (не только самая последняя), и вам не нужна вторая таблица, вы можете использовать группу по:
CREATE TABLE state ( // simplified a little
service int,
user int,
updated_at timeuuid,
data text,
PRIMARY KEY (service, user, updated_at)
) WITH CLUSTERING ORDER BY (user ASC, updated_at DESC);
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '3');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 2, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 2, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 2, 1, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '3');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '2');
SELECT * FROM state WHERE service = 1 GROUP BY service, user;
service | user | updated_at | data
---------+------+--------------------------------------+------
1 | 1 | 7c2bd900-981e-11e9-a27a-7b01c564a3f0 | 3
1 | 2 | 7c2d1180-981e-11e9-a27a-7b01c564a3f0 | 2
1 | 3 | 7c88c610-981e-11e9-a27a-7b01c564a3f0 | 2
Это не удивительно эффективно или что-то еще, но оно будет работать, если вы никогда не позволите одному сервисному разделу стать слишком большим. Я бы на самом деле настоятельно рекомендовал бы добавить к нему компонент даты / корзины, например:
CREATE TABLE state (
bucket text
service int,
user int,
updated_at timeuuid,
data text,
PRIMARY KEY ((bucket, service), user, updated_at)
) WITH CLUSTERING ORDER BY (user ASC, updated_at DESC);
где bucket - строка YYYY-MM-DD (или YYYY-WEEKOFYEAR или что-то в этом роде). Затем, как раз в граничное время, вы запрашиваете как текущий, так и последний сегмент. В противном случае разделы будут расти, пока не возникнут проблемы.