Обновления Кассандры не работают последовательно
Я запускаю следующий код на моей локальной (Mac) машине и на удаленном сервере Unix:
public void deleteValue(final String id, final String value) {
log.info("Removing value " + value);
final Collection<String> valuesBeforeRemoval = getValues(id);
final MutationBatch m = keyspace.prepareMutationBatch();
m.withRow(VALUES_CF, id).deleteColumn(value);
try {
m.execute();
} catch (final ConnectionException e) {
log.error("Unable to delete location " + value, e);
}
final Collection<String> valuesAfterRemoval = getValues(id);
if (valuesAfterRemoval.size()!=(valuesBeforeRemoval.size()-1)) {
log.error("value " + value + " was supposed to be removed from list " + valuesBeforeRemoval + " but it wasn't: " + valuesAfterRemoval);
}
...
}
protected Collection<String> getValues(final String id) {
try {
final OperationResult<ColumnList<String>> operationResult = keyspace
.prepareQuery(VALUES_CF).getKey(id).execute();
final ColumnList<String> result = operationResult.getResult();
if (result.isEmpty()) {
log.info("No value found for id: " + id);
return new ArrayList<String>();
}
return result.getColumnNames();
} catch (final ConnectionException e) {
log.error("Unable to retrieve session " + id, e);
}
return new ArrayList<String>();
}
Локально эта строка никогда не выполняется, что имеет смысл:
log.error("value " + value + " was supposed to be removed from list " + valuesBeforeRemoval + " but it wasn't: " + valuesAfterRemoval);
но эта строка выполняется на моем сервере dev:
[ОШИБКА] [main] [nowsdSessionDaoCassandraImpl] [2013-03-08 13:12:24,801] [] - значение 3 должно быть удалено из списка [3, 2, 1, 0, 7, 6, 5, 4, 9, 8] но не было: [3, 2, 1, 0, 7, 6, 5, 4, 9, 8]
- Я использую com.netflix.astyanax
- И моя локальная машина, и удаленный сервер dev подключаются к одному и тому же экземпляру cassandra.
- Как на моей локальной машине, так и на удаленном сервере разработки выполняется один и тот же тест, создающий новое семейство строк и добавляющий 10 записей перед удалением одной.
- Когда ошибка возникает в dev, log.error("Невозможно удалить местоположение" + значение, e); не был выполнен (т. е. при выполнении команды удаления не возникло никаких исключений).
- Я на 100% уверен, что никакой другой код не влияет на содержимое базы данных, пока я запускаю тест на dev, так что это не какая-то странная проблема параллелизма.
Что может объяснить, что запрос deleteColumn(значение) выполняется без каких-либо ошибок, но по-прежнему не удаляет столбец из базы данных?
ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ
Вот как я создал пространство клавиш:
create keyspace sessiondata
with placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy'
and strategy_options = {replication_factor:1};
Вот как я создал значения семейства столбцов, на которые ссылается VALUES_CF в приведенном выше коде:
create column family values
with comparator = UTF8Type
;
Вот как определяется пространство клавиш, указанное в приведенном выше коде Java:
final AstyanaxContext.Builder contextBuilder = getBuilder();
final AstyanaxContext<Keyspace> keyspaceContext = contextBuilder
.forKeyspace(keyspaceName).buildKeyspace(
ThriftFamilyFactory.getInstance());
keyspaceContext.start();
keyspace = keyspaceContext.getEntity();
где getBuilder:
private Builder getBuilder() {
final AstyanaxConfigurationImpl conf = new AstyanaxConfigurationImpl()
.setDiscoveryType(NodeDiscoveryType.NONE)
.setRetryPolicy(new RunOnce());
final ConnectionPoolConfigurationImpl poolConf = new ConnectionPoolConfigurationImpl("MyPool")
.setPort(port)
.setMaxConnsPerHost(1)
.setSeeds(value);
return new AstyanaxContext.Builder()
.forCluster(cluster)
.withAstyanaxConfiguration(conf)
.withConnectionPoolConfiguration(poolConf)
.withConnectionPoolMonitor(new CountingConnectionPoolMonitor());
}
ВТОРОЕ ОБНОВЛЕНИЕ
Во-первых, проблемы связаны не только с удалениями. Я наблюдаю аналогичные проблемы при обновлении записей в базе данных, их чтении и невозможности прочитать только что написанные обновления
Во-вторых, я создал тест, который в 100 раз выполняет следующие операции:
- написать ряд в Кассандру
- обновить эту строку в Кассандре
- прочитайте эту строку от Кассандры и проверьте, действительно ли строка была обновлена, и регулярно проверяйте после задержек, не было ли это
Из этого теста я наблюдаю следующее:
- опять же, когда я запускаю этот код локально, все 100 итераций проходят сразу (повторных попыток не требуется)
- когда я запускаю этот код на удаленном сервере, некоторые итерации проходят, а некоторые не выполняются. Когда они терпят неудачу, независимо от того, насколько велика задержка (я жду до 10 секунд), тест всегда терпит неудачу.
На данный момент, я действительно не уверен, как любая установка cassandra могла бы объяснить это поведение, так как я подключаюсь к тому же серверу для своих тестов, и поскольку задержки, которые я вставляю, намного больше, чем любая дополнительная задержка, которая может понадобиться для запуска теста при подключении с моей локальной машины.
Единственное существенное различие заключается в том, на какой машине выполняется код.
ТРЕТЬЕ ОБНОВЛЕНИЕ
Если в тесте, упомянутом в предыдущем обновлении, я вставляю задержку между двумя записями, код начинает проходить, если задержка>= 1000 мс. Задержка, скажем, 100 мс не помогает. Я также изменил компоновщик, чтобы установить согласованность чтения и записи по умолчанию для самого требовательного: ALL, и это никак не повлияло на результаты теста (по-прежнему происходил сбой примерно в половине случаев, если задержка между записями>1 с):
final AstyanaxConfigurationImpl conf = new AstyanaxConfigurationImpl()
.setDiscoveryType(NodeDiscoveryType.NONE)
.setRetryPolicy(new RunOnce()).setDefaultReadConsistencyLevel(ConsistencyLevel.CL_ALL).setDefaultWriteConsistencyLevel(ConsistencyLevel.CL_ALL);
1 ответ
Для отладки попробуйте напечатать полную строку, а не только имена столбцов. Когда я говорю полную строку, я имею в виду имя столбца, значение столбца и отметку времени. В общем, часы на одной из ваших тестовых машин неверны, а на другой - выкидывают ваши тесты.
Еще одна вещь, требующая двойной проверки, заключается в том, что ip действительно то, что вы думаете, как в вашем приложении, так и в cassandra. Когда вы получите его, напечатайте его между чем-то, например println("-" + ip "-"). До и после вашего блока try для execute в deleteSecureLocation выполните get только для этого столбца, а не для всей строки. Я не слишком уверен, как это сделать в astynax, в конце концов это будет получить [id][ip].
Следует иметь в виду, что удаление не завершится неудачей, даже если нечего удалять. Для cassandra это запись, единственное, что сделает его удалением, это если при чтении это последняя запись с меткой времени для этого имени строки / столбца.