Démarrez votre indexeur (tutoriel)

Pour le dernier réseau gdev, j’avais rédigé un tutoriel pour démarrer un indexeur : Duniter | Run an indexer

Aujourd’hui j’aimerais avoir des retours pour améliorer ce tutoriel, et si possible des contributions directes.

Quelques modifications :

  • utiliser l’image h30x/duniter-indexer en attendant que la CI soit validée et les améliorations mergées dans master
  • vérifier la présence du network default dans le docker-compose.yml (cf Mise en production de l'indexeur, j’ai mis à jour la doc)

Avec les insertions par batch, l’indexeur devrait être beaucoup plus rapide à démarrer. Les logs sont écrits dans un fichier dédié dans le volume logs. On y voit notamment

  • les événements non pris en charge
# Unhandled event: account.AccountLinked
{"level":40,"time":1700575226062,"pid":1,"hostname":"d1bb47d605b6","msg":"[30801] Unhandled event: account.AccountLinked"}
  • les erreurs
# ici l'indexeur tente d'ajouter la transaction avant le nouveau compte, c'est un bug
{"level":50,"time":1700572081106,"pid":1,"hostname":"d1bb47d605b6","err":{"type":"PostgresError","message":"insert or update on table \"transaction\" violates foreign key constraint \"transaction_receiver_id_fkey\"","stack":"PostgresError: insert or update on table \"transaction\" violates foreign key constraint \"transaction_receiver_id_fkey\"\n    at ErrorResponse (file:///node_modules/postgres/src/connection.js:790:26)\n    at handle (file:///node_modules/postgres/src/connection.js:476:6)\n    at Socket.data (file:///node_modules/postgres/src/connection.js:315:9)\n    at Socket.emit (node:events:517:28)\n    at addChunk (node:internal/streams/readable:335:12)\n    at readableAddChunk (node:internal/streams/readable:308:9)\n    at Readable.push (node:internal/streams/readable:245:10)\n    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)\n    at new Query (file:///node_modules/postgres/src/query.js:35:9)\n    at Object.sql (file:///node_modules/postgres/src/index.js:112:11)\n    at EventEmitter.<anonymous> (file:///build/assets/transaction-21260075.mjs:13:22)\n    at EventEmitter.emit (node:events:517:28)\n    at processBlock (file:///build/assets/start-indexer-e0d1da9f.mjs:220:26)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n    at async indexBlocks (file:///build/assets/start-indexer-e0d1da9f.mjs:289:15)\n    at async file:///build/assets/start-indexer-e0d1da9f.mjs:346:7","name":"PostgresError","severity_local":"ERROR","severity":"ERROR","code":"23503","detail":"Key (receiver_pubkey)=(5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz) is not present in table \"account\".","schema_name":"public","table_name":"transaction","constraint_name":"transaction_receiver_id_fkey","file":"ri_triggers.c","line":"2463","routine":"ri_ReportViolation","query":"INSERT INTO transaction (\"issuer_pubkey\",\"receiver_pubkey\",\"amount\",\"created_at\",\"created_on\")values($1,$2,$3,$4,$5)","parameters":["5DSF5HxiQvy2xJdtdtMSPYZdSoLAsGDxzJaifiAfAByinrH4","5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz","5000","2023-11-21T13:07:48.002Z","30277"],"args":[{"first":{"issuer_pubkey":"5DSF5HxiQvy2xJdtdtMSPYZdSoLAsGDxzJaifiAfAByinrH4","receiver_pubkey":"5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz","amount":5000,"created_at":"2023-11-21T13:07:48.002Z","created_on":30277},"rest":[]}],"types":[25,25,700,1184,23]},"msg":"insert or update on table \"transaction\" violates foreign key constraint \"transaction_receiver_id_fkey\""}
{"level":40,"time":1700572081107,"pid":1,"hostname":"d1bb47d605b6","msg":"[30277] Insert account 5Dq8xjvkmbz7q4g2LbZgyExD26VSCutfEc6n4W4AfQeVHZqz"}

Pour info les logs

relation "parameters" does not exist

signifient qu’un nouveau bloc est finalisé sur le réseau mais que l’indexeur n’a pas encore fini d’indexer le genesis

et les logs

Already indexing.

signifient qu’un nouveau bloc est finalisé sur le réseau mais que l’indexeur est en train de rattraper son retard.

1 Like

Pourrais-tu publier ton docker-compose.yml à jour ? Ou un fichier équivalent, car là j’ai beau essayer je n’arrive pas à lancer d’indexeur.

En fait je n’arrive même pas à faire démarrer Hasura.

Voilà le docker-compose qui tourne actuellement sur mon serveur :

détail
services:
  # postgres database
  postgres:
    image: postgres:12
    restart: always
    volumes:
      - postgres-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: xxx

  # hasura
  graphql-engine:
    image: duniter/hasura-indexer:latest
    depends_on:
      - postgres
    restart: always
    ports:
      - 127.0.0.1:8484:8080 # 8080 already used by wotwizard
    environment:
      # postgres database
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:xxx@postgres:5432/postgres
      # enable the console served by server
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      # dev mode
      HASURA_GRAPHQL_DEV_MODE: "false"
      # logging
      # HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup
      # admin password
      HASURA_GRAPHQL_ADMIN_SECRET: hasura_password
      # Name of role when the Authorization header is absent in JWT
      HASURA_GRAPHQL_UNAUTHORIZED_ROLE: public
      # telemetry
      HASURA_GRAPHQL_ENABLE_TELEMETRY: "false"
      HASURA_GRAPHQL_MIGRATIONS_SERVER_TIMEOUT: 60

  # indexer (must have duniter-archive on duniter network)
  indexer:
    image: h30x/duniter-indexer
    environment:
      - POSTGRES_HOST=postgres
      - INDEXER_DUNITER_WS_ENDPOINT=ws://duniter-archive:9944
      - INDEXER_DUNITER_WS_ENDPOINT_GRAPHIQL=wss://gdev.coinduf.eu/ws
      - INDEXER_HASURA_GRAPHQL_ENDPOINT_GRAPHIQL=https://hasura.gdev.coinduf.eu
    restart: unless-stopped
    ports:
      - 127.0.0.1:3000:3000
    depends_on:
      # - duniter-archive # depends on archive node through network
      - postgres
      - graphql-engine
    volumes:
      - logs:/logs
      - resources:/resources
    # allows to connect to duniter node
    networks:
      - default
      - duniter

# define volumes
volumes:
  postgres-data:
  logs:
  resources:

# define duniter external network to allow connect to duniter archive node
networks:
  duniter:
    name: duniter-gdev-archive_default
    external: true

(j’allais justement arrêter de bosser, tu arrives pile au bon moment)

Merci, mais il doit manquer quelque chose. Voici les logs que j’avais hier soir et que j’ai encore ce matin :

2023-11-22 08:00:23 {"timestamp":"2023-11-22T07:00:23.000+0000","level":"info","type":"startup","detail":{"kind":"migrations-startup","info":"migrations server port env var is not set, defaulting to 9691"}}
2023-11-22 08:00:23 {"timestamp":"2023-11-22T07:00:23.000+0000","level":"info","type":"startup","detail":{"kind":"migrations-startup","info":"server timeout is not set, defaulting to 30 seconds"}}
2023-11-22 08:00:23 {"timestamp":"2023-11-22T07:00:23.000+0000","level":"info","type":"startup","detail":{"kind":"migrations-startup","info":"starting graphql engine temporarily on port 9691"}}
2023-11-22 08:00:23 {"timestamp":"2023-11-22T07:00:23.000+0000","level":"info","type":"startup","detail":{"kind":"migrations-startup","info":"waiting 30 for 9691 to be ready"}}
2023-11-22 08:00:55 {"timestamp":"2023-11-22T07:00:55.000+0000","level":"info","type":"startup","detail":{"kind":"migrations-startup","info":"failed waiting for 9691, try increasing HASURA_GRAPHQL_MIGRATIONS_SERVER_TIMEOUT (default: 30)"}}

Peux-tu essayer sur une machine “neuve” ? Je me demande si tu ne réutilises pas des volumes pré-existants avec des données dedans que moi je n’ai pas.

Édit : ah, tiens, j’ai un résultat positif sur une autre machine. C’est encourageant.

J’ai bien supprimé les volumes avant de les recréer, et je le fais régulièrement en mode dev. Mais bonne idée, je ferai sur une machine neuve comme le “tuto d’installation en moins de 10 minutes”.

Par rapport à tes logs :

migrations server port env var is not set, defaulting to 9691

Étrange, parce que le serveur de migration c’est la base postgres si je me trompe pas et son port est défini à 5432. C’est drôle parce qu’avec une recherche sur “port 9691”, je tombe justement sur une issue hasura à ce sujet : Auto-apply migrations/metadata version breaks the app · Issue #5172 · hasura/graphql-engine · GitHub. Ça l’air un peu spécifique comme bug.

Et ensuite c’est les conséquences d’avoir le mauvais port. Donc il faut trouver la variable d’environnement responsable et lui donner une valeur.

Donc oui, possible qu’une autre machine fasse l’affaire.

C’est bon pour moi, j’avais plusieurs soucis dont – je pense – les propriétés de networks.

Disponible sur : https://gdev-indexer.cgeek.fr (indexation en cours)

Par contre pour mon installation qui est sur ARM, j’ai dû recompiler pour les images Docker. Et je devrais le faire à chaque mise à jour. Idéalement il faudrait le faire automatiquement avec la CI comme pour Duniter dont on peut s’inspirer (.gitlab-ci et Dockerfile). J’ai créé #144 pour ne pas oublier.

Tu n’a pas dû prendre la dernière version parce que là ton genesis est celui d’une chaîne de développement.

Et sinon l’issue devrait plutôt aller dans le dépôt de l’indexeur, non ?

Ah oui, j’étais sur master j’ai pensé que c’était la bonne sans vérifier. C’est corrigé.

Quant aux issues, oui en théorie. Mais c’est plus simple de tout centraliser dans le projet Duniter vu la petite taille des équipes et des moyens disponibles.

1 Like

Salut,

Je tente à mon tour de monter une instance d’indexeur. Mais je suis bien incapable de vérifier que ça fonctionne correctement. Si quelqu’un veut bien jeter un oeil là : https://indexer-gdev.pini.fr.

EDIT: J’ai ce message d’erreur en boucle dans la log de duniter-indexer :

2023-12-16 22:46:55        RPC-CORE: getBlock(hash?: BlockHash): SignedBlock:: -32000: Client error: Api called for an unknown Block: State already discarded for 0xa565a0ccbab8e5f29f0c8a901a3a062d17360a4d4f5319d03a1580fba4cbf3f6

EDIT 2: Bon en fait mon noeud n’était pas en mode archive. Je le reconstruis, puis je relancerai l’indexeur avec suppression des données, voir si ça lui plaît mieux.

EDIT 3: Ça y est ça a l’air de marcher.

3 Likes

En examinant les logs de mon indexeur je trouve cette erreur intervenue probablement pendant la synchro :

node:internal/process/promises:288
            triggerUncaughtException(err, true /* fromPromise */);
            ^

PostgresError: insert or update on table "identity" violates foreign key constraint "identity_id_fkey"
    at ErrorResponse (file:///node_modules/postgres/src/connection.js:790:26)
    at handle (file:///node_modules/postgres/src/connection.js:476:6)
    at Socket.data (file:///node_modules/postgres/src/connection.js:315:9)
    at Socket.emit (node:events:517:28)
    at addChunk (node:internal/streams/readable:335:12)
    at readableAddChunk (node:internal/streams/readable:308:9)
    at Readable.push (node:internal/streams/readable:245:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
    at new Query (file:///node_modules/postgres/src/query.js:35:9)
    at Object.sql (file:///node_modules/postgres/src/index.js:112:11)
    at EventEmitter.<anonymous> (file:///build/assets/identity-83441b8c.mjs:53:20)
    at EventEmitter.emit (node:events:517:28)
    at processBlock (file:///build/assets/start-indexer-e0d1da9f.mjs:220:26)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async indexBlocks (file:///build/assets/start-indexer-e0d1da9f.mjs:289:15)
    at async Timeout._onTimeout (file:///build/assets/start-indexer-e0d1da9f.mjs:363:7) {
  severity_local: 'ERROR',
  severity: 'ERROR',
  code: '23503',
  detail: 'Key (pubkey)=(5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn) is not present in table "account".',
  schema_name: 'public',
  table_name: 'identity',
  constraint_name: 'identity_id_fkey',
  file: 'ri_triggers.c',
  line: '2528',
  routine: 'ri_ReportViolation',
  query: '\n      UPDATE identity SET "pubkey"=$1\n      WHERE pubkey = $2\n    ',
  parameters: [
    '5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn',
    '5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv'
  ],
  args: [
    Builder {
      first: {
        oldPubkey: '5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv',
        pubkey: '5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn'
      },
      rest: [ 'pubkey' ]
    },
    '5GMyvKsTNk9wDBy9jwKaX6mhSzmFFtpdK9KNnmrLoSTSuJHv'
  ],
  types: [ 25, 25 ]
}
1 Like

Oui, il y a des bugs dans l’indexation, c’est plus ou moins lié à ce sujet : ⚠ Relations circulaires compliquées. Le fix implémenté par Manu de trier les événements ne fonctionne pas toujours, je n’ai pas compris pourquoi. Comme subsquid est plus facile à débugger, je me fie plutôt à cet indexeur. Il va falloir qu’on fasse un choix à un moment mais pour l’instant on est dans une position inconfortable où les outils utilisent duniter-indexer (Ğecko, gecko-web, Ğcli) mais où les données les plus fiables sont sur duniter-squid.

Et c’est en travaillant sur duniter-squid que je me suis rendu compte de la galère que c’était de gérer tous les événements liés aux pending membership, d’où la réflexion À quoi servent les pending membership? qui a abouti à leur retrait (MR !215). Donc c’est normal que tout bouge encore sous nos pieds, c’est justement ça le travail de stabilisation ! (je le dis surtout pour les autres qui lisent ces messages parce que je me doute que tu le sais :wink:).

2 Likes

A tous ceux qui héberge un indexer Hasura (notamment @HugoTrentesaux, @cgeek et @Pini), pouvez-vous activer l’écoute websocket dans votre conf nginx svp ?

      # for websocket
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";

Exemple pour ma conf datapod:

upstream gdev-datapod.p2p.legal {
   server       10.10.10.107:10010; 
}

[...]

   location / {
      proxy_pass        http://gdev-datapod.p2p.legal;
      proxy_set_header  X-Real-IP  $remote_addr;
      proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto https;
      proxy_set_header  Host $http_host;
      proxy_redirect    off;

      # for websocket
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
   }

C’est nécessaire pour utiliser des subscriptions GraphQL. Merci!

1 Like

N’est-ce pas déjà le cas pour gdev-indexer.cgeek.fr ? J’ai la même conf que pour d’autres services qui fonctionnent déjà (notamment la webui de Duniter v1).

As-tu une URL que je peux tester pour vérifier ?

Via la console Hasura, tu peux faire des subscriptions, par exemple sur les datapod:

subscription {
  profiles_by_pk(address: "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn") {
    updated_at
  }
}

N’importe quelle query peut se transformer en subscription.
Tu fais play et le bouton doit se transformer en bouton stop, si la requête initiale fonctionne c’est que WS est OK.

Je n’ai plus accès une console Hasura d’un indexer vue que le miens est HS, mais j’ai dû ajouter ça dans ma conf nginx pour datapod pour ws.

En fait le code que je fais dans gecko pour tenter une subscription ne fonctionne sur aucun de vos 3 indexer et moi j’en ai pas, donc je sais pas si le soucis vient de moi (probablement c’est la première fois que j’en fait en GraphQL) ou de votre conf nginx ^^

Sur ma console hasura ton exemple donne ça :

Non excuse moi j’étais pas clair, il s’agit d’une requête datapod et non indexer.

Voilà un exemple qui devrait fonctionner mais que je n’ai pas pu tester:

subscription ($address: String!) {
  account_by_pk(pubkey: $address) {
    transactions_issued(limit: 1) {
      receiver_pubkey
      amount
      created_at
    }
  }
}

En fait n’importe quelle query classique tu remplaces le mot clé query par subscription avant la requête.

subscription {
  account_by_pk(pubkey: "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn") {
    transactions_issued(limit: 1) {
      receiver_pubkey
      amount
      created_at
    }
  }
}

Là j’ai bien le bouton Play qui se transforme en bouton Stop.

2 Likes

ok et surtout la requête initiale semble avoir fonctionner visiblement, donc c’est que tes websockets sont ok, donc je fais n’imp dans mon code :laughing:

Là regarde tu va voir la transaction changer dans 30 secondes

(voilà, si ton indexer fonctionne …)

1 Like