bunsane 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +445 -318
- package/config/cache.config.ts +35 -1
- package/core/App.ts +24 -1064
- package/core/ArcheType.ts +78 -2110
- package/core/BatchLoader.ts +56 -32
- package/core/Entity.ts +85 -1043
- package/core/EntityHookManager.ts +52 -754
- package/core/Logger.ts +10 -0
- package/core/RequestContext.ts +64 -6
- package/core/RequestLoaders.ts +187 -36
- package/core/SchedulerManager.ts +28 -600
- package/core/app/bootstrap.ts +133 -0
- package/core/app/cors.ts +85 -0
- package/core/app/graphqlSetup.ts +56 -0
- package/core/app/healthEndpoints.ts +31 -0
- package/core/app/metricsCollector.ts +27 -0
- package/core/app/preparedStatementWarmup.ts +15 -0
- package/core/app/processHandlers.ts +43 -0
- package/core/app/requestRouter.ts +310 -0
- package/core/app/restRegistry.ts +80 -0
- package/core/app/shutdown.ts +97 -0
- package/core/app/studioRouter.ts +83 -0
- package/core/archetype/customTypes.ts +100 -0
- package/core/archetype/decorators.ts +171 -0
- package/core/archetype/fieldResolvers.ts +666 -0
- package/core/archetype/helpers.ts +29 -0
- package/core/archetype/relationLoader.ts +161 -0
- package/core/archetype/schemaBuilder.ts +141 -0
- package/core/archetype/weaver.ts +218 -0
- package/core/archetype/zodSchemaBuilder.ts +527 -0
- package/core/cache/CacheManager.ts +173 -267
- package/core/cache/CompressionUtils.ts +34 -3
- package/core/cache/MemoryCache.ts +40 -37
- package/core/cache/RedisCache.ts +4 -4
- package/core/cache/health.ts +30 -0
- package/core/cache/invalidation.ts +96 -0
- package/core/cache/strategies/writeInvalidate.ts +111 -0
- package/core/cache/strategies/writeThrough.ts +233 -0
- package/core/components/BaseComponent.ts +16 -8
- package/core/components/ComponentRegistry.ts +28 -0
- package/core/decorators/IndexedField.ts +1 -1
- package/core/entity/cacheStrategies.ts +97 -0
- package/core/entity/componentAccess.ts +364 -0
- package/core/entity/finders.ts +202 -0
- package/core/entity/pendingOps.ts +72 -0
- package/core/entity/saveEntity.ts +377 -0
- package/core/hooks/dispatcher.ts +439 -0
- package/core/hooks/guards.ts +155 -0
- package/core/hooks/registry.ts +247 -0
- package/core/metadata/definitions/Component.ts +1 -1
- package/core/metadata/index.ts +15 -4
- package/core/middleware/AccessLog.ts +8 -1
- package/core/middleware/RateLimit.ts +102 -105
- package/core/middleware/RequestId.ts +2 -9
- package/core/middleware/SecurityHeaders.ts +2 -11
- package/core/middleware/headers.ts +28 -0
- package/core/remote/OutboxWorker.ts +213 -183
- package/core/remote/RemoteManager.ts +401 -400
- package/core/remote/types.ts +153 -151
- package/core/requestScope.ts +34 -0
- package/core/scheduler/cronEvaluator.ts +174 -0
- package/core/scheduler/lifecycleHooks.ts +21 -0
- package/core/scheduler/lockCoordinator.ts +27 -0
- package/core/scheduler/metrics.ts +14 -0
- package/core/scheduler/taskRunner.ts +420 -0
- package/database/DatabaseHelper.ts +128 -101
- package/database/IndexingStrategy.ts +72 -2
- package/database/PreparedStatementCache.ts +20 -5
- package/database/cancellable.ts +35 -0
- package/database/index.ts +15 -3
- package/database/instrumentedDb.ts +141 -0
- package/endpoints/archetypes.ts +2 -8
- package/endpoints/tables.ts +6 -1
- package/gql/index.ts +1 -1
- package/gql/visitors/ResolverGeneratorVisitor.ts +25 -4
- package/package.json +22 -1
- package/query/CTENode.ts +5 -3
- package/query/ComponentInclusionNode.ts +240 -13
- package/query/OrNode.ts +6 -5
- package/query/Query.ts +203 -59
- package/query/QueryContext.ts +6 -0
- package/query/QueryDAG.ts +7 -2
- package/query/membershipSource.ts +66 -0
- package/storage/LocalStorageProvider.ts +8 -3
- package/studio/dist/assets/index-BMZ67Npg.js +254 -0
- package/studio/dist/assets/index-BpbuYz9g.css +1 -0
- package/studio/{index.html → dist/index.html} +3 -2
- package/swagger/generator.ts +11 -1
- package/upload/UploadManager.ts +8 -6
- package/utils/uuid.ts +40 -10
- package/.claude/settings.local.json +0 -47
- package/.prettierrc +0 -4
- package/.serena/memories/architectural-decision-no-dependency-injection.md +0 -76
- package/.serena/memories/architecture.md +0 -154
- package/.serena/memories/cache-interface-refactoring-2026-01-24.md +0 -165
- package/.serena/memories/code_style_and_conventions.md +0 -76
- package/.serena/memories/project_overview.md +0 -43
- package/.serena/memories/schema-dsl-plan.md +0 -107
- package/.serena/memories/suggested_commands.md +0 -80
- package/.serena/memories/typescript-compilation-status.md +0 -54
- package/.serena/project.yml +0 -114
- package/BunSane.jpg +0 -0
- package/CLAUDE.md +0 -198
- package/TODO.md +0 -2
- package/bun.lock +0 -302
- package/bunfig.toml +0 -10
- package/docs/SCALABILITY_PLAN.md +0 -175
- package/studio/bun.lock +0 -482
- package/studio/package.json +0 -39
- package/studio/postcss.config.js +0 -6
- package/studio/src/components/DataTable.tsx +0 -211
- package/studio/src/components/Layout.tsx +0 -13
- package/studio/src/components/PageContainer.tsx +0 -9
- package/studio/src/components/PageHeader.tsx +0 -13
- package/studio/src/components/SearchBar.tsx +0 -57
- package/studio/src/components/Sidebar.tsx +0 -294
- package/studio/src/components/ui/button.tsx +0 -56
- package/studio/src/components/ui/checkbox.tsx +0 -26
- package/studio/src/components/ui/input.tsx +0 -25
- package/studio/src/hooks/useDataTable.ts +0 -131
- package/studio/src/index.css +0 -36
- package/studio/src/lib/api.ts +0 -186
- package/studio/src/lib/utils.ts +0 -13
- package/studio/src/main.tsx +0 -17
- package/studio/src/pages/ArcheType.tsx +0 -239
- package/studio/src/pages/Components.tsx +0 -124
- package/studio/src/pages/EntityInspector.tsx +0 -302
- package/studio/src/pages/QueryRunner.tsx +0 -246
- package/studio/src/pages/Table.tsx +0 -94
- package/studio/src/pages/Welcome.tsx +0 -241
- package/studio/src/routes.tsx +0 -45
- package/studio/src/store/archeTypeSettings.ts +0 -30
- package/studio/src/store/studio.ts +0 -65
- package/studio/src/utils/columnHelpers.tsx +0 -114
- package/studio/studio-instructions.md +0 -81
- package/studio/tailwind.config.js +0 -77
- package/studio/utils.ts +0 -54
- package/studio/vite.config.js +0 -19
- package/tests/benchmark/BENCHMARK_DATABASES_PLAN.md +0 -338
- package/tests/benchmark/bunfig.toml +0 -9
- package/tests/benchmark/fixtures/EcommerceComponents.ts +0 -283
- package/tests/benchmark/fixtures/EcommerceDataGenerators.ts +0 -301
- package/tests/benchmark/fixtures/RelationTracker.ts +0 -159
- package/tests/benchmark/fixtures/index.ts +0 -6
- package/tests/benchmark/index.ts +0 -22
- package/tests/benchmark/noop-preload.ts +0 -3
- package/tests/benchmark/query-lateral-benchmark.test.ts +0 -372
- package/tests/benchmark/runners/BenchmarkLoader.ts +0 -132
- package/tests/benchmark/runners/index.ts +0 -4
- package/tests/benchmark/scenarios/query-benchmarks.test.ts +0 -465
- package/tests/benchmark/scripts/generate-db.ts +0 -344
- package/tests/benchmark/scripts/run-benchmarks.ts +0 -97
- package/tests/e2e/http.test.ts +0 -130
- package/tests/fixtures/archetypes/TestUserArchetype.ts +0 -21
- package/tests/fixtures/components/TestOrder.ts +0 -23
- package/tests/fixtures/components/TestProduct.ts +0 -23
- package/tests/fixtures/components/TestUser.ts +0 -20
- package/tests/fixtures/components/index.ts +0 -6
- package/tests/graphql/SchemaGeneration.test.ts +0 -90
- package/tests/graphql/builders/ResolverBuilder.test.ts +0 -223
- package/tests/graphql/builders/TypeDefBuilder.test.ts +0 -153
- package/tests/helpers/MockRedisClient.ts +0 -113
- package/tests/helpers/MockRedisStreamServer.ts +0 -448
- package/tests/integration/archetype/ArcheType.persistence.test.ts +0 -241
- package/tests/integration/cache/CacheInvalidation.test.ts +0 -259
- package/tests/integration/entity/Entity.persistence.test.ts +0 -333
- package/tests/integration/entity/Entity.saveTimeout.test.ts +0 -110
- package/tests/integration/query/Query.complexAnalysis.test.ts +0 -557
- package/tests/integration/query/Query.edgeCases.test.ts +0 -595
- package/tests/integration/query/Query.exec.test.ts +0 -576
- package/tests/integration/query/Query.explainAnalyze.test.ts +0 -233
- package/tests/integration/query/Query.jsonbArray.test.ts +0 -214
- package/tests/integration/remote/dlq.test.ts +0 -175
- package/tests/integration/remote/event-dispatch.test.ts +0 -114
- package/tests/integration/remote/outbox.test.ts +0 -130
- package/tests/integration/remote/rpc.test.ts +0 -177
- package/tests/pglite-setup.ts +0 -62
- package/tests/setup.ts +0 -164
- package/tests/stress/BenchmarkRunner.ts +0 -203
- package/tests/stress/DataSeeder.ts +0 -190
- package/tests/stress/StressTestReporter.ts +0 -229
- package/tests/stress/cursor-perf-test.ts +0 -171
- package/tests/stress/fixtures/RealisticComponents.ts +0 -235
- package/tests/stress/fixtures/StressTestComponents.ts +0 -58
- package/tests/stress/index.ts +0 -7
- package/tests/stress/scenarios/query-benchmarks.test.ts +0 -285
- package/tests/stress/scenarios/realistic-scenarios.test.ts +0 -1081
- package/tests/stress/scenarios/timeout-investigation.test.ts +0 -522
- package/tests/unit/BatchLoader.test.ts +0 -196
- package/tests/unit/archetype/ArcheType.test.ts +0 -107
- package/tests/unit/cache/CacheManager.test.ts +0 -367
- package/tests/unit/cache/MemoryCache.test.ts +0 -260
- package/tests/unit/cache/RedisCache.test.ts +0 -411
- package/tests/unit/entity/Entity.components.test.ts +0 -317
- package/tests/unit/entity/Entity.drainSideEffects.test.ts +0 -51
- package/tests/unit/entity/Entity.reload.test.ts +0 -63
- package/tests/unit/entity/Entity.requireComponents.test.ts +0 -72
- package/tests/unit/entity/Entity.test.ts +0 -345
- package/tests/unit/gql/depthLimit.test.ts +0 -203
- package/tests/unit/gql/operationMiddleware.test.ts +0 -293
- package/tests/unit/health/Health.test.ts +0 -129
- package/tests/unit/middleware/AccessLog.test.ts +0 -37
- package/tests/unit/middleware/Middleware.test.ts +0 -98
- package/tests/unit/middleware/RequestId.test.ts +0 -54
- package/tests/unit/middleware/SecurityHeaders.test.ts +0 -66
- package/tests/unit/query/FilterBuilder.test.ts +0 -111
- package/tests/unit/query/JsonbArrayBuilder.test.ts +0 -178
- package/tests/unit/query/Query.emptyString.test.ts +0 -69
- package/tests/unit/query/Query.test.ts +0 -310
- package/tests/unit/remote/CircuitBreaker.test.ts +0 -159
- package/tests/unit/remote/RemoteError.test.ts +0 -55
- package/tests/unit/remote/decorators.test.ts +0 -195
- package/tests/unit/remote/metrics.test.ts +0 -115
- package/tests/unit/remote/mockRedisStreamServer.test.ts +0 -104
- package/tests/unit/scheduler/DistributedLock.test.ts +0 -274
- package/tests/unit/scheduler/SchedulerManager.timeBased.test.ts +0 -95
- package/tests/unit/schema/schema-integration.test.ts +0 -426
- package/tests/unit/schema/schema.test.ts +0 -580
- package/tests/unit/storage/S3StorageProvider.test.ts +0 -567
- package/tests/unit/upload/RestUpload.test.ts +0 -267
- package/tests/unit/validateEnv.test.ts +0 -82
- package/tests/utils/entity-tracker.ts +0 -57
- package/tests/utils/index.ts +0 -13
- package/tests/utils/test-context.ts +0 -149
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stress Tests - Query Performance Benchmarks
|
|
3
|
-
*
|
|
4
|
-
* Tests query performance with configurable data volumes
|
|
5
|
-
* Default: 10,000 records (smoke test)
|
|
6
|
-
* Set STRESS_RECORD_COUNT env var for larger tests
|
|
7
|
-
*/
|
|
8
|
-
import { describe, test, beforeAll, afterAll, expect } from 'bun:test';
|
|
9
|
-
import { DataSeeder } from '../DataSeeder';
|
|
10
|
-
import { BenchmarkRunner } from '../BenchmarkRunner';
|
|
11
|
-
import { StressTestReporter } from '../StressTestReporter';
|
|
12
|
-
import { Query, FilterOp } from '../../../query/Query';
|
|
13
|
-
import { StressUser, StressProfile } from '../fixtures/StressTestComponents';
|
|
14
|
-
import { ensureComponentsRegistered } from '../../utils';
|
|
15
|
-
|
|
16
|
-
// Configurable via environment variable
|
|
17
|
-
const RECORD_COUNT = parseInt(process.env.STRESS_RECORD_COUNT || '10000', 10);
|
|
18
|
-
const BATCH_SIZE = Math.min(5000, Math.floor(RECORD_COUNT / 10) || 1000);
|
|
19
|
-
|
|
20
|
-
describe('Stress Tests - Query Performance', () => {
|
|
21
|
-
const seeder = new DataSeeder();
|
|
22
|
-
const benchmark = new BenchmarkRunner();
|
|
23
|
-
const reporter = new StressTestReporter();
|
|
24
|
-
let entityIds: string[] = [];
|
|
25
|
-
let setupTime = 0;
|
|
26
|
-
|
|
27
|
-
beforeAll(async () => {
|
|
28
|
-
const startSetup = performance.now();
|
|
29
|
-
|
|
30
|
-
// Ensure components are registered
|
|
31
|
-
await ensureComponentsRegistered(StressUser, StressProfile);
|
|
32
|
-
|
|
33
|
-
// Wait for index creation to settle (prevents deadlocks from concurrent index creation)
|
|
34
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
35
|
-
|
|
36
|
-
console.log(`\n Seeding ${RECORD_COUNT.toLocaleString()} records...`);
|
|
37
|
-
|
|
38
|
-
const result = await seeder.seed(
|
|
39
|
-
StressUser,
|
|
40
|
-
(i) => ({
|
|
41
|
-
name: `User ${i}`,
|
|
42
|
-
email: `user${i}@stress.test`,
|
|
43
|
-
age: 18 + (i % 62),
|
|
44
|
-
status: ['active', 'inactive', 'pending', 'banned'][i % 4],
|
|
45
|
-
score: Math.random() * 1000,
|
|
46
|
-
createdAt: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000)
|
|
47
|
-
}),
|
|
48
|
-
{
|
|
49
|
-
totalEntities: RECORD_COUNT,
|
|
50
|
-
batchSize: BATCH_SIZE,
|
|
51
|
-
onProgress: (current, total, elapsed) => {
|
|
52
|
-
if (current % (BATCH_SIZE * 2) === 0 || current === total) {
|
|
53
|
-
const pct = ((current / total) * 100).toFixed(1);
|
|
54
|
-
const rate = ((current / elapsed) * 1000).toFixed(0);
|
|
55
|
-
console.log(` Progress: ${pct}% (${rate} records/sec)`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
entityIds = result.entityIds;
|
|
62
|
-
console.log(` Seeded in ${(result.totalTime / 1000).toFixed(1)}s (${result.recordsPerSecond.toFixed(0)} records/sec)`);
|
|
63
|
-
|
|
64
|
-
// Add profile components to 50% of entities
|
|
65
|
-
if (RECORD_COUNT >= 1000) {
|
|
66
|
-
console.log(' Adding profile components to 50% of entities...');
|
|
67
|
-
await seeder.seedAdditionalComponent(
|
|
68
|
-
entityIds.slice(0, Math.floor(entityIds.length / 2)),
|
|
69
|
-
StressProfile,
|
|
70
|
-
(i) => ({
|
|
71
|
-
bio: `This is bio ${i}`,
|
|
72
|
-
avatarUrl: `https://example.com/avatar/${i}.png`,
|
|
73
|
-
verified: i % 3 === 0
|
|
74
|
-
}),
|
|
75
|
-
BATCH_SIZE
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
console.log(' Running VACUUM ANALYZE...');
|
|
80
|
-
await seeder.optimize();
|
|
81
|
-
|
|
82
|
-
setupTime = performance.now() - startSetup;
|
|
83
|
-
console.log(` Setup complete in ${(setupTime / 1000).toFixed(1)}s\n`);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
afterAll(async () => {
|
|
87
|
-
// Print report
|
|
88
|
-
const recordCount = await seeder.getRecordCount();
|
|
89
|
-
const report = reporter.generateReport(benchmark.getResults(), {
|
|
90
|
-
recordCount,
|
|
91
|
-
environment: `PostgreSQL, Bun ${Bun.version}`,
|
|
92
|
-
duration: setupTime
|
|
93
|
-
});
|
|
94
|
-
console.log('\n' + report);
|
|
95
|
-
|
|
96
|
-
// Cleanup seeded data
|
|
97
|
-
console.log('\n Cleaning up test data...');
|
|
98
|
-
await seeder.cleanup(entityIds, BATCH_SIZE);
|
|
99
|
-
console.log(' Cleanup complete.');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('indexed equality filter (status = active)', async () => {
|
|
103
|
-
const result = await benchmark.runWithOutput(
|
|
104
|
-
'Filter: status = active',
|
|
105
|
-
() => new Query()
|
|
106
|
-
.with(StressUser, { filters: [{ field: 'status', operator: FilterOp.EQ, value: 'active' }] })
|
|
107
|
-
.take(100)
|
|
108
|
-
.exec(),
|
|
109
|
-
{ targetP95: 50, iterations: 15 }
|
|
110
|
-
);
|
|
111
|
-
expect(result.passed).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('indexed range filter (age 25-35)', async () => {
|
|
115
|
-
const result = await benchmark.runWithOutput(
|
|
116
|
-
'Filter: age 25-35',
|
|
117
|
-
() => new Query()
|
|
118
|
-
.with(StressUser, {
|
|
119
|
-
filters: [
|
|
120
|
-
{ field: 'age', operator: FilterOp.GTE, value: 25 },
|
|
121
|
-
{ field: 'age', operator: FilterOp.LTE, value: 35 }
|
|
122
|
-
]
|
|
123
|
-
})
|
|
124
|
-
.take(100)
|
|
125
|
-
.exec(),
|
|
126
|
-
{ targetP95: 75, iterations: 15 }
|
|
127
|
-
);
|
|
128
|
-
expect(result.passed).toBe(true);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('count query', async () => {
|
|
132
|
-
const result = await benchmark.runWithOutput(
|
|
133
|
-
'COUNT all',
|
|
134
|
-
async () => [await new Query().with(StressUser).count()],
|
|
135
|
-
{ targetP95: 100, iterations: 15 }
|
|
136
|
-
);
|
|
137
|
-
expect(result.passed).toBe(true);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('pagination - shallow offset (1000)', async () => {
|
|
141
|
-
const result = await benchmark.runWithOutput(
|
|
142
|
-
'Offset 1000',
|
|
143
|
-
() => new Query()
|
|
144
|
-
.with(StressUser)
|
|
145
|
-
.sortBy(StressUser, 'name', 'ASC')
|
|
146
|
-
.take(100)
|
|
147
|
-
.offset(1000)
|
|
148
|
-
.exec(),
|
|
149
|
-
{ targetP95: 75, iterations: 15 }
|
|
150
|
-
);
|
|
151
|
-
expect(result.passed).toBe(true);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('pagination - deep offset (50% of records)', async () => {
|
|
155
|
-
const deepOffset = Math.floor(RECORD_COUNT / 2);
|
|
156
|
-
const result = await benchmark.runWithOutput(
|
|
157
|
-
`Offset ${deepOffset.toLocaleString()}`,
|
|
158
|
-
() => new Query()
|
|
159
|
-
.with(StressUser)
|
|
160
|
-
.sortBy(StressUser, 'name', 'ASC')
|
|
161
|
-
.take(100)
|
|
162
|
-
.offset(deepOffset)
|
|
163
|
-
.exec(),
|
|
164
|
-
{ targetP95: 500, iterations: 10 }
|
|
165
|
-
);
|
|
166
|
-
// Deep pagination is expected to be slower, use a more lenient target
|
|
167
|
-
expect(result.timings.p95).toBeLessThan(2000);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('cursor pagination - first page', async () => {
|
|
171
|
-
const result = await benchmark.runWithOutput(
|
|
172
|
-
'Cursor: first page',
|
|
173
|
-
() => new Query()
|
|
174
|
-
.with(StressUser)
|
|
175
|
-
.take(100)
|
|
176
|
-
.exec(),
|
|
177
|
-
{ targetP95: 50, iterations: 15 }
|
|
178
|
-
);
|
|
179
|
-
expect(result.passed).toBe(true);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('cursor pagination - from middle (using cursor)', async () => {
|
|
183
|
-
// First, get an entity ID from the middle of the dataset
|
|
184
|
-
const midpointResults = await new Query()
|
|
185
|
-
.with(StressUser)
|
|
186
|
-
.take(1)
|
|
187
|
-
.offset(Math.floor(RECORD_COUNT / 2))
|
|
188
|
-
.exec();
|
|
189
|
-
|
|
190
|
-
const cursorId = midpointResults[0]?.id;
|
|
191
|
-
expect(cursorId).toBeDefined();
|
|
192
|
-
|
|
193
|
-
const result = await benchmark.runWithOutput(
|
|
194
|
-
'Cursor: from middle (O(1))',
|
|
195
|
-
() => new Query()
|
|
196
|
-
.with(StressUser)
|
|
197
|
-
.cursor(cursorId!)
|
|
198
|
-
.take(100)
|
|
199
|
-
.exec(),
|
|
200
|
-
{ targetP95: 50, iterations: 15 }
|
|
201
|
-
);
|
|
202
|
-
// Cursor pagination should be fast regardless of position
|
|
203
|
-
expect(result.passed).toBe(true);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
test('cursor pagination - near end (using cursor)', async () => {
|
|
207
|
-
// Get an entity ID from near the end (90%)
|
|
208
|
-
const nearEndResults = await new Query()
|
|
209
|
-
.with(StressUser)
|
|
210
|
-
.take(1)
|
|
211
|
-
.offset(Math.floor(RECORD_COUNT * 0.9))
|
|
212
|
-
.exec();
|
|
213
|
-
|
|
214
|
-
const cursorId = nearEndResults[0]?.id;
|
|
215
|
-
expect(cursorId).toBeDefined();
|
|
216
|
-
|
|
217
|
-
const result = await benchmark.runWithOutput(
|
|
218
|
-
'Cursor: near end (O(1))',
|
|
219
|
-
() => new Query()
|
|
220
|
-
.with(StressUser)
|
|
221
|
-
.cursor(cursorId!)
|
|
222
|
-
.take(100)
|
|
223
|
-
.exec(),
|
|
224
|
-
{ targetP95: 50, iterations: 15 }
|
|
225
|
-
);
|
|
226
|
-
// Cursor pagination should be fast regardless of position
|
|
227
|
-
expect(result.passed).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
test('multi-component join (User + Profile)', async () => {
|
|
231
|
-
const result = await benchmark.runWithOutput(
|
|
232
|
-
'Join: User + Profile',
|
|
233
|
-
() => new Query()
|
|
234
|
-
.with(StressUser)
|
|
235
|
-
.with(StressProfile)
|
|
236
|
-
.take(100)
|
|
237
|
-
.exec(),
|
|
238
|
-
{ targetP95: 150, iterations: 15 }
|
|
239
|
-
);
|
|
240
|
-
expect(result.passed).toBe(true);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test('combined filters and sort', async () => {
|
|
244
|
-
const result = await benchmark.runWithOutput(
|
|
245
|
-
'Filter + Sort + Limit',
|
|
246
|
-
() => new Query()
|
|
247
|
-
.with(StressUser, {
|
|
248
|
-
filters: [
|
|
249
|
-
{ field: 'status', operator: FilterOp.EQ, value: 'active' },
|
|
250
|
-
{ field: 'age', operator: FilterOp.GTE, value: 21 }
|
|
251
|
-
]
|
|
252
|
-
})
|
|
253
|
-
.sortBy(StressUser, 'score', 'DESC')
|
|
254
|
-
.take(50)
|
|
255
|
-
.exec(),
|
|
256
|
-
{ targetP95: 100, iterations: 15 }
|
|
257
|
-
);
|
|
258
|
-
expect(result.passed).toBe(true);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test('simple query without filters', async () => {
|
|
262
|
-
const result = await benchmark.runWithOutput(
|
|
263
|
-
'Simple: take 100',
|
|
264
|
-
() => new Query()
|
|
265
|
-
.with(StressUser)
|
|
266
|
-
.take(100)
|
|
267
|
-
.exec(),
|
|
268
|
-
{ targetP95: 50, iterations: 15 }
|
|
269
|
-
);
|
|
270
|
-
expect(result.passed).toBe(true);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test('query with populate', async () => {
|
|
274
|
-
const result = await benchmark.runWithOutput(
|
|
275
|
-
'Populated: take 50',
|
|
276
|
-
() => new Query()
|
|
277
|
-
.with(StressUser)
|
|
278
|
-
.populate()
|
|
279
|
-
.take(50)
|
|
280
|
-
.exec(),
|
|
281
|
-
{ targetP95: 100, iterations: 15 }
|
|
282
|
-
);
|
|
283
|
-
expect(result.passed).toBe(true);
|
|
284
|
-
});
|
|
285
|
-
});
|