Skip to content

bug: [P2] trace loses shutdown observations recorded after the beforeExit flush #75

Description

@aoto-tech

Scope

  • Commit: 1a88d503e83ceb6688d41be1e00495abfd17f054
  • Package: @cellfence/trace (0.4.0)
  • Test environment: Windows, Node.js v24.12.0

Actual and expected behavior

When an application's beforeExit listener schedules additional asynchronous work, observations recorded by that work are missing from the final evidence. Node.js can continue running application shutdown work after trace has already flushed its evidence during beforeExit.

The expected result is that both before-shutdown and during-shutdown, recorded before the process exits, are retained in the final evidence.

Reproduction

This reproduction uses the actual Node.js event loop and shutdown events. Only evidence writes are mocked; no database communication takes place.

import fs from "node:fs";
import path from "node:path";

const root = process.cwd();
const writes = [];
fs.mkdirSync = () => undefined;
fs.writeFileSync = (filename, data) => {
  writes.push({ filename: path.resolve(filename), data: String(data) });
};
process.env.CELLFENCE_TRACE_COMMIT_SHA = "0000000000000000000000000000000000000000";
process.env.CELLFENCE_TRACE_OUT = path.join(root, "mock-evidence.json");
const trace = await import("./packages/trace/dist/index.js");
trace.recordDatabaseAccess("before-shutdown");
let lateRecorded = false;
process.once("beforeExit", () => {
  setImmediate(() => {
    trace.recordDatabaseAccess("during-shutdown");
    lateRecorded = true;
  });
});
process.once("exit", () => {
  console.log({
    lateRecorded,
    flushCount: writes.length,
    selectors: writes.flatMap(w => JSON.parse(w.data).accesses.map(a => a.selector))
  });
});

Observed result:

{"lateRecorded":true,"flushCount":1,"selectors":["before-shutdown"]}

Cause and fix requirements

registerFlushHooks registers both beforeExit and exit hooks, but flushEvidence sets flushed = true on the first write and always skips subsequent writes. New observations added after the first flush need to be included when the process exits.

Validation scope

Verified using the trace implementation compiled from the specified commit. Because the environment was read-only, file creation and evidence writes were replaced with in-memory mocks. After building the repository, save the reproduction code as repro.mjs in the repository root and run it with node repro.mjs. The full test suite was not run.

If you find this repository useful, please consider giving it a star.

Activity

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions