4 minutos
Boas Práticas de Subgraph 3 - Como Melhorar o Desempenho da Indexação e de Queries com Entidades Imutáveis e Bytes como IDs
TLDR
Usar Entidades Imutáveis e Bytes como IDs no nosso arquivo schema.graphql
acelera muito a indexação e o desempenho das queries.
Entidades Imutáveis
Para fazer uma entidade imutável, basta adicionar (immutable: true)
a uma entidade.
1type Transfer @entity(immutable: true) {2 id: Bytes!3 from: Bytes!4 to: Bytes!5 value: BigInt!6}
Ao tornar a entidade Transfer
imutável, o graph-node pode processá-la com mais eficácia, o que melhora as velocidades de indexação e a capacidade de resposta das queries.
Estruturas de Entidades Imutáveis não mudarão no futuro. Uma entidade ideal para tornar Imutável seria uma que grava diretamente dados de eventos on-chain; por exemplo, um evento Transfer
registrado como uma entidade Transfer
.
De dentro da casca
Entidades mutáveis tem um block range
(alcance de bloco) que indica a sua validade. Atualizar estas entidades exige que o graph-node ajuste o alcance de bloco de versões anteriores, o que aumenta a carga de trabalho do banco de dados. Queries também precisam de filtragem para encontrar apenas entidades vivas. Entidades imutáveis são mais rápidas por serem todas vivas, e como não mudam, nenhuma verificação ou atualização é necessária durante a escrita, e nenhuma filtragem é necessária durante queries.
Quando não usar Entidades Imutáveis
Se tiver um campo como status
que precise ser gradualmente modificado, então esta entidade não deve ser imutável. Fora isto, é recomendado usar entidades imutáveis sempre que possível.
Bytes como IDs
Toda entidade requer uma ID. No exemplo anterior, vemos que a ID já é do tipo Bytes.
1type Transfer @entity(immutable: true) {2 id: Bytes!3 from: Bytes!4 to: Bytes!5 value: BigInt!6}
Enquanto outros tipos de IDs são possíveis, como String e Int8, recomendamos usar o tipo Bytes para todas as IDs porque strings de caracteres ocupam o dobro do espaço ocupado por strings de Byte para armazenar dados, e comparações de strings de caracteres em UTF-8 devem levar em conta o local, que é muito mais caro que a comparação em byte usada para comparar strings em Byte.
Razões para Não Usar Bytes como IDs
- Se IDs de entidade devem ser legíveis para humanos, como IDs numéricas automaticamente incrementadas ou strings legíveis, então Bytes como IDs não devem ser usados.
- If integrating a Subgraph’s data with another data model that does not use Bytes as IDs, Bytes as IDs should not be used.
- Melhorias no desempenho de indexação e queries não são desejáveis.
Concatenação com Bytes como IDs
It is a common practice in many Subgraphs to use string concatenation to combine two properties of an event into a single ID, such as using event.transaction.hash.toHex() + "-" + event.logIndex.toString()
. However, as this returns a string, this significantly impedes Subgraph indexing and querying performance.
Em vez disto, devemos usar o método concatI32()
para concatenar propriedades de evento. Esta estratégia resulta numa ID Bytes
que tem um desempenho muito melhor.
1export function handleTransfer(event: TransferEvent): void {2 let entity = new Transfer(event.transaction.hash.concatI32(event.logIndex.toI32()))3 entity.from = event.params.from4 entity.to = event.params.to5 entity.value = event.params.value67 entity.blockNumber = event.block.number8 entity.blockTimestamp = event.block.timestamp9 entity.transactionHash = event.transaction.hash1011 entity.save()12}
Organização com Bytes como IDs
A organização com Bytes como IDs não é o melhor recurso, como visto neste exemplo de query e resposta.
Query:
1{2 transfers(first: 3, orderBy: id) {3 id4 from5 to6 value7 }8}
Resposta de query:
1{2 "data": {3 "transfers": [4 {5 "id": "0x00010000",6 "from": "0xabcd...",7 "to": "0x1234...",8 "value": "256"9 },10 {11 "id": "0x00020000",12 "from": "0xefgh...",13 "to": "0x5678...",14 "value": "512"15 },16 {17 "id": "0x01000000",18 "from": "0xijkl...",19 "to": "0x9abc...",20 "value": "1"21 }22 ]23 }24}
As IDs são retornadas como hex.
Para melhorar a organização, devemos criar outro campo na entidade que seja um BigInt.
1type Transfer @entity {2 id: Bytes!3 from: Bytes! # address4 to: Bytes! # address5 value: BigInt! # unit2566 tokenId: BigInt! # uint2567}
Isto otimizará sequencialmente a organização.
Query:
1{2 transfers(first: 3, orderBy: tokenId) {3 id4 tokenId5 }6}
Resposta de query:
1{2 "data": {3 "transfers": [4 {5 "id": "0x…",6 "tokenId": "1"7 },8 {9 "id": "0x…",10 "tokenId": "2"11 },12 {13 "id": "0x…",14 "tokenId": "3"15 }16 ]17 }18}
Conclusão
Using both Immutable Entities and Bytes as IDs has been shown to markedly improve Subgraph efficiency. Specifically, tests have highlighted up to a 28% increase in query performance and up to a 48% acceleration in indexing speeds.
Leia mais sobre o uso de Entidades Imutáveis e Bytes como IDs nesta publicação por David Lutterkort, Engenheiro de Software na Edge & Node: Duas Melhorias Simples no Desempenho de Subgraphs.