Querying > Sistemas Distribuídos

Sistemas Distribuídos

Reading time: 5 min

O The Graph é um protocolo implementado como um sistema distribuído.

Conexões falham. Pedidos chegam fora da ordem. Computadores diferentes, com relógios e estados fora de sincronia, processam pedidos relacionados. Servidores reiniciam. Reorganizações acontecem entre pedidos. Estes problemas são inerentes a todos os sistemas distribuídos, mas pioram em sistemas que operam em uma escala global.

Considere este exemplo do que pode ocorrer se um cliente consultar um Indexador sobre os dados mais recentes durante uma reorganização.

  1. Indexador ingere o bloco 8
  2. Pedido servido ao cliente para o bloco 8
  3. Indexador ingere o bloco 9
  4. Indexador ingere o bloco 10A
  5. Pedido servido ao cliente para o bloco 10A
  6. Indexador deteta reorganização ao 10B e faz rollback para o 10A
  7. Pedido servido ao cliente para o bloco 9
  8. Indexador ingere o bloco 10B
  9. Indexador ingere o bloco 11
  10. Pedido servido ao cliente para o bloco 11

Do ponto de vista do Indexador, está tudo a progredir logicamente. O tempo move-se para frente, porém tivemos que fazer rollback num bloco uncle e reproduzir o bloco sob consenso à frente, ainda por cima. No caminho, o Indexador serve pedidos com o último estado de que tem ciência naquela hora.

Do ponto de vista do cliente, no entanto, tudo parece caótico. O cliente observa que as respostas foram para os blocos 8, 10, 9 e 11 naquela ordem — o problema do "bloco bambo". Quando um cliente experiencia um bloco bambo, os dados podem aparentar se contradizer temporalmente. Isto só piora quando consideramos que os Indexadores não ingerem os blocos mais recentes simultaneamente, e os seus pedidos podem ser encaminhados para vários Indexadores.

É responsabilidade do cliente e do servidor trabalharem juntos para fornecer dados consistentes ao utilizador. Métodos diferentes devem ser usados, a depender da consistência desejada, já que não há um único programa correto para todos os problemas.

Raciocinar as implicações de sistemas distribuídos é difícil, mas a solução pode não ser! Nós estabelecemos APIs e padrões para ajudar-lhe a navegar alguns casos de uso comuns. Os seguintes exemplos ilustram esses padrões, mas ainda omitem detalhes exigidos pelo código de produção (como gestão de erros e cancelamento) para não ofuscar as ideias principais.

Consulta por dados atualizados

Link para esta seção

O The Graph providencia a API block: { number_gte: $minBlock }, que garante que a resposta é para um único bloco, igual a ou maior que $minBlock. Se o pedido for feito para uma instância de graph-node e o bloco mínimo ainda não tiver sido sincronizado, o graph-node retornará um erro. Se o graph-node tiver sincronizado o bloco mínimo, ele executará a resposta para o bloco mais recente. Se o pedido for feito a um Gateway de Edge & Node, o Gateway filtrará quaisquer Indexadores que ainda não tiverem sincronizado o bloco mínimo, e fará o pedido para o bloco mais recente que o Indexador sincronizou.

Podemos usar o number_gte para garantir que o tempo nunca rebobine ao consultar dados num loop. Aqui vai um exemplo:

/// Atualiza a variável protocol.paused ao valor mais
/// recente conhecido em um loop, ao retirá-lo com o The Graph.
async function updateProtocolPaused() {
// Tudo bem começar com o minBlock no 0. A consulta será servida
// usando o bloco mais recente disponível. Configurar o minBlock no 0
// é o mesmo que deixar aquele argumento de fora.
let minBlock = 0
for (;;) {
// Agenda uma promessa que estará pronta quando
// o próximo bloco no Ethereum provavelmente estar disponível.
const nextBlock = new Promise((f) => {
setTimeout(f, 14000)
})
const query = `
query GetProtocol($minBlock: Int!) {
protocol(block: { number_gte: $minBlock } id: "0") {
paused
}
_meta {
block {
number
}
}
}`
const variables = { minBlock }
const response = await graphql(query, variables)
minBlock = response._meta.block.number
// TODO: Faça algo com os dados da resposta aqui em vez de logá-los.
console.log(response.protocol.paused)
// Hibernação para esperar pelo próximo bloco
await nextBlock
}
}

Como retirar um conjunto de itens relacionados

Link para esta seção

Outro caso de uso é o retiro de um grande conjunto, ou mais geralmente, de itens relacionados em múltiplos pedidos. Ao contrário do caso da consulta (onde a consistência desejada era acelerar no tempo), a consistência desejada é para um único ponto no tempo.

Aqui usaremos o argumento block: { hash: $blockHash } para fixar todos os nossos resultados ao mesmo bloco.

/// Retira uma lista de nomes de domínio de um único bloco com a paginação
async function getDomainNames() {
// Determina um limite no número máximo de itens para retirar.
let pages = 5
const perPage = 1000
// A primeira consulta pegará a primeira página dos resultados, e também pegará
// o hash do bloco para que o resto das consultas esteja consistente com a primeira.
const listDomainsQuery = `
query ListDomains($perPage: Int!) {
domains(first: $perPage) {
name
id
}
_meta {
block {
hash
}
}
}`
let data = await graphql(listDomainsQuery, { perPage })
let result = data.domains.map((d) => d.name)
let blockHash = data._meta.block.hash
let query
// Continua a retirar páginas adicionais até alcançarmos o limite total de
// 5 páginas (especificado acima) ou sabermos que alcançamos a última página
// because the page has fewer entities than a full page.
while (data.domains.length == perPage && --pages) {
let lastID = data.domains[data.domains.length - 1].id
query = `
query ListDomains($perPage: Int!, $lastID: ID!, $blockHash: Bytes!) {
domains(first: $perPage, where: { id_gt: $lastID }, block: { hash: $blockHash }) {
name
id
}
}`
data = await graphql(query, { perPage, lastID, blockHash })
// Acumula nomes de domínio no resultado
for (domain of data.domains) {
result.push(domain.name)
}
}
return result
}

Note que em caso de uma reorganização, o cliente deve tentar novamente a partir do primeiro pedido, para atualizar o hash do bloco a um block que não é uncle.

Editar página

Anterior
Como Fazer Queries de um Aplicativo
Próximo
API GraphQL
Editar página