Статьи

Как реализовать управление версиями документов с помощью Couchbase

Вступление

Разработчики часто спрашивают меня, как «оформить» документы с помощью Couchbase 2.0. Короткий ответ: клиенты и сервер не предоставляют такую ​​возможность, но ее довольно легко реализовать.

В этой статье я буду использовать базовый подход, и вы сможете расширить его в зависимости от ваших бизнес-требований.

дизайн

Первое, что нужно сделать, это выбрать, как «хранить / упорядочить» версии вашего документа, и для этого у вас есть разные дизайны:

  • скопировать версии документа в новые документы
  • скопировать версии документа в список вложенных документов
  • хранить список атрибутов, которые были изменены во встроенный элемент (или новые документы)
  • хранить «дельта»

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

  1. Текущая версия представляет собой простой ключ / документ, без изменений ключа.
  2. Версия является копией документа, а номер версии добавляется к ключу.

Это выглядит так:

Текущая версия мой ключ
Версия 1 MyKey :: v1
Версия 2 MyKey :: v2

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

Например, в приложении «Образец пива» для просмотра пива по имени используется следующий вид:

1
2
3
4
5
function (doc, meta) {
    if(doc.type && doc.type == "beer") {
        emit(doc.name);
    }
}

Поддерживать управление версиями довольно просто, не затрагивая существующий код, кроме самого представления. Новое представление должно излучать ключи, значение только для текущей версии документа. Это новый код вида:

1
2
3
4
5
function (doc, meta) {
    if(doc.type && doc.type == "beer" && (meta.id).indexOf("::v") == -1   ) {
        emit(doc.name);
    }
}

С этим изменением существующие приложения, использующие это представление, продолжат работать с тем же поведением.

Реализация управления версиями

Исходя из этой схемы, когда приложению требуется версия документа, должна произойти следующая логика:

  1. Получить текущую версию документа
  2. Увеличьте номер версии (например, используя другой ключ, который поддерживает номер версии для каждого документа)
  3. Создайте версию с новым ключом «mykey :: v1»
  4. Сохранить документ текущей версии

Давайте посмотрим на код в Java

01
02
03
04
05
06
07
08
09
10
11
12
Object obj = client.get(key);
  if (obj != null) {
    // get the next version, create or use the key: mykey_version
    long version = client.incr(key + "_version", 1, 1);
    String keyForVersion = key + "::v" + version; // mykey::v1
    try {
        client.set(keyForVersion, obj).get();
    } catch (Exception e) {
        logger.severe("Cannot save version "+ version + " for key "+ key +" - Error:"+ e.getMessage() );
    }
   }
   client.set(key, value);

Довольно просто, не так ли?

Приложение может получить доступ к документу с помощью ключа, но также получить одну версию или список всех версий, это одна из причин, по которой интересно создавать ключ (
mykey_version), и используйте его также для удаления документов и связанных версий.

Исходя из предыдущего комментария, операция удаления выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
Object obj = client.get(key);
  // need to delete all the version first
  Object vObject = this.get(key + "_version");
  if (vObject != null) {
    long biggerVersion = Long.parseLong((String) vObject);
    try {
        // delete all the versions
        for (int i = 1; i <= biggerVersion; i++) {
            String versionKey = key + "::v" + i;
            client.delete(versionKey).get();
        }
        // delete the counter
        client.delete(key + "_version").get();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
  }
  client.delete(key);

Использовать управление версиями

В качестве примера, я создал небольшую библиотеку, доступную на GitHub https://github.com/tgrall/couchbase-how-to-versioning , эта библиотека расширяет клиент Couchbase и переопределяет некоторые операции: установить, заменить и удалить. (основной: нет TLL, нет долговечности) Как я уже говорил, это всего лишь пример.

Сборка и установка

1
2
3
git clone https://github.com/tgrall/couchbase-how-to-versioning.git
cd how-to-versioning
mvn clean install

Затем добавьте эту библиотеку в свой проект в дополнение к Couchbase Java Client, например, в свой pom.xml

01
02
03
04
05
06
07
08
09
10
11
...
 
      com.couchbase.howtos
      couchbase-how-to-versioning
      1.0-SNAPSHOT
 
      couchbase
      couchbase-client
      1.1.8
 
...

Код вашей заявки

Создайте документ и установите его версию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
List uris = new LinkedList();
 uris.add(URI.create("http://127.0.0.1:8091/pools"));
 CouchbaseClientWithVersioning client = null
 try {
  client = new CouchbaseClientWithVersioning(uris, "default", "");
  String key = "key-001";
  client.set(key, "This is the original version");
  System.out.printf("Original '%s' .\n", client.get(key));
  client.set(key, "This is a new version", true); // create a new version
  System.out.printf("Current Version '%s' .\n", client.get(key));
  System.out.printf("Version 1 '%s' .\n", client.get(key, 1));
  client.set(key, "This is another version", true); // create a new version
  System.out.printf("All versions %s .\n", client.getAllVersions(key));
  client.deleteVersion(key, 1); // create a new version
  System.out.printf("All versions %s (after delete 1 version).\n", client.getAllVersions(key));
  client.delete(key); // create a new version
  System.out.printf("All versions %s (after delete the main key).\n", client.getAllVersions(key));
 } catch (Exception e) {
  e.printStackTrace();
 }
 if (client !=null) {
  client.shutdown();
 }

Быстрое объяснение:

  • Строка 5: вместо использования CouchbaseClient приложение использует расширенный класс CouchbaseClientWithVersioning.
  • Строка 7: создать новую запись
  • Строка 9: создайте новую версию, логическое значение «true» принудительно версирует документ
  • Приложение использует другие методы, такие как получение определенной версии (строка 11), получение всех версий (строка 13), удаление определенной версии (строка 14) и, наконец, удаление ключа и всех версий (строка 16).

Таким образом, используя этот подход, разработчик явно контролирует, когда создавать версию, поскольку он должен добавить логический параметр в операции set. В этой небольшой библиотеке примеров также возможно автоматическое управление версиями, в этом случае все вызовы set и replace создадут версию для достижения того, что разработчику просто нужно вызвать метод setAutoVersioning (true). Что-то типа:

1
2
client = new CouchbaseClientWithVersioning(uris, "default", "");
    client.setAutomaticVersionning(true);

При таком подходе вы можете обеспечить управление версиями вашего приложения с минимальным изменением кода. Вы можете протестировать его в приложении Beer Sample, просто не забудьте изменить представления, как указано выше, только для возврата
текущая версия документов.

Вывод

Как вы можете видеть, создание версий в Couchbase не так сложно, но это то, что должно быть сделано вашим приложением на основе его требований и ограничений. У вас есть много разных решений, и ни один из этих вариантов не подходит для всех вариантов использования.

В этом конкретном примере кода я работаю с простым проектом, в котором я создаю копию документов для каждой версии. Также при таком подходе интересно упомянуть, что вы можете создавать «все», не только документ JSON, но и любые значения. Как я уже говорил, это один из возможных подходов, и, как и любой дизайн, он оказывает некоторое влияние на приложение или базу данных, в данном случае большую часть базы данных:

  • Увеличьте количество ключей и документов
  • Удвойте или увеличьте число операций, например, при обновлении документа, приложение должно получить текущее значение, создать версию, сохранить текущую версию.
  • Управление согласованностью при добавлении новой версии и увеличении номера версии (необходимо учитывать ошибки при создании новой версии, удалении версий и счетчика….)

Многие функции могут быть добавлены к этому легко, например:

  • Ограничение до определенного количества версий,
  • Включить управление версиями только операции replace ()
  • Добавить определенный атрибут о версиях в документ JSON (например, дату версии)
  • ….

Если вы используете управление версиями в своем приложении Couchbase, не стесняйтесь комментировать или написать небольшую статью, которая описывает, как вы это делаете.