Opérations par lots asynchrones dans Couchbase

Opérations par lots asynchrones dans Couchbase

1. introduction

Dans ce suivi de notre tutoriel surusing Couchbase in a Spring application, nous explorons la nature asynchrone du SDK Couchbase et comment il peut être utilisé pour effectuer des opérations de persistance par lots, permettant ainsi à notre application d'obtenir une utilisation optimale des ressources Couchbase.

1.1. InterfaceCrudService

Tout d'abord, nous augmentons notre interface génériqueCrudService pour inclure les opérations par lots:

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. InterfaceCouchbaseEntity

Nous définissons une interface pour les entités que nous voulons conserver:

public interface CouchbaseEntity {

    String getId();

    void setId(String id);

}

1.3. ClasseAbstractCrudService

Ensuite, nous allons implémenter chacune de ces méthodes dans une classe abstraite générique. Cette classe est dérivée de la classePersonCrudService que nous avons utilisée dansthe previous tutorial et commence comme suit:

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. L'interface de compartiment asynchrone

Le SDK Couchbase fournit l'interfaceAsyncBucket pour effectuer des opérations asynchrones. Étant donné une instanceBucket, vous pouvez obtenir sa version asynchrone via la méthodeasync():

AsyncBucket asyncBucket = bucket.async();

3. Opérations par lots

Pour effectuer des opérations par lots à l'aide de l'interfaceAsyncBucket, nous utilisons la bibliothèqueRxJava.

3.1. Lecture par lots

Ici, nous implémentons la méthodereadBulk. Nous utilisons d'abord le mécanismeAsyncBucket etflatMap dans RxJava pour récupérer les documents de manière asynchrone dans unObservable<JsonDocument>, puis nous utilisons le mécanismetoBlocking dansRxJava pour convertir ceux-ci à une liste d'entités:

@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. Insertion de lot

Nous utilisons à nouveau la constructionRxJava’s flatMap pour implémenter la méthodecreateBulk.

Étant donné que les demandes de mutation en masse sont produites plus rapidement que leurs réponses ne peuvent être générées, ce qui entraîne parfois une condition de surcharge, nous instituons une nouvelle tentative avec un délai exponentiel chaque fois qu'unBackpressureException est rencontré:

@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. Mise à jour par lots

Nous utilisons un mécanisme similaire dans la méthodeupdateBulk:

@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. Supprimer par lots

Et nous écrivons la méthodedeleteBulk comme suit:

@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

Enfin, nous écrivons un service Spring,PersonCrudService, qui étend nosAbstractCrudService pour l'entitéPerson.

Puisque toute l'interaction de Couchbase est implémentée dans la classe abstraite, l'implémentation d'une classe d'entité est triviale, car nous devons simplement nous assurer que toutes nos dépendances sont injectées et que notre compartiment est chargé:

@Service
public class PersonCrudService extends AbstractCrudService {

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

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

5. Conclusion

Le code source montré dans ce didacticiel est disponible dans lesgithub project.

Vous pouvez en savoir plus sur le SDK Java Couchbase sur le site officielCouchbase developer documentation site.