Cardano GraphQL

As its name suggests the Cardano GraphQL project adds support for the GraphQL query language to the Cardano ecosystem.

Services

The Cardano GraphQL project includes the following services:

  • PostgreSQL
  • Cardano Node Ogmios
  • Cardano DB Sync Extended
  • Hasura
  • Cardano GraphQL

Getting Started

The project includes an example docker-compose.yml. However, before you start it is important that you understand the interdependencies between each of the project's services.

For example, you need to sync the Cardano Node with the blockchain before you use Cardano DB Sync Extended to restore a snapshot (see: Cardano DB Sync rolling back to genesis).

docker-compose-cardano-node.yml:

version: "3.8"

services:
  cardano-node-ogmios:
    image: cardanosolutions/cardano-node-ogmios:${CARDANO_NODE_OGMIOS_VERSION:-v5.1.0}-${NETWORK:-mainnet}
    restart: on-failure
    container_name: cardano-node-ogmios
    ports:
      - ${OGMIOS_PORT:-1337}:1337
    volumes:
      - node-db:/db
      - node-ipc:/ipc
    logging:
      driver: "json-file"
      options:
        max-size: "400k"
        max-file: "20"

volumes:
  node-db:
  node-ipc:

Pull and run Cardano Node Ogmios:

export NETWORK=mainnet && \
docker-compose -f docker-compose-cardano-node.yml up -d

After 24 hours or so you can bring down the service:

docker-compose down

Tip: Cardano Node does not need to be at the tip of the blockchain it just needs to be past the last block in the snapshot (e.g., db-sync-snapshot-schema-12-block-6451499-x86_64.tgz) you want to restore. So keep an eye on the logs:

docker container logs -f --tail 10 cardano-node-ogmios

Now you are ready to pull and run all the Cardano GraphQL services and restore a snapshot:

export NETWORK=mainnet && \
export RESTORE_SNAPSHOT=/snapshots/db-sync-snapshot-schema-12-block-6451499-x86_64.tgz && \
docker-compose up -d

docker-compose.yml:

version: "3.8"

services:
  postgres:
    image: postgres:${POSTGRES_VERSION:-11.5-alpine}
    restart: on-failure
    container_name: postgres
    environment:
      - POSTGRES_LOGGING=true
      - POSTGRES_DB_FILE=/run/secrets/postgres_db
      - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password
      - POSTGRES_USER_FILE=/run/secrets/postgres_user
    ports:
      - ${POSTGRES_PORT:-5432}:5432
    secrets:
      - postgres_db
      - postgres_password
      - postgres_user
    shm_size: '2gb'
    volumes:
      - postgres-data:/var/lib/postgresql/data
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
        
  cardano-node-ogmios:
    image: cardanosolutions/cardano-node-ogmios:${CARDANO_NODE_OGMIOS_VERSION:-v5.1.0}-${NETWORK:-mainnet}
    restart: on-failure
    container_name: cardano-node-ogmios
    ports:
      - ${OGMIOS_PORT:-1337}:1337
    volumes:
      - node-db:/db
      - node-ipc:/ipc
    logging:
      driver: "json-file"
      options:
        max-size: "400k"
        max-file: "20"

  cardano-db-sync-extended:
    working_dir: /var/lib/cdbsync
    image: inputoutput/cardano-db-sync:${CARDANO_DB_SYNC_VERSION:-12.0.0}
    command: [
      "--config", "/config/cardano-db-sync/config.json",
      "--socket-path", "/node-ipc/node.socket"
    ]
    restart: on-failure
    container_name: cardano-db-sync-extended
    environment:
      - EXTENDED=true
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - RESTORE_SNAPSHOT=${RESTORE_SNAPSHOT:-}
      - RESTORE_RECREATE_DB=N
    secrets:
      - postgres_password
      - postgres_user
      - postgres_db
    volumes:
      - ./config/network/${NETWORK:-mainnet}:/config
      - db-sync-data:/var/lib/cdbsync
      - db-sync-tmp:/tmp
      - node-ipc:/node-ipc
      - ./snapshots:/snapshots
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
    depends_on:
      - cardano-node-ogmios
      - postgres

  hasura:
    image: inputoutput/cardano-graphql-hasura:${CARDANO_GRAPHQL_VERSION:-6.2.0}
    restart: on-failure
    container_name: hasura
    ports:
      - ${HASURA_PORT:-8090}:8080
    environment:
      - HASURA_GRAPHQL_ENABLE_CONSOLE=true
      - HASURA_GRAPHQL_CORS_DOMAIN=http://localhost:9695
    secrets:
      - postgres_db
      - postgres_password
      - postgres_user
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
    depends_on:
      - postgres

  cardano-graphql:
    build:
      context: ./services/cardano-graphql
      dockerfile: Dockerfile
    restart: on-failure
    container_name: cardano-graphql
    environment:
      - SLEEP=${SLEEP:-3600}
      - ALLOW_INTROSPECTION=true
      - CACHE_ENABLED=true
      - LOGGER_MIN_SEVERITY=${LOGGER_MIN_SEVERITY:-info}
      - PROMETHEUS_METRICS=true
      - TRACING=true
    expose:
      - ${API_PORT:-3100}
    ports:
      - ${API_PORT:-3100}:3100
    secrets:
      - postgres_db
      - postgres_password
      - postgres_user
    logging:
      driver: "json-file"
      options:
        max-size: "200k"
        max-file: "10"
    depends_on:
      - postgres

  prometheus:
    image: prom/prometheus:latest
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--web.enable-lifecycle'
    container_name: prometheus
    ports:
      - ${PROMETHEUS_PORT:-9090}:9090
    volumes:
      - ./prometheus:/etc/prometheus
      - prometheus-data:/var/lib/prometheus/data
    depends_on:
      - cardano-graphql

  grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    ports:
      - ${GRAFANA_PORT:-3000}:3000
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning
    depends_on:
      - prometheus

secrets:
  postgres_db:
    file: ./placeholder-secrets/postgres_db
  postgres_password:
    file: ./placeholder-secrets/postgres_password
  postgres_user:
    file: ./placeholder-secrets/postgres_user

volumes:
  db-sync-data:
  db-sync-tmp:
  node-db:
  node-ipc:
  postgres-data:
  prometheus-data:

Navigate to the GraphQL Playground: http://localhost:3100/graphql

Query the epoch dataset:

{ cardanoDbMeta { initialized syncPercentage }}

Wait for initialized to be true to ensure the epoch dataset is complete.

Query the full dataset:

{ cardano { tip { number slotNo epoch { number } } } }

Cardano GraphQL

I added an ENTRYPOINT to the cardano-graphql build so that it would wait (it might need to sleep for a hour or more depending on the size of the snapshot) for the snapshot to be restored before it tries to add any new (Hasura) views to PostgreSQL.

docker-entrypoint.sh

#!/bin/sh

# Abort on any error
set -e

sleep "$SLEEP"

# Run the main container command
exec "$@"

Dockerfile

FROM inputoutput/cardano-graphql:6.2.0-mainnet

COPY docker-entrypoint.sh docker-entrypoint.sh
RUN chmod +x ./docker-entrypoint.sh

ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["node", "index.js"]

Docker Desktop for Mac

When I first tried to restore a snapshot I paid close attention to the logs:

docker container logs -f --tail 10 cardano-db-sync-extended

I encountered the following issue:

psql:/tmp/db-sync-snapshot-hc6bN9lE7U/db-sync-snapshot-schema-12-block-6451499-x86_64.sql:238416086: ERROR:  could not extend file "base/16384/214298": No space left on device
HINT:  Check free disk space.

psql:/tmp/db-sync-snapshot-hc6bN9lE7U/db-sync-snapshot-schema-12-block-6451499-x86_64.sql:238416150: ERROR:  could not write block 35045 of temporary file: No space left on device
CONTEXT:  parallel worker

To address this issue I had to remove all containers, networks, images and volumes:

docker system prune --all

Remove the Docker.raw (see: ERROR: No space left on device) file:

sudo rm /Users/<user>/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw

And increase the default Disk image size:

References: