Skip to content

fix: make gRPC max recv message size configurable and distinguish ResourceExhausted errors - #427

Open
fraseragain wants to merge 3 commits into
developfrom
fix/grpc-max-recv-msg-size
Open

fix: make gRPC max recv message size configurable and distinguish ResourceExhausted errors#427
fraseragain wants to merge 3 commits into
developfrom
fix/grpc-max-recv-msg-size

Conversation

@fraseragain

Copy link
Copy Markdown

Summary

DID Documents whose resource collections exceed gRPC's default 4MB receive-message limit fail resolution — e.g. did:cheqd:testnet:a6af96a5-ec0c-4b66-b4cd-6e5916cadc8b, whose collection has grown to 9,008 linked resources (~4.2MB), returns notFound from resolver.cheqd.net despite existing and resolving correctly via direct chain queries.

Root cause: openGRPCConnectionWithTimeout opens its gRPC connection with no message-size override, so QueryCollectionResources (called during normal DID resolution to build metadata.resources) fails client-side with codes.ResourceExhausted once a collection's response exceeds 4MB. Every LedgerService.Query* method then maps any gRPC error, including ResourceExhausted, straight to NewNotFoundError, so the real cause was invisible — the resolver reported notFound for a DID that genuinely exists.

  • codes.ResourceExhausted is a safe, unambiguous signal to special-case: confirmed cheqd-node returns codes.Unknown (wrapped Cosmos SDK error) for a genuinely nonexistent DID, never codes.NotFound, so it can't collide with a real not-found response.
  • Found a second DID (did:cheqd:testnet:bf2d7da8-0f79-43cc-8126-cb176e589c5c) from the same publishing pattern at 3,746 resources (~1.8MB) and still actively growing — this isn't a one-off, other DIDs are on the same trajectory.

Changes

  • Adds GRPC_MAX_RECV_MSG_SIZE as a configurable setting (default 16MB), threaded through the existing RawConfig/Config → constructor-injection pipeline the same way other ledger connection settings (TESTNET_ENDPOINT timeout/TLS) already work — not hardcoded, since an operator being unable to raise this without a code change is what caused the original incident to go unresolved.
  • Adds a mapGrpcError helper (mirroring the status.FromError/codes pattern already used in endpoint_manager.go) that maps ResourceExhausted to internalError (500) instead of notFound (404), so a future overflow is diagnosable rather than silently misreported. All other gRPC errors keep today's notFound behaviour unchanged.
  • Documents the new env var in docker-compose.yml and README.md alongside the existing ones.

No automated test added for mapGrpcError — the existing tests/unit/tests/integration suites use a MockLedgerService fake that fabricates *types.IdentityError directly and never exercises the real gRPC call path this touches; a meaningful test would need a colocated services/*_test.go file, a first for this codebase. Verified manually instead (see test plan).

Test plan

  • go build ./... — clean
  • gofmt -l . / go vet ./... — clean
  • make unit-tests — all 7 suites, 227 specs, 0 failures
  • Ran the resolver locally against the real grpc.cheqd.network backend:
    • did:cheqd:testnet:a6af96a5-ec0c-4b66-b4cd-6e5916cadc8b (9,008 resources) → 200 with full DID Document, previously 404
    • A genuinely nonexistent DID → unchanged 404 notFound
    • did:cheqd:testnet:bf2d7da8-0f79-43cc-8126-cb176e589c5c (3,746 resources, still growing) → unchanged 200
    • Restarted with GRPC_MAX_RECV_MSG_SIZE set below the DID's actual size → 500 internalError with a clear log line, confirming the error-mapping fires correctly rather than falling back to notFound

…ourceExhausted errors

DID Documents with resource collections exceeding gRPC's default 4MB
receive-message limit (e.g. did:cheqd:testnet:a6af96a5-ec0c-4b66-b4cd-6e5916cadc8b,
which has grown to 9,008 linked resources) fail resolution with a
gRPC ResourceExhausted error that was previously indistinguishable
from a genuine not-found DID, since every ledger query mapped any
error to notFound.

Adds a configurable GRPC_MAX_RECV_MSG_SIZE setting (default 16MB,
threaded through the existing Config/RawConfig pipeline alongside
the other ledger connection settings) so operators can raise the
limit without a code change, and distinguishes ResourceExhausted
from other gRPC errors so a future overflow surfaces as
internalError (500) instead of silently misreporting notFound (404).
The linked page moved as part of a Cosmos SDK docs site
reorganization; the URL now 404s. Points to the current location.
The Test workflow has been failing on every run since 2026-02-25
(develop and every subsequent PR, ~6 months) because both the
unit-tests and integration-tests jobs installed the ginkgo CLI via
`go install .../ginkgo@latest`, which ignores go.mod entirely and
always fetches the newest published release. ginkgo v2.32.1 now
requires Go >= 1.25.0, but CI's actions/setup-go resolves Go 1.24.0
from go.mod, so the install step itself failed before any tests ran.

go.mod already pins github.com/onsi/ginkgo/v2 v2.28.1 as a real
dependency, compatible with the project's own Go 1.24.0 requirement.
Dropping the @latest suffix makes `go install` resolve the CLI
version from the current module's go.mod/go.sum instead, keeping it
permanently in lockstep with the library version the project
actually depends on rather than drifting again on ginkgo's next
release.

Also fixes the integration-tests job's ginkgo install running from
`working-directory: ./..` (needed for @latest, which ignores the
local module regardless of cwd; breaks the module-relative install
once @latest is dropped, since it points outside the repo), and the
"Show logs" step's working-directory: ./docker/localnet, which
doesn't exist in this repo and was failing outright whenever
triggered, compounding earlier failures in the same job.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants