Skip to content

An app that lists jsResource before graphqlSchema can't see its own schema tables from resources.js on the first boot of a fresh data root #4258

Open

Description

@kriszyp

What happens

When an application's config.yaml lists jsResource before graphqlSchema, its resources.js cannot see its own schema tables at module top level on the first boot of a fresh data root. tables.CachedThing is undefined, so tables.CachedThing.sourcedFrom(Source, { expiration: 2, eviction: 2 }) throws Cannot read properties of undefined (reading 'sourcedFrom'). No source is attached, and GET /CachedThing/<id> returns 404.

After a restart on the same data root, the table already exists in the persisted catalog when components load. The same application then works: sourcedFrom is applied, the worker reports expirationMS/evictionMS 2000/2000, all 20 GETs return 200, and the cached rows expire and are removed at about 5 s.

The application behaves differently on its first boot than on every later boot.

Owner Build First boot, fresh root After restart
isolated app (threads: {count: 2, maxIsolated: 1}, dedicated worker) PR head 51a5a77 (×3 RocksDB, ×1 LMDB) typeof tables.CachedThing undefined; 20/20 GETs 404 20/20 GETs 200; rows removed at 5.0–5.1 s
isolated app main 87078c2dc same: undefined, 20/20 GETs 404 20/20 GETs 200; rows removed at 5.1 s
shared owner, threads.count: 2 PR head 51a5a77 undefined on the worker sampled; 20/20 GETs 404 works
shared owner, threads.count: 2 main 87078c2dc undefined on worker 0; GETs 6×200, 14×404 (per-worker split) works

On a shared owner the result depends on timing. A worker whose resources.js evaluates after another worker's graphqlSchema has created the table can see the table. A worker that evaluates earlier cannot. So the workers disagree until a restart.

Repro

  • Harper 51a5a77c1ff8e8847a6f1708f1fc1ebcdeb2c4ca (PR Clear a removed table TTL everywhere, reject invalid TTLs where they are declared, and scan catalog-loaded tables without a write #4250 head) and 87078c2dc5a2217d5ae1b4da834a737c466de840 (main, the merge base).
  • Spec: QA scenario QA-5430 (qa5430-iso-ttl.test.ts with fixture qa5430-iso-ttl/sourced, kept in the QA archive; candidate P-6847 promotes the TTL part to an integration test). Run from integrationTests/qa-scratch/:
    QA5430_ARM=A npm run test:integration -- "integrationTests/qa-scratch/qa5430-iso-ttl.test.ts"                        # isolated
    QA5430_ARM=A QA5430_OWNER=shared npm run test:integration -- "integrationTests/qa-scratch/qa5430-iso-ttl.test.ts"    # shared, threads 2
    
  • Fixture config.yaml: rest: true / jsResource: {files: resources.js} / graphqlSchema: {files: schema.graphql}. Schema: type CachedThing @table @export { id: ID @primaryKey value: String }.
  • resources.js calls tables.CachedThing.sourcedFrom(...) at top level inside a try/catch, and an /Info/ resource reports typeof tables.CachedThing at load along with the caught error.

Mechanism

Existing work checked

Options

  1. Declare a component's schema tables before evaluating any of its jsResource modules (a graphqlSchema pre-pass in the loader). First boot would then match restart.
  2. Or document the order requirement, and log a warning when a component lists jsResource before graphqlSchema.

What this does not prove

  • The shared-owner run was RocksDB only. LMDB was run for the isolated owner.
  • Not tested: a resources.js with no try/catch. Does the top-level throw fail the component and show in get_status, or only log?
  • Not tested: the deploy_component path. The fixture was staged before startHarper.
  • Not tested: a new node joining a cluster, and whether a later redeploy repairs it without a restart.
  • Whether this order is a supported configuration at all is a product decision. The behaviour is measured; the contract is undocumented.

Found by PR QA on harper#4250 (QA finding F-1245); pre-existing on main.

🤖 Filed by Claude Opus 5.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    P2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions