Асинхронные пакетные операции в Couchbase

Асинхронные пакетные операции в Couchbase

1. Вступление

В этом дополнении к нашему руководству поusing Couchbase in a Spring application мы исследуем асинхронную природу Couchbase SDK и то, как его можно использовать для выполнения операций сохранения в пакетном режиме, что позволяет нашему приложению оптимально использовать ресурсы Couchbase.

1.1. CrudService Интерфейс

Во-первых, мы добавляем в наш общий интерфейсCrudService пакетные операции:

public interface CrudService {
    ...

    List readBulk(Iterable ids);

    void createBulk(Iterable items);

    void updateBulk(Iterable items);

    void deleteBulk(Iterable ids);

    boolean exists(String id);
}

1.2. CouchbaseEntity Интерфейс

Мы определяем интерфейс для сущностей, которые мы хотим сохранить:

public interface CouchbaseEntity {

    String getId();

    void setId(String id);

}

1.3. AbstractCrudService Класс

Затем мы реализуем каждый из этих методов в общем абстрактном классе. Этот класс является производным от классаPersonCrudService, который мы использовали вthe previous tutorial, и начинается следующим образом:

public abstract class AbstractCrudService implements CrudService {
    private BucketService bucketService;
    private Bucket bucket;
    private JsonDocumentConverter converter;

    public AbstractCrudService(BucketService bucketService, JsonDocumentConverter converter) {
        this.bucketService = bucketService;
        this.converter = converter;
    }

    protected void loadBucket() {
        bucket = bucketService.getBucket();
    }

    ...
}

2. Асинхронный интерфейс сегмента

Couchbase SDK предоставляет интерфейсAsyncBucket для выполнения асинхронных операций. Учитывая экземплярBucket, вы можете получить его асинхронную версию с помощью методаasync():

AsyncBucket asyncBucket = bucket.async();

3. Пакетные операции

Для выполнения пакетных операций с использованием интерфейсаAsyncBucket мы используем библиотекуRxJava.

3.1. Пакетное чтение

Здесь мы реализуем методreadBulk. Сначала мы используем механизмAsyncBucket иflatMap в RxJava для асинхронного извлечения документов вObservable<JsonDocument>, затем мы используем механизмtoBlocking вRxJava для преобразования их в список сущностей:

@Override
public List readBulk(Iterable ids) {
    AsyncBucket asyncBucket = bucket.async();
    Observable asyncOperation = Observable
      .from(ids)
      .flatMap(new Func1>() {
          public Observable call(String key) {
              return asyncBucket.get(key);
          }
    });

    List items = new ArrayList();
    try {
        asyncOperation.toBlocking()
          .forEach(new Action1() {
              public void call(JsonDocument doc) {
                  T item = converter.fromDocument(doc);
                  items.add(item);
              }
        });
    } catch (Exception e) {
        logger.error("Error during bulk get", e);
    }

    return items;
}

3.2. Пакетная вставка

Мы снова используем конструкциюRxJava’s flatMap для реализации методаcreateBulk.

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

@Override
public void createBulk(Iterable items) {
    AsyncBucket asyncBucket = bucket.async();
    Observable
      .from(items)
      .flatMap(new Func1>() {
          @SuppressWarnings("unchecked")
          @Override
          public Observable call(final T t) {
              if(t.getId() == null) {
                  t.setId(UUID.randomUUID().toString());
              }
              JsonDocument doc = converter.toDocument(t);
              return asyncBucket.insert(doc)
                .retryWhen(RetryBuilder
                  .anyOf(BackpressureException.class)
                  .delay(Delay.exponential(TimeUnit.MILLISECONDS, 100))
                  .max(10)
                  .build());
          }
      })
      .last()
      .toBlocking()
      .single();
}

3.3. Пакетное обновление

Мы используем аналогичный механизм в методеupdateBulk:

@Override
public void updateBulk(Iterable items) {
    AsyncBucket asyncBucket = bucket.async();
    Observable
      .from(items)
      .flatMap(new Func1>() {
          @SuppressWarnings("unchecked")
          @Override
          public Observable call(final T t) {
              JsonDocument doc = converter.toDocument(t);
              return asyncBucket.upsert(doc)
                .retryWhen(RetryBuilder
                  .anyOf(BackpressureException.class)
                  .delay(Delay.exponential(TimeUnit.MILLISECONDS, 100))
                  .max(10)
                  .build());
          }
      })
      .last()
      .toBlocking()
      .single();
}

3.4. Пакетное удаление

И мы пишем методdeleteBulk следующим образом:

@Override
public void deleteBulk(Iterable ids) {
    AsyncBucket asyncBucket = bucket.async();
    Observable
      .from(ids)
      .flatMap(new Func1>() {
          @SuppressWarnings("unchecked")
          @Override
          public Observable call(String key) {
              return asyncBucket.remove(key)
                .retryWhen(RetryBuilder
                  .anyOf(BackpressureException.class)
                  .delay(Delay.exponential(TimeUnit.MILLISECONDS, 100))
                  .max(10)
                  .build());
          }
      })
      .last()
      .toBlocking()
      .single();
}

4. PersonCrudServiceс

Наконец, мы пишем службу SpringPersonCrudService, которая расширяет нашAbstractCrudService для объектаPerson.

Поскольку все взаимодействие Couchbase реализовано в абстрактном классе, реализация для класса сущности тривиальна, так как нам нужно только убедиться, что все наши зависимости введены и наш контейнер загружен:

@Service
public class PersonCrudService extends AbstractCrudService {

    @Autowired
    public PersonCrudService(
      @Qualifier("TutorialBucketService") BucketService bucketService,
      PersonDocumentConverter converter) {
        super(bucketService, converter);
    }

    @PostConstruct
    private void init() {
        loadBucket();
    }
}

5. Заключение

Исходный код, показанный в этом руководстве, доступен вgithub project.

Вы можете узнать больше о Couchbase Java SDK на официальном сайтеCouchbase developer documentation site.