@aztec/prover-client 0.65.2 → 0.67.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.
Files changed (98) hide show
  1. package/dest/block_builder/index.d.ts +6 -0
  2. package/dest/block_builder/index.d.ts.map +1 -0
  3. package/dest/block_builder/index.js +2 -0
  4. package/dest/block_builder/light.d.ts +32 -0
  5. package/dest/block_builder/light.d.ts.map +1 -0
  6. package/dest/block_builder/light.js +75 -0
  7. package/dest/index.d.ts +1 -2
  8. package/dest/index.d.ts.map +1 -1
  9. package/dest/index.js +2 -3
  10. package/dest/mocks/fixtures.d.ts +4 -5
  11. package/dest/mocks/fixtures.d.ts.map +1 -1
  12. package/dest/mocks/fixtures.js +4 -8
  13. package/dest/mocks/test_context.d.ts +30 -12
  14. package/dest/mocks/test_context.d.ts.map +1 -1
  15. package/dest/mocks/test_context.js +61 -24
  16. package/dest/orchestrator/block-building-helpers.d.ts +5 -5
  17. package/dest/orchestrator/block-building-helpers.d.ts.map +1 -1
  18. package/dest/orchestrator/block-building-helpers.js +10 -11
  19. package/dest/orchestrator/epoch-proving-state.d.ts +5 -6
  20. package/dest/orchestrator/epoch-proving-state.d.ts.map +1 -1
  21. package/dest/orchestrator/epoch-proving-state.js +10 -12
  22. package/dest/orchestrator/orchestrator.d.ts +8 -6
  23. package/dest/orchestrator/orchestrator.d.ts.map +1 -1
  24. package/dest/orchestrator/orchestrator.js +85 -74
  25. package/dest/orchestrator/orchestrator_metrics.d.ts.map +1 -1
  26. package/dest/orchestrator/orchestrator_metrics.js +2 -5
  27. package/dest/orchestrator/tx-proving-state.d.ts +0 -1
  28. package/dest/orchestrator/tx-proving-state.d.ts.map +1 -1
  29. package/dest/orchestrator/tx-proving-state.js +2 -34
  30. package/dest/prover-agent/memory-proving-queue.d.ts.map +1 -1
  31. package/dest/prover-agent/memory-proving-queue.js +5 -4
  32. package/dest/prover-agent/prover-agent.d.ts.map +1 -1
  33. package/dest/prover-agent/prover-agent.js +3 -3
  34. package/dest/prover-client/factory.d.ts +6 -0
  35. package/dest/prover-client/factory.d.ts.map +1 -0
  36. package/dest/prover-client/factory.js +6 -0
  37. package/dest/prover-client/index.d.ts +3 -0
  38. package/dest/prover-client/index.d.ts.map +1 -0
  39. package/dest/prover-client/index.js +3 -0
  40. package/dest/{tx-prover/tx-prover.d.ts → prover-client/prover-client.d.ts} +8 -11
  41. package/dest/prover-client/prover-client.d.ts.map +1 -0
  42. package/dest/prover-client/prover-client.js +107 -0
  43. package/dest/proving_broker/caching_broker_facade.d.ts +12 -12
  44. package/dest/proving_broker/caching_broker_facade.d.ts.map +1 -1
  45. package/dest/proving_broker/caching_broker_facade.js +32 -29
  46. package/dest/proving_broker/factory.d.ts +2 -1
  47. package/dest/proving_broker/factory.d.ts.map +1 -1
  48. package/dest/proving_broker/factory.js +4 -4
  49. package/dest/proving_broker/proving_agent.d.ts +5 -0
  50. package/dest/proving_broker/proving_agent.d.ts.map +1 -1
  51. package/dest/proving_broker/proving_agent.js +15 -4
  52. package/dest/proving_broker/proving_agent_instrumentation.d.ts +8 -0
  53. package/dest/proving_broker/proving_agent_instrumentation.d.ts.map +1 -0
  54. package/dest/proving_broker/proving_agent_instrumentation.js +16 -0
  55. package/dest/proving_broker/proving_broker.d.ts +29 -5
  56. package/dest/proving_broker/proving_broker.d.ts.map +1 -1
  57. package/dest/proving_broker/proving_broker.js +142 -41
  58. package/dest/proving_broker/proving_broker_database/persisted.d.ts +3 -1
  59. package/dest/proving_broker/proving_broker_database/persisted.d.ts.map +1 -1
  60. package/dest/proving_broker/proving_broker_database/persisted.js +6 -2
  61. package/dest/proving_broker/proving_broker_instrumentation.d.ts +25 -0
  62. package/dest/proving_broker/proving_broker_instrumentation.d.ts.map +1 -0
  63. package/dest/proving_broker/proving_broker_instrumentation.js +85 -0
  64. package/dest/proving_broker/rpc.d.ts.map +1 -1
  65. package/dest/proving_broker/rpc.js +3 -2
  66. package/dest/test/mock_prover.d.ts +3 -2
  67. package/dest/test/mock_prover.d.ts.map +1 -1
  68. package/dest/test/mock_prover.js +9 -5
  69. package/package.json +18 -13
  70. package/src/block_builder/index.ts +6 -0
  71. package/src/block_builder/light.ts +120 -0
  72. package/src/index.ts +1 -2
  73. package/src/mocks/fixtures.ts +6 -18
  74. package/src/mocks/test_context.ts +85 -29
  75. package/src/orchestrator/block-building-helpers.ts +13 -14
  76. package/src/orchestrator/epoch-proving-state.ts +10 -13
  77. package/src/orchestrator/orchestrator.ts +101 -81
  78. package/src/orchestrator/orchestrator_metrics.ts +1 -11
  79. package/src/orchestrator/tx-proving-state.ts +1 -56
  80. package/src/prover-agent/memory-proving-queue.ts +4 -3
  81. package/src/prover-agent/prover-agent.ts +2 -2
  82. package/src/{tx-prover → prover-client}/factory.ts +4 -3
  83. package/src/prover-client/index.ts +2 -0
  84. package/src/{tx-prover/tx-prover.ts → prover-client/prover-client.ts} +25 -15
  85. package/src/proving_broker/caching_broker_facade.ts +31 -15
  86. package/src/proving_broker/factory.ts +7 -3
  87. package/src/proving_broker/proving_agent.ts +18 -3
  88. package/src/proving_broker/proving_agent_instrumentation.ts +21 -0
  89. package/src/proving_broker/proving_broker.ts +182 -50
  90. package/src/proving_broker/proving_broker_database/persisted.ts +11 -2
  91. package/src/proving_broker/proving_broker_instrumentation.ts +123 -0
  92. package/src/proving_broker/rpc.ts +2 -1
  93. package/src/test/mock_prover.ts +8 -4
  94. package/dest/tx-prover/factory.d.ts +0 -6
  95. package/dest/tx-prover/factory.d.ts.map +0 -1
  96. package/dest/tx-prover/factory.js +0 -6
  97. package/dest/tx-prover/tx-prover.d.ts.map +0 -1
  98. package/dest/tx-prover/tx-prover.js +0 -110
@@ -9,13 +9,17 @@ import {
9
9
  type ProvingJobStatus,
10
10
  ProvingRequestType,
11
11
  } from '@aztec/circuit-types';
12
- import { createDebugLogger } from '@aztec/foundation/log';
12
+ import { asyncPool } from '@aztec/foundation/async-pool';
13
+ import { createLogger } from '@aztec/foundation/log';
13
14
  import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise';
14
15
  import { PriorityMemoryQueue } from '@aztec/foundation/queue';
16
+ import { Timer } from '@aztec/foundation/timer';
17
+ import { type TelemetryClient } from '@aztec/telemetry-client';
15
18
 
16
19
  import assert from 'assert';
17
20
 
18
21
  import { type ProvingBrokerDatabase } from './proving_broker_database.js';
22
+ import { type MonitorCallback, ProvingBrokerInstrumentation } from './proving_broker_instrumentation.js';
19
23
 
20
24
  type InProgressMetadata = {
21
25
  id: ProvingJobId;
@@ -27,29 +31,33 @@ type ProofRequestBrokerConfig = {
27
31
  timeoutIntervalMs?: number;
28
32
  jobTimeoutMs?: number;
29
33
  maxRetries?: number;
34
+ maxEpochsToKeepResultsFor?: number;
35
+ maxParallelCleanUps?: number;
30
36
  };
31
37
 
38
+ type EnqueuedProvingJob = Pick<ProvingJob, 'id' | 'epochNumber'>;
39
+
32
40
  /**
33
41
  * A broker that manages proof requests and distributes them to workers based on their priority.
34
42
  * It takes a backend that is responsible for storing and retrieving proof requests and results.
35
43
  */
36
44
  export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
37
45
  private queues: ProvingQueues = {
38
- [ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
39
- [ProvingRequestType.TUBE_PROOF]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
40
- [ProvingRequestType.PRIVATE_KERNEL_EMPTY]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
46
+ [ProvingRequestType.PUBLIC_VM]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
47
+ [ProvingRequestType.TUBE_PROOF]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
48
+ [ProvingRequestType.PRIVATE_KERNEL_EMPTY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
41
49
 
42
- [ProvingRequestType.PRIVATE_BASE_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
43
- [ProvingRequestType.PUBLIC_BASE_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
44
- [ProvingRequestType.MERGE_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
45
- [ProvingRequestType.ROOT_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
50
+ [ProvingRequestType.PRIVATE_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
51
+ [ProvingRequestType.PUBLIC_BASE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
52
+ [ProvingRequestType.MERGE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
53
+ [ProvingRequestType.ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
46
54
 
47
- [ProvingRequestType.BLOCK_MERGE_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
48
- [ProvingRequestType.BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
49
- [ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
55
+ [ProvingRequestType.BLOCK_MERGE_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
56
+ [ProvingRequestType.BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
57
+ [ProvingRequestType.EMPTY_BLOCK_ROOT_ROLLUP]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
50
58
 
51
- [ProvingRequestType.BASE_PARITY]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
52
- [ProvingRequestType.ROOT_PARITY]: new PriorityMemoryQueue<ProvingJob>(provingJobComparator),
59
+ [ProvingRequestType.BASE_PARITY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
60
+ [ProvingRequestType.ROOT_PARITY]: new PriorityMemoryQueue<EnqueuedProvingJob>(provingJobComparator),
53
61
  };
54
62
 
55
63
  // holds a copy of the database in memory in order to quickly fulfill requests
@@ -58,6 +66,9 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
58
66
  // as above, but for results
59
67
  private resultsCache = new Map<ProvingJobId, ProvingJobSettledResult>();
60
68
 
69
+ // tracks when each job was enqueued
70
+ private enqueuedAt = new Map<ProvingJobId, Timer>();
71
+
61
72
  // keeps track of which jobs are currently being processed
62
73
  // in the event of a crash this information is lost, but that's ok
63
74
  // the next time the broker starts it will recreate jobsCache and still
@@ -70,23 +81,65 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
70
81
  // a map of promises that will be resolved when a job is settled
71
82
  private promises = new Map<ProvingJobId, PromiseWithResolvers<ProvingJobSettledResult>>();
72
83
 
73
- private timeoutPromise: RunningPromise;
74
- private timeSource = () => Math.floor(Date.now() / 1000);
84
+ private cleanupPromise: RunningPromise;
85
+ private msTimeSource = () => Date.now();
75
86
  private jobTimeoutMs: number;
76
87
  private maxRetries: number;
77
88
 
89
+ private instrumentation: ProvingBrokerInstrumentation;
90
+
91
+ private maxParallelCleanUps: number;
92
+
93
+ /**
94
+ * The broker keeps track of the highest epoch its seen.
95
+ * This information is used for garbage collection: once it reaches the next epoch, it can start pruning the database of old state.
96
+ * This clean up pass is only done against _settled_ jobs. This pass will not cancel jobs that are in-progress or in-queue.
97
+ * It is a client responsibility to cancel jobs if they are no longer necessary.
98
+ * Example:
99
+ * proving epoch 11 - the broker will wipe all setlled jobs for epochs 9 and lower
100
+ * finished proving epoch 11 and got first job for epoch 12 -> the broker will wipe all setlled jobs for epochs 10 and lower
101
+ * reorged back to end of epoch 10 -> epoch 11 is skipped and epoch 12 starts -> the broker will wipe all setlled jobs for epochs 10 and lower
102
+ */
103
+ private epochHeight = 0;
104
+ private maxEpochsToKeepResultsFor = 1;
105
+
78
106
  public constructor(
79
107
  private database: ProvingBrokerDatabase,
80
- { jobTimeoutMs = 30, timeoutIntervalMs = 10, maxRetries = 3 }: ProofRequestBrokerConfig = {},
81
- private logger = createDebugLogger('aztec:prover-client:proving-broker'),
108
+ client: TelemetryClient,
109
+ {
110
+ jobTimeoutMs = 30_000,
111
+ timeoutIntervalMs = 10_000,
112
+ maxRetries = 3,
113
+ maxEpochsToKeepResultsFor = 1,
114
+ maxParallelCleanUps = 20,
115
+ }: ProofRequestBrokerConfig = {},
116
+ private logger = createLogger('prover-client:proving-broker'),
82
117
  ) {
83
- this.timeoutPromise = new RunningPromise(this.timeoutCheck, timeoutIntervalMs);
118
+ this.instrumentation = new ProvingBrokerInstrumentation(client);
119
+ this.cleanupPromise = new RunningPromise(this.cleanupPass, timeoutIntervalMs);
84
120
  this.jobTimeoutMs = jobTimeoutMs;
85
121
  this.maxRetries = maxRetries;
122
+ this.maxEpochsToKeepResultsFor = maxEpochsToKeepResultsFor;
123
+ this.maxParallelCleanUps = maxParallelCleanUps;
86
124
  }
87
125
 
88
- // eslint-disable-next-line require-await
89
- public async start(): Promise<void> {
126
+ private measureQueueDepth: MonitorCallback = (type: ProvingRequestType) => {
127
+ return this.queues[type].length();
128
+ };
129
+
130
+ private countActiveJobs: MonitorCallback = (type: ProvingRequestType) => {
131
+ let count = 0;
132
+ for (const { id } of this.inProgress.values()) {
133
+ const job = this.jobsCache.get(id);
134
+ if (job?.type === type) {
135
+ count++;
136
+ }
137
+ }
138
+
139
+ return count;
140
+ };
141
+
142
+ public start(): Promise<void> {
90
143
  for (const [item, result] of this.database.allProvingJobs()) {
91
144
  this.logger.info(`Restoring proving job id=${item.id} settled=${!!result}`);
92
145
 
@@ -102,11 +155,16 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
102
155
  }
103
156
  }
104
157
 
105
- this.timeoutPromise.start();
158
+ this.cleanupPromise.start();
159
+
160
+ this.instrumentation.monitorQueueDepth(this.measureQueueDepth);
161
+ this.instrumentation.monitorActiveJobs(this.countActiveJobs);
162
+
163
+ return Promise.resolve();
106
164
  }
107
165
 
108
- public stop(): Promise<void> {
109
- return this.timeoutPromise.stop();
166
+ public async stop(): Promise<void> {
167
+ await this.cleanupPromise.stop();
110
168
  }
111
169
 
112
170
  public async enqueueProvingJob(job: ProvingJob): Promise<void> {
@@ -129,15 +187,22 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
129
187
  return promiseWithResolvers.promise;
130
188
  }
131
189
 
132
- public async removeAndCancelProvingJob(id: ProvingJobId): Promise<void> {
133
- this.logger.info(`Cancelling job id=${id}`);
134
- await this.database.deleteProvingJobAndResult(id);
135
-
190
+ public async cancelProvingJob(id: ProvingJobId): Promise<void> {
136
191
  // notify listeners of the cancellation
137
192
  if (!this.resultsCache.has(id)) {
138
- this.promises.get(id)?.resolve({ status: 'rejected', reason: 'Aborted' });
193
+ this.logger.info(`Cancelling job id=${id}`);
194
+ await this.reportProvingJobError(id, 'Aborted', false);
195
+ }
196
+ }
197
+
198
+ public async cleanUpProvingJobState(id: ProvingJobId): Promise<void> {
199
+ if (!this.resultsCache.has(id)) {
200
+ this.logger.warn(`Can't cleanup busy proving job: id=${id}`);
201
+ return;
139
202
  }
140
203
 
204
+ this.logger.debug(`Cleaning up state for job id=${id}`);
205
+ await this.database.deleteProvingJobAndResult(id);
141
206
  this.jobsCache.delete(id);
142
207
  this.promises.delete(id);
143
208
  this.resultsCache.delete(id);
@@ -174,19 +239,24 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
174
239
 
175
240
  for (const proofType of allowedProofs) {
176
241
  const queue = this.queues[proofType];
177
- let job: ProvingJob | undefined;
242
+ let enqueuedJob: EnqueuedProvingJob | undefined;
178
243
  // exhaust the queue and make sure we're not sending a job that's already in progress
179
244
  // or has already been completed
180
245
  // this can happen if the broker crashes and restarts
181
246
  // it's possible agents will report progress or results for jobs that are in the queue (after the restart)
182
- while ((job = queue.getImmediate())) {
183
- if (!this.inProgress.has(job.id) && !this.resultsCache.has(job.id)) {
184
- const time = this.timeSource();
247
+ while ((enqueuedJob = queue.getImmediate())) {
248
+ const job = this.jobsCache.get(enqueuedJob.id);
249
+ if (job && !this.inProgress.has(enqueuedJob.id) && !this.resultsCache.has(enqueuedJob.id)) {
250
+ const time = this.msTimeSource();
185
251
  this.inProgress.set(job.id, {
186
252
  id: job.id,
187
253
  startedAt: time,
188
254
  lastUpdatedAt: time,
189
255
  });
256
+ const enqueuedAt = this.enqueuedAt.get(job.id);
257
+ if (enqueuedAt) {
258
+ this.instrumentation.recordJobWait(job.type, enqueuedAt);
259
+ }
190
260
 
191
261
  return { job, time };
192
262
  }
@@ -212,15 +282,23 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
212
282
  this.inProgress.delete(id);
213
283
  }
214
284
 
215
- if (retry && retries + 1 < this.maxRetries) {
285
+ if (this.resultsCache.has(id)) {
286
+ this.logger.warn(`Proving job id=${id} already is already settled, ignoring error`);
287
+ return;
288
+ }
289
+
290
+ if (retry && retries + 1 < this.maxRetries && !this.isJobStale(item)) {
216
291
  this.logger.info(`Retrying proving job id=${id} type=${ProvingRequestType[item.type]} retry=${retries + 1}`);
217
292
  this.retries.set(id, retries + 1);
218
293
  this.enqueueJobInternal(item);
294
+ this.instrumentation.incRetriedJobs(item.type);
219
295
  return;
220
296
  }
221
297
 
222
- this.logger.debug(
223
- `Marking proving job id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${retries + 1} as failed`,
298
+ this.logger.warn(
299
+ `Marking proving job as failed id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${
300
+ retries + 1
301
+ } err=${err}`,
224
302
  );
225
303
 
226
304
  await this.database.setProvingJobError(id, err);
@@ -228,6 +306,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
228
306
  const result: ProvingJobSettledResult = { status: 'rejected', reason: String(err) };
229
307
  this.resultsCache.set(id, result);
230
308
  this.promises.get(id)!.resolve(result);
309
+ this.instrumentation.incRejectedJobs(item.type);
310
+ if (info) {
311
+ const duration = this.msTimeSource() - info.startedAt;
312
+ this.instrumentation.recordJobDuration(item.type, duration);
313
+ }
231
314
  }
232
315
 
233
316
  reportProvingJobProgress(
@@ -241,8 +324,13 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
241
324
  return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
242
325
  }
243
326
 
327
+ if (this.resultsCache.has(id)) {
328
+ this.logger.warn(`Proving job id=${id} has already been completed`);
329
+ return filter ? this.getProvingJob(filter) : Promise.resolve(undefined);
330
+ }
331
+
244
332
  const metadata = this.inProgress.get(id);
245
- const now = this.timeSource();
333
+ const now = this.msTimeSource();
246
334
  if (!metadata) {
247
335
  this.logger.warn(
248
336
  `Proving job id=${id} type=${ProvingRequestType[job.type]} not found in the in-progress cache, adding it`,
@@ -253,7 +341,7 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
253
341
  this.inProgress.set(id, {
254
342
  id,
255
343
  startedAt,
256
- lastUpdatedAt: this.timeSource(),
344
+ lastUpdatedAt: this.msTimeSource(),
257
345
  });
258
346
  return Promise.resolve(undefined);
259
347
  } else if (startedAt <= metadata.startedAt) {
@@ -294,6 +382,11 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
294
382
  this.inProgress.delete(id);
295
383
  }
296
384
 
385
+ if (this.resultsCache.has(id)) {
386
+ this.logger.warn(`Proving job id=${id} already settled, ignoring result`);
387
+ return;
388
+ }
389
+
297
390
  this.logger.debug(
298
391
  `Proving job complete id=${id} type=${ProvingRequestType[item.type]} totalAttempts=${retries + 1}`,
299
392
  );
@@ -303,9 +396,34 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
303
396
  const result: ProvingJobSettledResult = { status: 'fulfilled', value };
304
397
  this.resultsCache.set(id, result);
305
398
  this.promises.get(id)!.resolve(result);
399
+ this.instrumentation.incResolvedJobs(item.type);
306
400
  }
307
401
 
308
- private timeoutCheck = () => {
402
+ private cleanupPass = async () => {
403
+ await this.cleanupStaleJobs();
404
+ await this.reEnqueueExpiredJobs();
405
+ };
406
+
407
+ private async cleanupStaleJobs() {
408
+ const jobIds = Array.from(this.jobsCache.keys());
409
+ const jobsToClean: ProvingJobId[] = [];
410
+ for (const id of jobIds) {
411
+ const job = this.jobsCache.get(id)!;
412
+ const isComplete = this.resultsCache.has(id);
413
+ if (isComplete && this.isJobStale(job)) {
414
+ jobsToClean.push(id);
415
+ }
416
+ }
417
+
418
+ if (jobsToClean.length > 0) {
419
+ this.logger.info(`Cleaning up [${jobsToClean.join(',')}]`);
420
+ await asyncPool(this.maxParallelCleanUps, jobsToClean, async jobId => {
421
+ await this.cleanUpProvingJobState(jobId);
422
+ });
423
+ }
424
+ }
425
+
426
+ private async reEnqueueExpiredJobs() {
309
427
  const inProgressEntries = Array.from(this.inProgress.entries());
310
428
  for (const [id, metadata] of inProgressEntries) {
311
429
  const item = this.jobsCache.get(id);
@@ -315,26 +433,42 @@ export class ProvingBroker implements ProvingJobProducer, ProvingJobConsumer {
315
433
  continue;
316
434
  }
317
435
 
318
- const msSinceLastUpdate = (this.timeSource() - metadata.lastUpdatedAt) * 1000;
436
+ const now = this.msTimeSource();
437
+ const msSinceLastUpdate = now - metadata.lastUpdatedAt;
319
438
  if (msSinceLastUpdate >= this.jobTimeoutMs) {
320
- this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`);
321
- this.inProgress.delete(id);
322
- this.enqueueJobInternal(item);
439
+ if (this.isJobStale(item)) {
440
+ // the job has timed out and it's also old, just cancel and move on
441
+ await this.cancelProvingJob(item.id);
442
+ } else {
443
+ this.logger.warn(`Proving job id=${id} timed out. Adding it back to the queue.`);
444
+ this.inProgress.delete(id);
445
+ this.enqueueJobInternal(item);
446
+ this.instrumentation.incTimedOutJobs(item.type);
447
+ }
323
448
  }
324
449
  }
325
- };
450
+ }
326
451
 
327
452
  private enqueueJobInternal(job: ProvingJob): void {
328
453
  if (!this.promises.has(job.id)) {
329
454
  this.promises.set(job.id, promiseWithResolvers());
330
455
  }
331
- this.queues[job.type].put(job);
456
+ this.queues[job.type].put({
457
+ epochNumber: job.epochNumber,
458
+ id: job.id,
459
+ });
460
+ this.enqueuedAt.set(job.id, new Timer());
461
+ this.epochHeight = Math.max(this.epochHeight, job.epochNumber);
332
462
  this.logger.debug(`Enqueued new proving job id=${job.id}`);
333
463
  }
464
+
465
+ private isJobStale(job: ProvingJob) {
466
+ return job.epochNumber < this.epochHeight - this.maxEpochsToKeepResultsFor;
467
+ }
334
468
  }
335
469
 
336
470
  type ProvingQueues = {
337
- [K in ProvingRequestType]: PriorityMemoryQueue<ProvingJob>;
471
+ [K in ProvingRequestType]: PriorityMemoryQueue<EnqueuedProvingJob>;
338
472
  };
339
473
 
340
474
  /**
@@ -343,12 +477,10 @@ type ProvingQueues = {
343
477
  * @param b - Another proving job
344
478
  * @returns A number indicating the relative priority of the two proving jobs
345
479
  */
346
- function provingJobComparator(a: ProvingJob, b: ProvingJob): -1 | 0 | 1 {
347
- const aBlockNumber = a.blockNumber ?? 0;
348
- const bBlockNumber = b.blockNumber ?? 0;
349
- if (aBlockNumber < bBlockNumber) {
480
+ function provingJobComparator(a: EnqueuedProvingJob, b: EnqueuedProvingJob): -1 | 0 | 1 {
481
+ if (a.epochNumber < b.epochNumber) {
350
482
  return -1;
351
- } else if (aBlockNumber > bBlockNumber) {
483
+ } else if (a.epochNumber > b.epochNumber) {
352
484
  return 1;
353
485
  } else {
354
486
  return 0;
@@ -1,14 +1,23 @@
1
1
  import { type ProofUri, ProvingJob, type ProvingJobId, ProvingJobSettledResult } from '@aztec/circuit-types';
2
2
  import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc';
3
3
  import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
4
+ import { Attributes, LmdbMetrics, type TelemetryClient } from '@aztec/telemetry-client';
4
5
 
5
6
  import { type ProvingBrokerDatabase } from '../proving_broker_database.js';
6
7
 
7
8
  export class KVBrokerDatabase implements ProvingBrokerDatabase {
8
9
  private jobs: AztecMap<ProvingJobId, string>;
9
10
  private jobResults: AztecMap<ProvingJobId, string>;
10
-
11
- constructor(private store: AztecKVStore) {
11
+ private metrics: LmdbMetrics;
12
+
13
+ constructor(private store: AztecKVStore, client: TelemetryClient) {
14
+ this.metrics = new LmdbMetrics(
15
+ client.getMeter('KVBrokerDatabase'),
16
+ {
17
+ [Attributes.DB_DATA_TYPE]: 'prover-broker',
18
+ },
19
+ () => store.estimateSize(),
20
+ );
12
21
  this.jobs = store.openMap('proving_jobs');
13
22
  this.jobResults = store.openMap('proving_job_results');
14
23
  }
@@ -0,0 +1,123 @@
1
+ import { ProvingRequestType } from '@aztec/circuit-types';
2
+ import { type Timer } from '@aztec/foundation/timer';
3
+ import {
4
+ Attributes,
5
+ type Histogram,
6
+ Metrics,
7
+ type ObservableGauge,
8
+ type ObservableResult,
9
+ type TelemetryClient,
10
+ type UpDownCounter,
11
+ ValueType,
12
+ } from '@aztec/telemetry-client';
13
+
14
+ export type MonitorCallback = (proofType: ProvingRequestType) => number;
15
+
16
+ export class ProvingBrokerInstrumentation {
17
+ private queueSize: ObservableGauge;
18
+ private activeJobs: ObservableGauge;
19
+ private resolvedJobs: UpDownCounter;
20
+ private rejectedJobs: UpDownCounter;
21
+ private timedOutJobs: UpDownCounter;
22
+ private jobWait: Histogram;
23
+ private jobDuration: Histogram;
24
+ private retriedJobs: UpDownCounter;
25
+
26
+ constructor(client: TelemetryClient, name = 'ProvingBroker') {
27
+ const meter = client.getMeter(name);
28
+
29
+ this.queueSize = meter.createObservableGauge(Metrics.PROVING_QUEUE_SIZE, {
30
+ valueType: ValueType.INT,
31
+ });
32
+
33
+ this.activeJobs = meter.createObservableGauge(Metrics.PROVING_QUEUE_ACTIVE_JOBS, {
34
+ valueType: ValueType.INT,
35
+ });
36
+
37
+ this.resolvedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_RESOLVED_JOBS, {
38
+ valueType: ValueType.INT,
39
+ });
40
+
41
+ this.rejectedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_REJECTED_JOBS, {
42
+ valueType: ValueType.INT,
43
+ });
44
+
45
+ this.retriedJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_RETRIED_JOBS, {
46
+ valueType: ValueType.INT,
47
+ });
48
+
49
+ this.timedOutJobs = meter.createUpDownCounter(Metrics.PROVING_QUEUE_TIMED_OUT_JOBS, {
50
+ valueType: ValueType.INT,
51
+ });
52
+
53
+ this.jobWait = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_WAIT, {
54
+ description: 'Records how long a job sits in the queue',
55
+ unit: 'ms',
56
+ valueType: ValueType.INT,
57
+ });
58
+
59
+ this.jobDuration = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_DURATION, {
60
+ description: 'Records how long a job takes to complete',
61
+ unit: 'ms',
62
+ valueType: ValueType.INT,
63
+ });
64
+ }
65
+
66
+ monitorQueueDepth(fn: MonitorCallback) {
67
+ this.queueSize.addCallback(obs => this.observe(obs, fn));
68
+ }
69
+
70
+ monitorActiveJobs(fn: MonitorCallback) {
71
+ this.activeJobs.addCallback(obs => this.observe(obs, fn));
72
+ }
73
+
74
+ incResolvedJobs(proofType: ProvingRequestType) {
75
+ this.resolvedJobs.add(1, {
76
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
77
+ });
78
+ }
79
+
80
+ incRejectedJobs(proofType: ProvingRequestType) {
81
+ this.rejectedJobs.add(1, {
82
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
83
+ });
84
+ }
85
+
86
+ incRetriedJobs(proofType: ProvingRequestType) {
87
+ this.retriedJobs.add(1, {
88
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
89
+ });
90
+ }
91
+
92
+ incTimedOutJobs(proofType: ProvingRequestType) {
93
+ this.timedOutJobs.add(1, {
94
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
95
+ });
96
+ }
97
+
98
+ recordJobWait(proofType: ProvingRequestType, msOrTimer: Timer | number) {
99
+ const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
100
+ this.jobWait.record(duration, {
101
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
102
+ });
103
+ }
104
+
105
+ recordJobDuration(proofType: ProvingRequestType, msOrTimer: Timer | number) {
106
+ const duration = typeof msOrTimer === 'number' ? msOrTimer : Math.floor(msOrTimer.ms());
107
+ this.jobDuration.record(duration, {
108
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
109
+ });
110
+ }
111
+
112
+ private observe(obs: ObservableResult, fn: MonitorCallback) {
113
+ for (const proofType of Object.values(ProvingRequestType)) {
114
+ // a type predicate for TypeScript to recognize that we're only iterating over enum values
115
+ if (typeof proofType !== 'number') {
116
+ continue;
117
+ }
118
+ obs.observe(fn(proofType), {
119
+ [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[proofType],
120
+ });
121
+ }
122
+ }
123
+ }
@@ -28,7 +28,8 @@ const GetProvingJobResponse = z.object({
28
28
  export const ProvingJobProducerSchema: ApiSchemaFor<ProvingJobProducer> = {
29
29
  enqueueProvingJob: z.function().args(ProvingJob).returns(z.void()),
30
30
  getProvingJobStatus: z.function().args(ProvingJobId).returns(ProvingJobStatus),
31
- removeAndCancelProvingJob: z.function().args(ProvingJobId).returns(z.void()),
31
+ cleanUpProvingJobState: z.function().args(ProvingJobId).returns(z.void()),
32
+ cancelProvingJob: z.function().args(ProvingJobId).returns(z.void()),
32
33
  waitForJobToSettle: z.function().args(ProvingJobId).returns(ProvingJobSettledResult),
33
34
  };
34
35
 
@@ -43,6 +43,7 @@ import {
43
43
  makeRootRollupPublicInputs,
44
44
  } from '@aztec/circuits.js/testing';
45
45
  import { times } from '@aztec/foundation/collection';
46
+ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
46
47
 
47
48
  import { InlineProofStore, type ProofStore } from '../proving_broker/proof_store.js';
48
49
  import { ProvingAgent } from '../proving_broker/proving_agent.js';
@@ -50,7 +51,7 @@ import { ProvingBroker } from '../proving_broker/proving_broker.js';
50
51
  import { InMemoryBrokerDatabase } from '../proving_broker/proving_broker_database/memory.js';
51
52
 
52
53
  export class TestBroker implements ProvingJobProducer {
53
- private broker = new ProvingBroker(new InMemoryBrokerDatabase());
54
+ private broker = new ProvingBroker(new InMemoryBrokerDatabase(), new NoopTelemetryClient());
54
55
  private agents: ProvingAgent[];
55
56
 
56
57
  constructor(
@@ -58,7 +59,7 @@ export class TestBroker implements ProvingJobProducer {
58
59
  prover: ServerCircuitProver,
59
60
  private proofStore: ProofStore = new InlineProofStore(),
60
61
  ) {
61
- this.agents = times(agentCount, () => new ProvingAgent(this.broker, proofStore, prover));
62
+ this.agents = times(agentCount, () => new ProvingAgent(this.broker, proofStore, prover, new NoopTelemetryClient()));
62
63
  }
63
64
 
64
65
  public async start() {
@@ -81,8 +82,11 @@ export class TestBroker implements ProvingJobProducer {
81
82
  getProvingJobStatus(id: ProvingJobId): Promise<ProvingJobStatus> {
82
83
  return this.broker.getProvingJobStatus(id);
83
84
  }
84
- removeAndCancelProvingJob(id: ProvingJobId): Promise<void> {
85
- return this.broker.removeAndCancelProvingJob(id);
85
+ cleanUpProvingJobState(id: ProvingJobId): Promise<void> {
86
+ return this.broker.cleanUpProvingJobState(id);
87
+ }
88
+ cancelProvingJob(id: string): Promise<void> {
89
+ return this.broker.cancelProvingJob(id);
86
90
  }
87
91
  waitForJobToSettle(id: ProvingJobId): Promise<ProvingJobSettledResult> {
88
92
  return this.broker.waitForJobToSettle(id);
@@ -1,6 +0,0 @@
1
- import { type ProvingJobBroker } from '@aztec/circuit-types';
2
- import { type TelemetryClient } from '@aztec/telemetry-client';
3
- import { type ProverClientConfig } from '../config.js';
4
- import { TxProver } from './tx-prover.js';
5
- export declare function createProverClient(config: ProverClientConfig, broker: ProvingJobBroker, telemetry?: TelemetryClient): Promise<TxProver>;
6
- //# sourceMappingURL=factory.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/tx-prover/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/D,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,gBAAgB,EACxB,SAAS,GAAE,eAA2C,qBAGvD"}
@@ -1,6 +0,0 @@
1
- import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
2
- import { TxProver } from './tx-prover.js';
3
- export function createProverClient(config, broker, telemetry = new NoopTelemetryClient()) {
4
- return TxProver.new(config, broker, telemetry);
5
- }
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90eC1wcm92ZXIvZmFjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUduRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUMsTUFBTSxVQUFVLGtCQUFrQixDQUNoQyxNQUEwQixFQUMxQixNQUF3QixFQUN4QixZQUE2QixJQUFJLG1CQUFtQixFQUFFO0lBRXRELE9BQU8sUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQ2pELENBQUMifQ==
@@ -1 +0,0 @@
1
- {"version":3,"file":"tx-prover.d.ts","sourceRoot":"","sources":["../../src/tx-prover/tx-prover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA2C,MAAM,kBAAkB,CAAC;AAC3G,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EAEvB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAIxC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAOvD;;;GAGG;AACH,qBAAa,QAAS,YAAW,kBAAkB;IAO/C,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,GAAG;IAVb,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAsB;IAEpC,OAAO,CAAC,QAAQ,CAAC,CAAS;IAE1B,OAAO;IAYA,iBAAiB,CAAC,EAAE,EAAE,yBAAyB,EAAE,KAAK,GAAE,WAAuC,GAAG,WAAW;IAS7G,WAAW,IAAI,EAAE;IAIlB,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IASnC;;OAEG;IACU,IAAI;IAQjB;;;;;;OAMG;WACiB,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe;IAMjG,mBAAmB,IAAI,kBAAkB;YAQlC,oBAAoB;YAmBpB,UAAU;CAGzB;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GAAG,UAAU,GAAG,QAAQ,EAClD,SAAS,EAAE,eAAe,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAU9B"}