Не удалось обновить статический столбец в cassandra

У меня странная проблема с базой данных Cassandra (версия 2.2.3) и использованием статических столбцов при написании некоторого доказательства концепции для простого приложения с функцией отправки денег.

Моя таблица:

CREATE TABLE transactions (
profile text,
timestamp timestamp,
amount text,
balance text,
lock int static,
PRIMARY KEY (profile, timestamp)) WITH CLUSTERING ORDER BY (timestamp ASC);

Первый шаг я добавляю новую запись

INSERT INTO transactions (profile, timestamp, amount) VALUES ( 'test_profile', '2015-11-05 15:20:01+0000', '10USD');

Затем я хочу «заблокировать» текущую пользовательскую транзакцию, чтобы выполнить какое-либо действие с его балансом. Я пытаюсь выполнить этот запрос:

UPDATE transactions SET lock = 1 WHERE profile = 'test_profile' IF lock = null;

Но как результат в cqlsh я вижу

     [applied]
-----------
     False

Я не понимаю, почему «False», потому что текущие данные для профиля:

 profile      | timestamp                | lock | amount | balance
--------------+--------------------------+------+--------+---------
 test_profile | 2015-11-05 15:20:01+0000 | null |  10USD |    null

Любая идея, что я делаю неправильно?

ОБНОВЛЕНИЕ

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

CREATE TABLE transactions (
    profile text,
    timestamp timestamp,
    amount text,
    balance text,
    lock int static,
    balances map<text,text> static,
    PRIMARY KEY (profile, timestamp)
) WITH CLUSTERING ORDER BY (timestamp ASC);
INSERT INTO transactions (profile, timestamp, amount) VALUES ( 'test_profile', '2015-11-05 15:20:01+0000', '1USD');
INSERT INTO transactions (profile, lock) VALUES ('test_profile', 1) IF NOT EXISTS;
BEGIN BATCH
        UPDATE transactions SET balances={'USD':'1USD'} WHERE profile='test_profile';
        UPDATE transactions SET balance='1USD' WHERE profile='test_profile' AND timestamp='2015-11-05 15:20:01+0000';
        DELETE lock FROM transactions WHERE profile='test_profile';
APPLY BATCH;

И если я снова попытаюсь получить блокировку, я получу

INSERT INTO transactions (profile, lock) VALUES ('test_profile', 1) IF NOT EXISTS;

 [applied] | profile      | timestamp | balances        | lock | amount | balance
-----------+--------------+-----------+-----------------+------+--------+---------
     False | test_profile |      null | {'USD': '1USD'} | null |   null |    null

person greenhost87    schedule 05.11.2015    source источник


Ответы (1)


Когда вы INSERT не вставляете поле lock, это означает, что это поле не существует. Нулевое представление в CQLSH или DevCenter — это всего лишь искусственный сахар, чтобы результаты выглядели как табличные данные, но на самом деле они имеют динамические значения ключей, а lock отсутствует в этой карте значений ключей. Полезно посмотреть экономное представление данных даже хотя он больше не используется, чтобы понять, как он хранится на диске.

Поэтому, когда UPDATE запускается, он ожидает наличия столбца для его обновления. В вашем случае столбец lock даже отсутствует, поэтому он не может его обновить. Эта тред о разнице между INSERT и UPDATE тоже хорошо читается.

У вас есть два решения, чтобы сделать эту работу:

Явно вставить null

Вы можете добавить lock в свой оператор вставки и установить для него значение null (что в Cassandra отличается от исключения его из вставки, потому что таким образом он получит нулевое значение, и когда вы его исключите, этот столбец не будет существовать в

INSERT INTO transactions (profile, timestamp, amount, lock) 
VALUES ( 'test_profile', '2015-11-05 15:20:01+0000', '10USD', null);

Использовать вставку во втором операторе

Поскольку вы вставляете второй оператор lock в первый раз вместо обновления существующего значения, и поскольку это статический столбец для этого раздела, вы можете использовать INSERT IF NOT EXISTS вместо UPDATE IF LWT способ сделать это (блокировка не будет существовать, поэтому это пройдет в первый раз и не удастся все остальные времена, так как блокировка будет иметь значение):

INSERT INTO transactions (profile, lock) 
VALUES ('test_profile', 1) IF NOT EXISTS;
person Nenad Bozic    schedule 06.11.2015
comment
Поскольку DELETE в Cassandra не является физическим удалением, это логическое удаление (значение столбца помечается надгробием для удаления при следующем сжатии) — docs.datastax.com/en/cql/3.0/cql/cql_using/use_delete.html - person Nenad Bozic; 06.11.2015
comment
Я предлагаю использовать значения здесь (заблокировано = 1, не заблокировано = 0 или логическое значение или что-то еще) и переключать значение на основе этого. В первой вставке установите его на 0, а затем выполните условное ОБНОВЛЕНИЕ с блокировкой IF = 0 и т. д. Также обратите внимание, что пакетная обработка — это только операция «все или ничего», но она не может гарантировать порядок операторов. - person Nenad Bozic; 06.11.2015
comment
Да я знаю обо всем или ничего. Теперь мне нужно подумать о том, как настроить статическое значение по умолчанию при выполнении первой записи и о производительности без дополнительного чтения перед записью, но я думаю, что это другая проблема. Про удаление знаю, но на мой взгляд теперь INSEER с IF NOT EXISTS работает странно - person greenhost87; 06.11.2015