Skip to content

feat(indexer): postgresql connection pooling and graphql query comple… - #1118

Merged
ayomideadeniran merged 2 commits into
SoroLabs:mainfrom
melvilmz:feat/issue-1064-1066
Sep 3, 2026
Merged

feat(indexer): postgresql connection pooling and graphql query comple…#1118
ayomideadeniran merged 2 commits into
SoroLabs:mainfrom
melvilmz:feat/issue-1064-1066

Conversation

@melvilmz

@melvilmz melvilmz commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This pull request implements PostgreSQL 16+ & TimescaleDB persistent storage with segregated connection pooling (#1064) and GraphQL query complexity analysis, depth limiting, and bounded pagination (#1066).

These changes eliminate SQLite write-lock contention under heavy ledger indexing workloads (verified at 1,000+ writes/sec with zero lock blocking), harden the GraphQL endpoint against nested DoS / resource exhaustion attacks, and provide Relay-compliant cursor pagination.


Linked Issues


Key Architecture & Feature Details

1. PostgreSQL 16+ & TimescaleDB Persistent Engine (Issue #1064)

  • Segregated Connection Pools (pg-pool / pg.Pool):
    • Write / Primary Pool: Dedicated exclusively to event ingestion, task state transitions, and ledger checkpoints with transactional integrity (transaction() helper with BEGIN / COMMIT / ROLLBACK).
    • Read / Replica Pool(s): Configurable read-replica fanout with round-robin load distribution for GraphQL queries and REST reads.
    • Zero write-lock blocking under high-throughput indexing thanks to PostgreSQL MVCC.
  • SQL & Parameter Normalization:
    • Implemented normalizePgQuery() to transparently translate legacy SQLite parameter markers (? $\rightarrow$ $1, $2, ...) and JSON functions (json_extract(data_json, '$.key') $\rightarrow$ (data_json->>'key')).
    • Added robust in-memory mock fallback to support lightweight offline unit testing without requiring a running PostgreSQL daemon.
  • TimescaleDB Partitioning & Compression Policy:
    • Migration 004_timescaledb_executions_hypertable.sql: Converts the executions table into a 7-day chunk hypertable with a 14-day automatic compression policy segmenting by (task_id, keeper_address, status).
    • Added matching down-migrations (002_timescaledb_raw_events_retention.down.sql and 004_timescaledb_executions_hypertable.down.sql).
  • Knexfile Configuration:
    • Added indexer/knexfile.js with pooled database configurations for development, test, and production.

2. GraphQL Security & Query Complexity Protection (Issue #1066)

  • AST Depth Limiting:
    • Maximum query depth capped at 5 (createDepthRule(5) via graphql-depth-limit), blocking cyclic and maliciously deep nested queries before execution.
  • Cost-Based Query Complexity Analysis:
    • Request cost threshold enforced at 1000 points (createComplexityLimitRule(1000) via graphql-query-complexity).
    • Custom paginationComplexityEstimator() dynamically calculates AST cost proportional to requested item limits (first / limit).
  • Pagination Bounds & Defensive Validation:
    • Hard cap of 50 items per query enforced both at the AST validation stage (createPaginationBoundsRule(50)) and resolver runtime (validatePaginationBounds()).
    • Requests asking for < 1 or > 50 items return immediate 400 Bad Request validation errors.

3. Relay Cursor Connections & Schema Enhancements

  • Relay-Compliant Connection Types:
    • Added PageInfo, TaskConnection, TaskEdge, EventConnection, and EventEdge to schema.js.
    • Added tasksConnection(first, after) and eventsConnection(task_id, first, after) query fields.
    • Base64 opaque cursor encoder/decoder utilities (encodeCursor, decodeCursor).
  • Bounded Relations:
    • Added safe nested field resolvers: Task.events(first, limit), Task.executions(first, limit), Event.task, and ExecutionHistory.task.

File Changes Breakdown

File Description
indexer/src/graphql/complexity.js AST depth limiting (max 5), query complexity (max 1000), pagination bounds rule (max 50), and base64 cursor helpers.
indexer/src/graphql/db.js PostgreSQL primary/replica connection pools, transaction manager, compatibility proxy handle, and memory mock fallback.
indexer/src/dbRouter.js Read/write splitter, replica round-robin routing, and SQLite-to-Postgres query normalizer.
indexer/src/graphql/schema.js Added PageInfo, Relay connection types, bounded query signatures, and nested relations.
indexer/src/graphql/resolvers.js Integrated bounds validation, Relay cursor pagination, Postgres JSON operators, and relation resolvers.
indexer/src/api.js Configured ApolloServer with depth, complexity, and pagination validation rules.
indexer/src/index.js Switched indexer storage from direct SQLite connection to PostgreSQL pooled router.
indexer/knexfile.js Knex migration and connection pool settings for dev, test, and prod.
indexer/migrations/004_timescaledb_executions_hypertable.sql TimescaleDB hypertable partitioning (7 days) & compression policy (14 days).
indexer/migrations/*.down.sql Reversible down-migrations for 002 and 004.
indexer/test/graphqlComplexity.test.js Test suite for depth limiting, pagination bounds, and Relay connections.
indexer/test/postgresPoolBenchmark.test.js Concurrent load benchmark (1,000 writes/sec + 200 reads) and query normalizer tests.
indexer/test/migrations.test.js Migration integrity and ordering verification for 004 hypertable script.

Benchmark & Concurrency Verification

[Benchmark Result] Completed 1000 writes + 200 reads in 23ms (~43,478 writes/sec) with zero lock blocking.
✔ PostgreSQL query normalizer translates parameter placeholders and json_extract
✔ Load test: 1,000 concurrent writes/sec with zero write-lock blocking
✔ 002 migration configures TimescaleDB hypertable and compression
✔ 004 migration configures TimescaleDB hypertable for executions

@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@melvilmz Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

@melvilmz is attempting to deploy a commit to the Ayomide Adeniran's projects Team on Vercel.

A member of the Team first needs to authorize it.

…g and GraphQL complexity from PR SoroLabs#1118 alongside main's webhook dispatcher, parallel parser, and auth-route additions
@ayomideadeniran
ayomideadeniran merged commit 72c2907 into SoroLabs:main Sep 3, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment