MongoDB – Trabalhando com Tailable Cursor
- Postado por Adriano Bonacin
- Categorias mongodb
- Data 09/02/2022
- Comentários 0 comentário
Tailable Cursor é algo que a gente não vê sempre por aí, mas ele é o coração da replicação no MongoDB, portanto, vamos explorá-lo um pouco.
Talvez seja mais fácil compreendê-lo se você conhece o comando tail -f no Linux. Basicamente é o que ele faz, fica acompanhando uma collection e sempre que aparece um documento novo, ele consome. Nos cursores tradicionais, que falamos no último artigo, após consumirmos o último documento ele será automaticamente fechado. No caso do Tailable Cursor, ele será mantido aberto e saberá até onde já foi consumido.
Caso de uso
Você deve usar Tailable Cursor em uma Capped Collection sempre que precisa processar todo novo documento e se basear em índices não é conveniente ou performático. Como já citado, o Tailable Cursor é o motor da replicação no MongoDB. A collection oplog é Capped e conseguimos ficar “acompanhando” toda nova transação e aplicando nos secondaries.
Como funciona
Quando criamos um cursor tradicional, usando .find() + filtro + projection, eles são fechados automaticamente ao consumir o último documento, liberando espaço em memória que foi utilizado. Porém, há um método que se aplica ao Cursor para que ele seja mantido aberto e possamos consumir novos registros que forem inserido. Basicamente este é o papel do .tailable() no mongosh. Ele ainda aceita pârametros para que fique aguardando por novos registros ou não, retornando apenas os novos registros, finalizando a execução, MAS mantendo o cursor aberto.
Desta forma, podemos ficar periodicamente pedindo novos registros que ele sempre retornará apenas os registros inseridos a partir da última execução.
Como fazer no mongosh
Se preferir revisar a forma como trabalhar com cursores tradicionais, confira este artigo, vamos usar uma forma muito parecida. Para começar vou reconstruir nossa Capped Collection e inserir alguns docs. Em seguida é só fazer a iteração. Veremos que os quatro documentos serão retornados e a execução finalizada. Se tentarmos iterar novamente, ele não retorna nenhum documento, mas a execução ocorre com sucesso.
rsYadax0 [direct: primary] yadax> db.cars2.drop()
true
rsYadax0 [direct: primary] yadax> db.createCollection("cars2", {capped:true, size:10000, max:4})
{ ok: 1 }
rsYadax0 [direct: primary] yadax> db.cars2.insertOne({"marca":"HONDA", "modelo":"CIVIC", "ano_fabricacao": 2015 , "ano_modelo": 2016, "cor": "Prata", "preco": 60000})
rsYadax0 [direct: primary] yadax> db.cars2.insertOne({"marca":"HONDA", "modelo":"Civic", "ano_fabricacao": 2018 , "ano_modelo": 2018, "cor": "Branco", "preco": 80000})
rsYadax0 [direct: primary] yadax> db.cars2.insertOne({"marca":"HONDA", "modelo":"FIT", "ano_fabricacao": 2019 , "ano_modelo": 2019, "cor": "Branco", "preco": 50000})
rsYadax0 [direct: primary] yadax> db.cars2.insertOne({"marca":"FIAT", "modelo":"UNO", "ano_fabricacao": 2000 , "ano_modelo": 2001, "cor": "Preto", "preco": 10000})
rsYadax0 [direct: primary] yadax>
rsYadax0 [direct: primary] yadax> var cars2 = db.cars2.find().tailable({ awaitData : false})
rsYadax0 [direct: primary] yadax> cars2.forEach(function (myCar) { print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco); })
Marca: HONDA - modelo: CIVIC - preco: 60000
Marca: HONDA - modelo: Civic - preco: 80000
Marca: HONDA - modelo: FIT - preco: 50000
Marca: FIAT - modelo: UNO - preco: 10000
rsYadax0 [direct: primary] yadax> cars2.forEach(function (myCar) { print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco); })
rsYadax0 [direct: primary] yadax>
Agora vamos inserir novos documentos e executar novamente.
db.cars2.insertOne({"marca":"FIAT", "modelo":"PALIO", "ano_fabricacao": 2006 , "ano_modelo": 2007, "cor": "Prata", "preco": 11000})
rsYadax0 [direct: primary] yadax> cars2.forEach(function (myCar) { print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco); })
Marca: FIAT - modelo: PALIO - preco: 11000
db.cars2.insertOne({"marca":"FIAT", "modelo":"STRADA", "ano_fabricacao": 2018 , "ano_modelo": 2018, "cor": "Branco", "preco": 40000})
db.cars2.insertOne({"marca":"FIAT", "modelo":"TORO", "ano_fabricacao": 2020 , "ano_modelo": 2020, "cor": "Preto", "preco": 90000})
rsYadax0 [direct: primary] yadax> cars2.forEach(function (myCar) { print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco); })
Marca: FIAT - modelo: STRADA - preco: 40000
Marca: FIAT - modelo: TORO - preco: 90000
A essa altura já está claro a ideia do Tailable Cursor. Apenas para ilustrar um pouco mais, vamos ver como isso funciona no Python.
Como fazer no Python
Já sabemos o propósito do Tailable Cursor, agora vou mostrar como podemos usá-lo no python. Gravei um vídeo (meu primeiro :)) para tentar deixar mais simples. Menos de 2 min, não vai ser tão ruim assim.
Basicamente abrimos a conexão, guardamos nosso Tailable Cursor em uma variável e iteramos de 1 em 1 segundo. Em seguida, é só ir adicionando os documentos que ele vai aparecendo.
import time from pymongo import MongoClient client = MongoClient('mymongo1', username='yadaxRoot', password='MinhaSenha12#', authSource='admin', authMechanism='SCRAM-SHA-1') myCollection = client.yadax.cars2 myTailableCursor = myCollection.find(cursor_type=pymongo.CursorType.TAILABLE_AWAIT) while True: while myTailableCursor.alive: for car in myTailableCursor: print(car) time.sleep(1)
Espero que tenha ficado claro. Qualquer dúvida, comente, ou nos procure nas redes sociais.