Cursores disponíveis do Spring Data MongoDB

Cursores disponíveis do Spring Data MongoDB

1. Introdução

Neste tutorial, vamos discutir como usar o MongoDB como um fluxo de dados infinito, utilizando cursores disponíveis comSpring Data MongoDB.

2. Cursores Disponíveis

Quando executamos uma consulta, o driver do banco de dados abre um cursor para fornecer os documentos correspondentes. Por padrão, o MongoDB fecha automaticamente o cursor quando o cliente lê todos os resultados. Portanto, transformar resulta em um fluxo de dados finito.

No entanto,we can use capped collections with a tailable cursor that remains open, even after the client consumed all initially returned data – making the infinite data stream. Esta abordagem é útil para aplicativos que lidam com fluxos de eventos, como mensagens de bate-papo ou atualizações de estoque.

O projeto Spring Data MongoDB nos ajuda a utilizar recursos de banco de dados reativos, incluindo cursores disponíveis.

3. Configuração

Para demonstrar os recursos mencionados, implementaremos um aplicativo de contador de registros simples. Vamos supor que haja algum agregador de log que coleta e persiste todos os logs em um local central - nossa coleção limitada do MongoDB.

Em primeiro lugar, usaremos a entidadeLog simples:

@Document
public class Log {
    private @Id String id;
    private String service;
    private LogLevel level;
    private String message;
}

Em segundo lugar, vamos armazenar os registros em nossa coleção limitada do MongoDB. Capped collections são coleções de tamanho fixo que inserem e recuperam documentos com base no pedido de inserção. Podemos criá-los comMongoOperations.createCollection:

db.createCollection(COLLECTION_NAME, new CreateCollectionOptions()
  .capped(true)
  .sizeInBytes(1024)
  .maxDocuments(5));

Para coleções limitadas, devemos definir a propriedadesizeInBytes. Além disso, omaxDocuments especifica o número máximo de documentos que uma coleção pode ter. Uma vez atingidos, os documentos mais antigos serão removidos da coleção.

Em terceiro lugar, usaremos oSpring Boot starter dependency apropriado:


    org.springframework.boot
    spring-boot-starter-data-mongodb-reactive
    2.1.6.RELEASE

4. Cursores reativos disponíveis

Podemos consumir cursores disponiveis comimperativee a API MongoDB reativa. É altamente recomendado parause the reactive variant.

Vamos implementar o contador de registros de nívelWARN usando uma abordagem reativa. We’re able to create infinite stream queries with ReactiveMongoOperations.tail method.

Um cursor disponível permanece aberto e emite dados - umFlux de entidades - conforme novos documentos chegam em uma coleção limitada e correspondem afilter query:

private Disposable subscription;

public WarnLogsCounter(ReactiveMongoOperations template) {
    Flux stream = template.tail(
      query(where("level").is(LogLevel.WARN)),
      Log.class);
    subscription = stream.subscribe(logEntity ->
      counter.incrementAndGet()
    );
}

Uma vez que o novo documento, tendo o nível de logWARN, é persistido na coleção, o assinante (expressão lambda) irá incrementar o contador.

Por fim, devemos descartar a assinatura para fechar o fluxo:

public void close() {
    this.subscription.dispose();
}

Além disso,please note that tailable cursors may become dead, or invalid if the query initially returns no match. Em outras palavras, mesmo que novos documentos persistentes correspondam à consulta do filtro, o assinante não poderá recebê-los. Essa é uma limitação conhecida dos cursores disponíveis do MongoDB. Devemos garantir que haja documentos correspondentes na coleção limitada, antes de criar um cursor disponível.

5. Cursores disponíveis com um repositório reativo

Os projetos Spring Data oferecem uma abstração de repositório para diferentes armazenamentos de dados, incluindo as versões reativas.

O MongoDB não é exceção. Por favor, verifique o artigoSpring Data Reactive Repositories with MongoDB para mais detalhes.

Além disso,MongoDB reactive repositories support infinite streams by annotating a query method with @Tailable. Podemos anotar qualquer método de repositório retornandoFlux ou outros tipos reativos capazes de emitir vários elementos:

public interface LogsRepository extends ReactiveCrudRepository {
    @Tailable
    Flux findByLevel(LogLevel level);
}

Vamos contar logs deINFO usando este método de repositório disponível:

private Disposable subscription;

public InfoLogsCounter(LogsRepository repository) {
    Flux stream = repository.findByLevel(LogLevel.INFO);
    this.subscription = stream.subscribe(logEntity ->
      counter.incrementAndGet()
    );
}

Da mesma forma, quanto aWarnLogsCounter, devemos descartar a assinatura para fechar o fluxo:

public void close() {
    this.subscription.dispose();
}

6. Cursores disponíveis com umMessageListener

No entanto, se não podemos usar a API reativa, podemos aproveitar o conceito de mensagens do Spring.

Primeiro, precisamos criar umMessageListenerContainer que tratará os objetosSubscriptionRequest enviados. O driver síncrono do MongoDB cria uma tarefa de bloqueio de execução longa que escuta novos documentos na coleção limitada.

Spring Data MongoDB vem coma default implementation capable of creating and executing Task instances por umTailableCursorRequest:

private String collectionName;
private MessageListenerContainer container;
private AtomicInteger counter = new AtomicInteger();

public ErrorLogsCounter(MongoTemplate mongoTemplate,
  String collectionName) {
    this.collectionName = collectionName;
    this.container = new DefaultMessageListenerContainer(mongoTemplate);

    container.start();
    TailableCursorRequest request = getTailableCursorRequest();
    container.register(request, Log.class);
}

private TailableCursorRequest getTailableCursorRequest() {
    MessageListener listener = message ->
      counter.incrementAndGet();

    return TailableCursorRequest.builder()
      .collection(collectionName)
      .filter(query(where("level").is(LogLevel.ERROR)))
      .publishTo(listener)
      .build();
}

TailableCursorRequest cria uma consulta filtrando apenas os logs de nívelERROR. Cada documento correspondente será publicado noMessageListener que incrementará o contador.

Observe que ainda precisamos garantir que a consulta inicial retorne alguns resultados. Caso contrário, o cursor disponível será imediatamente fechado.

Além disso, não devemos esquecer de parar o contêiner, uma vez que não precisamos mais dele:

public void close() {
    container.stop();
}

7. Conclusão

As coleções limitadas do MongoDB com cursores disponíveis nos ajudam a receber informações do banco de dados de forma contínua. Podemos executar uma consulta que continuará fornecendo resultados até que seja explicitamente fechado. Spring Data MongoDB oferece a maneira reativa e de bloqueio de utilizar cursores disponíveis.

O código-fonte do exemplo completo está disponívelover on GitHub.