Skip to content

ADR 0002: Select testcase I/O only when requested ​

Context ​

The admin GraphQL Problem.testcase resolver previously selected input and output from PostgreSQL for every testcase request, including requests for only IDs, ordering, visibility, or scoring metadata. Large values may be stored out of line with PostgreSQL TOAST, making an unnecessary read and serialization costly.

The implementation PR measured one problem with 51 active testcases, including 40 out-of-line inputs and 30 out-of-line outputs. In one PostgreSQL 18.4 EXPLAIN (ANALYZE, BUFFERS, SERIALIZE TEXT) comparison, the metadata-only query took 0.152 ms to execute and returned 2 kB, while the query including I/O took 123.564 ms and returned 35,877 kB. These are single-query observations and exclude network transfer; they do not establish a general latency improvement for every query.

Decision drivers ​

  • Avoid fetching and serializing large testcase bodies when the GraphQL caller asks only for metadata.
  • Preserve the existing Problem.testcase schema and responses for callers that request I/O.
  • Respect aliases, fragments, and conditional directives when determining the selected fields.
  • Preserve the existing rule that ZIP-uploaded testcase I/O is not read from database columns.

Considered options ​

OptionBenefitCost or limitation
Always select input and outputSimple and preserves current query behaviorMetadata-only requests still read large values.
Add a separate metadata resolver or schema fieldMakes the cheaper access path explicitAdds another API path and requires callers to migrate.
Inspect the current GraphQL selection and conditionally select I/OKeeps the API stable and avoids the columns when unusedAdds selection parsing and requires tests for GraphQL selection semantics. Selected.

Decision ​

The admin Problem.testcase resolver checks whether either input or output is selected as an immediate child field. It uses graphql-parse-resolve-info through the shared hasSelectedField helper, which handles aliases, named and inline fragments, repeated fields, and @skip/@include directives. If selection parsing fails, the helper conservatively includes I/O to preserve the requested response.

The resolver passes that result to TestcaseService.getProblemTestcases. The service omits both columns from its Prisma select for metadata-only requests. When either field is requested, it selects both columns for testcases whose I/O is stored in the database. The existing sample and hidden ZIP flags continue to suppress database I/O selection for their respective testcase groups. The service defaults to including I/O for callers that do not pass the new argument.

Consequences ​

  • Metadata-only callers avoid unnecessary PostgreSQL I/O column reads and serialization.
  • Callers requesting either input or output still fetch both columns; the query is not optimized per individual I/O field.
  • Selection parsing adds a backend dependency and a small amount of resolver work.

Verification ​

The implementation includes tests for direct fields, aliases, fragments, conditional directives, repeated fields, abstract return types, resolver forwarding, and Prisma selection under all sample and hidden ZIP flag combinations. The PR reported passing backend typecheck and ESLint. Its unit tests could not run locally because native modules failed to load, so the test suite's runtime result was not established in that PR.