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.