feat(orm): add $diagnostics() for cache stats and slow query tracking#2481
feat(orm): add $diagnostics() for cache stats and slow query tracking#2481
Conversation
…racking Introduces a new `$diagnostics()` method on ZenStackClient that returns Zod schema cache statistics and slow query information, helping users monitor and debug ORM performance. - Add `diagnostics` option to `ClientOptions` with `slowQueryThresholdMs` and `slowQueryMaxRecords` settings - Track slow queries in `ZenStackQueryExecutor` when diagnostics is enabled - Share slow query collection across derived clients (via $setAuth, $setOptions, $use, transactions, etc.) - Cap slow query records with an eviction policy that keeps the slowest queries (default max: 100) - Validate diagnostics config with Zod in ClientImpl constructor - Add `Diagnostics`, `QueryInfo`, and `ZodCacheStats` types - Add e2e tests covering all diagnostics features Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds diagnostics: zod cache stats exposure and in-memory slow-query tracking with configurable threshold/max records; validates diagnostics config; exposes client APIs ( Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics FeatureI've reviewed this PR and provide feedback on code quality, potential issues, performance, security, and test coverage. Todo List:
✅ Code Quality & ArchitectureStrengths:
Recommendations:
✅ Implementation AnalysisConfiguration & Validation (
Query Tracking (
Shared State Management (
✅ Security ReviewNo security concerns identified:
|
|
Claude finished @ymc9's task —— View job Claude finished @ymc9's task —— View job PR Review: Diagnostics FeatureI've reviewed this PR and provide feedback on code quality, potential issues, performance, security, and test coverage. Todo List:
✅ Code Quality & ArchitectureStrengths:
Recommendations:
✅ Implementation AnalysisConfiguration & Validation (
Query Tracking (
Shared State Management (
✅ Security ReviewNo security concerns identified:
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/orm/src/client/client-impl.ts (1)
85-101:⚠️ Potential issue | 🟠 MajorTrim inherited slow-query state when a child client lowers the cap.
slowQueriesis shared by reference across clones. If a parent has already accumulated records and a child is created with a smallerslowQueryMaxRecordsvia$setOptions(), the oversized buffer is inherited unchanged, so$diagnostics()can remain above the new limit forever. The0case is especially visible: future inserts stop, but existing records are never cleared.💡 Proposed fix
this.kyselyRaw = baseClient.kyselyRaw; this.auth = baseClient.auth; this.slowQueries = baseClient.slowQueries; + const maxRecords = + options.diagnostics === undefined ? undefined : options.diagnostics.slowQueryMaxRecords ?? 100; + if (maxRecords !== undefined && this.slowQueries.length > maxRecords) { + this.slowQueries.sort((a, b) => b.durationMs - a.durationMs); + this.slowQueries.length = maxRecords; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/client-impl.ts` around lines 85 - 101, When inheriting slowQueries from baseClient you must trim the shared buffer to respect a child client's smaller slowQueryMaxRecords (set via $setOptions) so $diagnostics() cannot exceed the new cap; after assigning this.slowQueries = baseClient.slowQueries, compute the new max (this.options.slowQueryMaxRecords or equivalent) and if the inherited buffer.length > newMax then shrink/clear it (slice to newMax or clear completely when newMax === 0), and ensure future inserts honor the newMax so the child client no longer inherits an oversized buffer.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/orm/src/client/client-impl.ts`:
- Around line 433-437: The diagnostics method $diagnostics currently returns
slowQueries via [...this.slowQueries] which only clones the array shell; change
it to return cloned QueryInfo objects so callers cannot mutate the shared
in-memory buffer. In the $diagnostics implementation (client-impl.ts, symbol
$diagnostics and this.slowQueries), map over this.slowQueries and return shallow
copies of each QueryInfo (e.g., by spreading each object) so fields like sql and
durationMs are copied into a new object before returning.
- Around line 130-145: The current guard in validateOptions skips
computed-fields validation for all derived clients and therefore misses cases
where a new options object (e.g., from $setOptions) changes computedFields or
flips skipValidationForComputedFields; update validateOptions (method on
ClientImpl) to run validateComputedFieldsConfig whenever there is no baseClient
OR when the new options object is different from the base client’s options
(i.e., options !== baseClient.options) and skipValidationForComputedFields is
false, so derived clients that receive a new options object get their computed
fields revalidated.
- Around line 136-145: The diagnostics schema currently rejects Infinity because
it uses z.int(); update the diagnosticsSchema so slowQueryMaxRecords accepts
either a nonnegative integer or Infinity by replacing
z.int().nonnegative().optional() with a validator that allows Infinity (for
example: z.number().nonnegative().optional().refine(n => n === undefined ||
Number.isInteger(n) || n === Infinity, "must be a nonnegative integer or
Infinity")), keeping the field optional and retaining the existing error
handling in the client-impl.ts validation logic.
---
Outside diff comments:
In `@packages/orm/src/client/client-impl.ts`:
- Around line 85-101: When inheriting slowQueries from baseClient you must trim
the shared buffer to respect a child client's smaller slowQueryMaxRecords (set
via $setOptions) so $diagnostics() cannot exceed the new cap; after assigning
this.slowQueries = baseClient.slowQueries, compute the new max
(this.options.slowQueryMaxRecords or equivalent) and if the inherited
buffer.length > newMax then shrink/clear it (slice to newMax or clear completely
when newMax === 0), and ensure future inserts honor the newMax so the child
client no longer inherits an oversized buffer.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bd5b2cf9-3614-4f2b-b9f9-4ffa1f169f7f
📒 Files selected for processing (7)
packages/orm/src/client/client-impl.tspackages/orm/src/client/contract.tspackages/orm/src/client/diagnostics.tspackages/orm/src/client/executor/zenstack-query-executor.tspackages/orm/src/client/options.tspackages/orm/src/client/zod/factory.tstests/e2e/orm/client-api/diagnostics.test.ts
e688cd2 to
24089de
Compare
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
z.int().nonnegative() rejects Infinity. Use .or(z.literal(Infinity)) to accept both nonnegative integers and Infinity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
There was a problem hiding this comment.
♻️ Duplicate comments (3)
packages/orm/src/client/client-impl.ts (3)
136-145:⚠️ Potential issue | 🟠 Major
slowQueryMaxRecordsshould acceptInfinityas documented.Line 139 currently rejects the documented “unlimited” value (
Infinity) forslowQueryMaxRecords.💡 Proposed fix
const diagnosticsSchema = z.object({ slowQueryThresholdMs: z.number().nonnegative().optional(), - slowQueryMaxRecords: z.int().nonnegative().optional(), + slowQueryMaxRecords: z.union([z.number().int().nonnegative(), z.literal(Infinity)]).optional(), });#!/bin/bash set -euo pipefail echo "Verify option contract documents Infinity:" rg -n -C2 'slowQueryMaxRecords|Infinity' packages/orm/src/client/options.ts echo echo "Verify diagnostics schema currently validates slowQueryMaxRecords:" rg -n -C3 'diagnosticsSchema|slowQueryMaxRecords|z\.int|z\.number\(\)\.int|z\.literal\(Infinity\)' packages/orm/src/client/client-impl.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/client-impl.ts` around lines 136 - 145, The diagnostics schema currently uses z.int().nonnegative().optional() for slowQueryMaxRecords which rejects the documented “unlimited” value Infinity; update diagnosticsSchema (the slowQueryMaxRecords entry) to accept either a nonnegative integer or the literal Infinity (e.g. a union of z.number().int().nonnegative() and z.literal(Infinity)) so safeParse(options.diagnostics) permits Infinity, and keep error handling via createConfigError/formatError unchanged.
433-437:⚠️ Potential issue | 🟡 Minor
$diagnostics()still exposes mutableQueryInforeferences.Line 436 clones only the array container, not the
QueryInfoentries, so callers can mutate internal state.💡 Proposed fix
async $diagnostics(): Promise<Diagnostics> { return { zodCache: this.inputValidator.zodFactory.cacheStats, - slowQueries: [...this.slowQueries], + slowQueries: this.slowQueries.map((query) => ({ ...query })), }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/client-impl.ts` around lines 433 - 437, $diagnostics currently returns a shallow copy of this.slowQueries which exposes internal QueryInfo objects to mutation; update the $diagnostics implementation to return cloned QueryInfo entries instead of the original references (e.g., map over this.slowQueries and create new objects for each entry, deep-cloning any nested mutable fields) so callers cannot mutate internal state—focus changes inside the async $diagnostics() method and operate on this.slowQueries / QueryInfo entries.
131-134:⚠️ Potential issue | 🟠 MajorRevalidate computed fields when derived clients get a new options object.
Line 131 restricts computed-fields validation to root construction only, so
$setOptions()on derived clients can bypass validation for changedcomputedFields.💡 Proposed fix
- if (!baseClient && !options.skipValidationForComputedFields) { - // validate computed fields configuration once for the root client + if (!options.skipValidationForComputedFields && (!baseClient || baseClient.$options !== options)) { + // validate on root construction and whenever a new options object is supplied this.validateComputedFieldsConfig(options); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/client-impl.ts` around lines 131 - 134, The current guard only runs validateComputedFieldsConfig during root construction (due to the baseClient && skipValidationForComputedFields check), which lets derived clients bypass validation when $setOptions() changes computedFields; update the logic so validateComputedFieldsConfig(options) is also invoked when $setOptions() is called on derived clients (unless options.skipValidationForComputedFields is true). Locate the client implementation where validateComputedFieldsConfig is defined and where $setOptions() and baseClient are used, and add a call to validateComputedFieldsConfig(options) in $setOptions() (or remove the baseClient-only restriction) to ensure any new/modified computedFields are revalidated.
🧹 Nitpick comments (1)
tests/e2e/orm/client-api/diagnostics.test.ts (1)
77-88: Strengthen the copy test to assert entry-level cloning too.This currently proves only array-shell copying; it won’t catch shared
QueryInfoobject references.💡 Proposed assertion enhancement
const diagnostics1 = await client.$diagnostics(); const diagnostics2 = await client.$diagnostics(); expect(diagnostics1.slowQueries).not.toBe(diagnostics2.slowQueries); expect(diagnostics1.slowQueries).toEqual(diagnostics2.slowQueries); + if (diagnostics1.slowQueries.length > 0) { + expect(diagnostics1.slowQueries[0]).not.toBe(diagnostics2.slowQueries[0]); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/diagnostics.test.ts` around lines 77 - 88, The test currently only checks that diagnostics.slowQueries arrays are different but not that each entry is cloned; update the test in diagnostics.test.ts (inside the it 'returns a copy of slow queries') to also assert that corresponding entries in diagnostics1.slowQueries and diagnostics2.slowQueries are not the same object references (e.g., for each index expect(diagnostics1.slowQueries[i]).not.toBe(diagnostics2.slowQueries[i])) while still asserting deep equality (expect(...).toEqual(...)); locate the creation via createTestClient and the client.$diagnostics() calls and add per-entry reference inequality checks to ensure QueryInfo objects are cloned rather than shared.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/orm/src/client/client-impl.ts`:
- Around line 136-145: The diagnostics schema currently uses
z.int().nonnegative().optional() for slowQueryMaxRecords which rejects the
documented “unlimited” value Infinity; update diagnosticsSchema (the
slowQueryMaxRecords entry) to accept either a nonnegative integer or the literal
Infinity (e.g. a union of z.number().int().nonnegative() and
z.literal(Infinity)) so safeParse(options.diagnostics) permits Infinity, and
keep error handling via createConfigError/formatError unchanged.
- Around line 433-437: $diagnostics currently returns a shallow copy of
this.slowQueries which exposes internal QueryInfo objects to mutation; update
the $diagnostics implementation to return cloned QueryInfo entries instead of
the original references (e.g., map over this.slowQueries and create new objects
for each entry, deep-cloning any nested mutable fields) so callers cannot mutate
internal state—focus changes inside the async $diagnostics() method and operate
on this.slowQueries / QueryInfo entries.
- Around line 131-134: The current guard only runs validateComputedFieldsConfig
during root construction (due to the baseClient &&
skipValidationForComputedFields check), which lets derived clients bypass
validation when $setOptions() changes computedFields; update the logic so
validateComputedFieldsConfig(options) is also invoked when $setOptions() is
called on derived clients (unless options.skipValidationForComputedFields is
true). Locate the client implementation where validateComputedFieldsConfig is
defined and where $setOptions() and baseClient are used, and add a call to
validateComputedFieldsConfig(options) in $setOptions() (or remove the
baseClient-only restriction) to ensure any new/modified computedFields are
revalidated.
---
Nitpick comments:
In `@tests/e2e/orm/client-api/diagnostics.test.ts`:
- Around line 77-88: The test currently only checks that diagnostics.slowQueries
arrays are different but not that each entry is cloned; update the test in
diagnostics.test.ts (inside the it 'returns a copy of slow queries') to also
assert that corresponding entries in diagnostics1.slowQueries and
diagnostics2.slowQueries are not the same object references (e.g., for each
index expect(diagnostics1.slowQueries[i]).not.toBe(diagnostics2.slowQueries[i]))
while still asserting deep equality (expect(...).toEqual(...)); locate the
creation via createTestClient and the client.$diagnostics() calls and add
per-entry reference inequality checks to ensure QueryInfo objects are cloned
rather than shared.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 755f157c-3512-49a6-ab33-0422c96a8ede
📒 Files selected for processing (2)
packages/orm/src/client/client-impl.tstests/e2e/orm/client-api/diagnostics.test.ts
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
There was a problem hiding this comment.
♻️ Duplicate comments (2)
packages/orm/src/client/client-impl.ts (1)
132-136:⚠️ Potential issue | 🟠 MajorRevalidate computed fields when derived clients receive a new options object.
At Line 133, validation is limited to root client construction (
!baseClient). A derived client created via$setOptions(...)can supply changedcomputedFieldsbut skip validation.💡 Proposed fix
- if (!baseClient && !options.skipValidationForComputedFields) { - // validate computed fields configuration once for the root client + if (!options.skipValidationForComputedFields && (!baseClient || baseClient.$options !== options)) { + // validate on root construction and whenever a new options object is supplied this.validateComputedFieldsConfig(options); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/client-impl.ts` around lines 132 - 136, The current validateOptions in ClientImpl only calls validateComputedFieldsConfig for root clients and skips revalidation for derived clients; change it so computed-fields are revalidated when a derived client receives a new options object (e.g., via $setOptions). Specifically, in validateOptions(baseClient, options) call this.validateComputedFieldsConfig(options) whenever options.skipValidationForComputedFields is false and (there is no baseClient OR the incoming options object differs from baseClient.options or options.computedFields differs from baseClient.options?.computedFields), ensuring derived clients with updated computedFields run validation; keep the skipValidationForComputedFields guard.tests/e2e/orm/client-api/diagnostics.test.ts (1)
169-187:⚠️ Potential issue | 🟡 MinorStrengthen this test to prove eviction keeps the slowest entries.
At Line 183, the assertions only verify cap and shape. An implementation that retains arbitrary entries could still pass. Please assert that retained durations are the top
Nobserved values (using deterministic timing/mocked clock).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/diagnostics.test.ts` around lines 169 - 187, Update the "keeps the slowest queries when limit is exceeded" test to assert eviction preserves the top N slowest durations: instrument each created query with a known/deterministic duration (e.g., mock or stub timing via fake timers or set explicit controlled waits) and collect those durations while creating the 5 users, then after calling client.$diagnostics() verify that diagnostics.slowQueries contains exactly the largest `maxRecords` durations from your observed set (ordered or as a set comparison), and still assert shape (sql and durationMs) — adjust the test surrounding client.$diagnostics(), slowQueryThresholdMs and slowQueryMaxRecords to use the mocked clock or deterministic waits so the comparison is stable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/orm/src/client/client-impl.ts`:
- Around line 132-136: The current validateOptions in ClientImpl only calls
validateComputedFieldsConfig for root clients and skips revalidation for derived
clients; change it so computed-fields are revalidated when a derived client
receives a new options object (e.g., via $setOptions). Specifically, in
validateOptions(baseClient, options) call
this.validateComputedFieldsConfig(options) whenever
options.skipValidationForComputedFields is false and (there is no baseClient OR
the incoming options object differs from baseClient.options or
options.computedFields differs from baseClient.options?.computedFields),
ensuring derived clients with updated computedFields run validation; keep the
skipValidationForComputedFields guard.
In `@tests/e2e/orm/client-api/diagnostics.test.ts`:
- Around line 169-187: Update the "keeps the slowest queries when limit is
exceeded" test to assert eviction preserves the top N slowest durations:
instrument each created query with a known/deterministic duration (e.g., mock or
stub timing via fake timers or set explicit controlled waits) and collect those
durations while creating the 5 users, then after calling client.$diagnostics()
verify that diagnostics.slowQueries contains exactly the largest `maxRecords`
durations from your observed set (ordered or as a set comparison), and still
assert shape (sql and durationMs) — adjust the test surrounding
client.$diagnostics(), slowQueryThresholdMs and slowQueryMaxRecords to use the
mocked clock or deterministic waits so the comparison is stable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1104c220-7cc7-484c-884f-179ee384a107
📒 Files selected for processing (5)
packages/orm/src/client/client-impl.tspackages/orm/src/client/contract.tspackages/orm/src/client/options.tspackages/orm/src/client/zod/factory.tstests/e2e/orm/client-api/diagnostics.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/orm/src/client/zod/factory.ts
|
@claude ,check why tests are failing? |
|
Claude finished @ymc9's task —— View job ✅ Test Failure Analysis CompleteTodo List
🔍 Root Cause AnalysisFailing Test: Error: Root Cause: Test isolation issue - the 🐛 The Problem
🔧 Recommended SolutionsOption 1: Fix Test Isolation (Recommended)Ensure each test gets a fresh client with empty slowQueries: it('accepts Infinity as slowQueryMaxRecords', async () => {
const client = await createTestClient(schema, {
diagnostics: {
slowQueryThresholdMs: 0,
slowQueryMaxRecords: Infinity,
},
});
// Clear any existing slow queries from shared state
const initialDiagnostics = await client.$diagnostics();
client.slowQueries.length = 0; // Reset array
try {
for (let i = 0; i < 5; i++) {
await client.user.create({ data: { email: `u${i}@test.com` } });
}
const diagnostics = await client.$diagnostics();
expect(diagnostics.slowQueries.length).toBe(5);
} finally {
await client.$disconnect();
}
});Option 2: Test the Actual BehaviorAccept that slowQueries may accumulate and test the increment instead: it('accepts Infinity as slowQueryMaxRecords', async () => {
const client = await createTestClient(schema, {
diagnostics: {
slowQueryThresholdMs: 0,
slowQueryMaxRecords: Infinity,
},
});
try {
const initialCount = (await client.$diagnostics()).slowQueries.length;
for (let i = 0; i < 5; i++) {
await client.user.create({ data: { email: `u${i}@test.com` } });
}
const diagnostics = await client.$diagnostics();
expect(diagnostics.slowQueries.length).toBe(initialCount + 5);
} finally {
await client.$disconnect();
}
});Option 3: Improve Database IsolationEnsure each test gets a unique database file by modifying the test utilities. 🏃♂️ Quick FixThe simplest immediate fix is Option 1 - just clear the slowQueries array at the start of the test to ensure test isolation. 📊 ImpactThis is a test environment issue, not a production bug. The diagnostics functionality itself works correctly - it's just the test that needs better isolation. |
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
tests/e2e/orm/client-api/diagnostics.test.ts (1)
169-187:⚠️ Potential issue | 🟡 MinorEviction test still does not prove “keep the slowest” behavior.
The assertions at Line 183-Line 187 only validate size/shape. An implementation that keeps any arbitrary
maxRecordsentries would still pass. Make timings deterministic and assert retained durations are the topNobserved durations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/diagnostics.test.ts` around lines 169 - 187, The test "keeps the slowest queries when limit is exceeded" only checks size/shape and can pass with arbitrary eviction; modify the test around createTestClient and client.$diagnostics() to produce deterministic, distinct query durations (e.g., by instrumenting timestamps or using a controllable clock or explicit delays when calling client.user.create) so you can capture the exact durations observed, then assert that diagnostics.slowQueries (from client.$diagnostics()) contains the top N durations sorted/selected by largest duration (not just length), i.e., collect the observed durations during the loop, compute the highest maxRecords durations, and assert the returned diagnostics.slowQueries durations match that expected set and order.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/orm/client-api/diagnostics.test.ts`:
- Around line 130-145: The test "limits the number of slow query records"
currently uses a weak assertion; replace the toBeLessThanOrEqual(maxRecords)
check on diagnostics.slowQueries.length with an exact equality assertion
(toBe(maxRecords)) so that after creating 10 users with slowQueryThresholdMs: 0
the slow query tracker has filled to the configured slowQueryMaxRecords; update
the assertion that references client.$diagnostics() and slowQueryMaxRecords
accordingly to catch under-recording regressions.
---
Duplicate comments:
In `@tests/e2e/orm/client-api/diagnostics.test.ts`:
- Around line 169-187: The test "keeps the slowest queries when limit is
exceeded" only checks size/shape and can pass with arbitrary eviction; modify
the test around createTestClient and client.$diagnostics() to produce
deterministic, distinct query durations (e.g., by instrumenting timestamps or
using a controllable clock or explicit delays when calling client.user.create)
so you can capture the exact durations observed, then assert that
diagnostics.slowQueries (from client.$diagnostics()) contains the top N
durations sorted/selected by largest duration (not just length), i.e., collect
the observed durations during the loop, compute the highest maxRecords
durations, and assert the returned diagnostics.slowQueries durations match that
expected set and order.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 76b4c431-ab65-40c5-ba56-cfd69b51a33a
📒 Files selected for processing (1)
tests/e2e/orm/client-api/diagnostics.test.ts
|
Claude finished @ymc9's task —— View job PR Review: Diagnostics Feature ImplementationTodo List
🎯 Overall AssessmentThis is a well-implemented diagnostics feature that adds valuable debugging capabilities to ZenStack. The implementation follows good architectural patterns and provides meaningful insights into both Zod schema caching and query performance. Summary: ✅ Ready to merge with minor fixes needed (see issues below) ✅ StrengthsArchitecture & Design
Implementation Quality
Configuration & Validation
|
There was a problem hiding this comment.
♻️ Duplicate comments (2)
tests/e2e/orm/client-api/diagnostics.test.ts (2)
131-146:⚠️ Potential issue | 🟡 MinorTighten cap assertion to detect under-recording.
At Line 145,
toBeLessThanOrEqual(maxRecords)is too weak for this workload (slowQueryThresholdMs: 0, 10 writes). Assert exact cap to catch regressions.Proposed fix
- expect(diagnostics.slowQueries.length).toBeLessThanOrEqual(maxRecords); + expect(diagnostics.slowQueries.length).toBe(maxRecords);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/diagnostics.test.ts` around lines 131 - 146, The test currently asserts diagnostics.slowQueries.length <= maxRecords which is too weak; change the assertion in the it block that uses createTestClient(...) and client.$diagnostics() to assert the exact cap by replacing expect(diagnostics.slowQueries.length).toBeLessThanOrEqual(maxRecords) with expect(diagnostics.slowQueries.length).toEqual(maxRecords) so the test fails if under-recording occurs.
170-189:⚠️ Potential issue | 🟡 MinorEviction-policy test does not prove “keeps the slowest”.
At Lines 183-189, the test only checks count/shape, so arbitrary retained records could still pass. Add deterministic duration control and assert retained durations are the top
maxRecordsvalues.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/diagnostics.test.ts` around lines 170 - 189, The test "keeps the slowest queries when limit is exceeded" currently only checks count/shape; change it to generate queries with deterministic, differing durations (e.g., issue synthetic queries that take known times or instrument a controllable delay) so you can record their durations, then call client.$diagnostics() and assert diagnostics.slowQueries contains exactly the top slowest durations (sorted or compare sets) limited to slowQueryMaxRecords; use the existing config keys slowQueryThresholdMs and slowQueryMaxRecords and the diagnostics.slowQueries array to locate and validate retained durations are the largest recorded values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@tests/e2e/orm/client-api/diagnostics.test.ts`:
- Around line 131-146: The test currently asserts diagnostics.slowQueries.length
<= maxRecords which is too weak; change the assertion in the it block that uses
createTestClient(...) and client.$diagnostics() to assert the exact cap by
replacing expect(diagnostics.slowQueries.length).toBeLessThanOrEqual(maxRecords)
with expect(diagnostics.slowQueries.length).toEqual(maxRecords) so the test
fails if under-recording occurs.
- Around line 170-189: The test "keeps the slowest queries when limit is
exceeded" currently only checks count/shape; change it to generate queries with
deterministic, differing durations (e.g., issue synthetic queries that take
known times or instrument a controllable delay) so you can record their
durations, then call client.$diagnostics() and assert diagnostics.slowQueries
contains exactly the top slowest durations (sorted or compare sets) limited to
slowQueryMaxRecords; use the existing config keys slowQueryThresholdMs and
slowQueryMaxRecords and the diagnostics.slowQueries array to locate and validate
retained durations are the largest recorded values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 30e8a5e0-2564-456e-8b4e-44969852aa9b
📒 Files selected for processing (3)
packages/orm/src/client/diagnostics.tspackages/orm/src/client/executor/zenstack-query-executor.tstests/e2e/orm/client-api/diagnostics.test.ts
Summary
$diagnostics()method toZenStackClientthat returns Zod schema cache statistics and slow query recordsdiagnosticsoption toClientOptionswithslowQueryThresholdMs(threshold to classify a query as slow) andslowQueryMaxRecords(max entries kept, default 100, evicts the fastest)$setAuth,$setOptions,$use,$unuse, transactions) via the same array referencediagnosticsconfig with Zod in the constructorTest plan
diagnosticsoption is not setslowQueryThresholdMs: 0)$diagnostics()returns a new array copy each call$setAuthderived clientsslowQueryMaxRecordslimits the number of records kept🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests