@@ -41,6 +41,11 @@ export type KafkaRecordPosition = {
4141 offset : bigint ;
4242} ;
4343
44+ export type DurableTrackOutcomeRecord = {
45+ position : KafkaRecordPosition ;
46+ outcome : TrackOutcome ;
47+ } ;
48+
4449export type DurableTrackOutcomeApplyResult =
4550 | {
4651 kind : "applied" | "duplicate" ;
@@ -174,98 +179,121 @@ export class SqliteBalanceStateStore {
174179 applyDurableTrackOutcome ( {
175180 position,
176181 outcome,
182+ } : DurableTrackOutcomeRecord ) : DurableTrackOutcomeApplyResult {
183+ const [ result ] = this . applyDurableTrackOutcomes ( {
184+ records : [ { position, outcome } ] ,
185+ } ) ;
186+ if ( ! result ) throw new Error ( "Expected a durable track outcome result" ) ;
187+ return result ;
188+ }
189+
190+ applyDurableTrackOutcomes ( {
191+ records,
177192 } : {
178- position : KafkaRecordPosition ;
179- outcome : TrackOutcome ;
180- } ) : DurableTrackOutcomeApplyResult {
181- assertTopic ( { topic : position . topic } ) ;
182- assertPartition ( { partition : position . partition } ) ;
183- assertOffset ( { offset : position . offset } ) ;
184- const parsedOutcome = parseTrackOutcome ( { input : outcome } ) ;
193+ records : readonly DurableTrackOutcomeRecord [ ] ;
194+ } ) : DurableTrackOutcomeApplyResult [ ] {
195+ const parsedRecords = records . map ( ( { position, outcome } ) => {
196+ assertTopic ( { topic : position . topic } ) ;
197+ assertPartition ( { partition : position . partition } ) ;
198+ assertOffset ( { offset : position . offset } ) ;
199+ return {
200+ position,
201+ outcome : parseTrackOutcome ( { input : outcome } ) ,
202+ } ;
203+ } ) ;
204+ if ( parsedRecords . length === 0 ) return [ ] ;
185205
186206 return this . database
187- . transaction ( ( ) => {
188- const expectedOffset = readNextOffset ( {
189- database : this . database ,
190- topic : position . topic ,
191- partition : position . partition ,
192- } ) ;
193- if ( expectedOffset === null ) {
194- throw new PartitionProgressNotFoundError ( {
195- topic : position . topic ,
196- partition : position . partition ,
197- } ) ;
198- }
199- if ( position . offset < expectedOffset ) {
200- return {
201- kind : "position_already_applied" ,
202- nextOffset : expectedOffset ,
203- } as const ;
204- }
205- const partitionKey = meteringPartitionKeyOf ( {
206- identity : parsedOutcome . identity ,
207- } ) ;
208- const state = readState ( {
209- database : this . database ,
210- identity : parsedOutcome . identity ,
211- } ) ;
212- if ( ! state ) throw new MeteringStateNotFoundError ( { partitionKey } ) ;
207+ . transaction ( ( ) =>
208+ parsedRecords . map ( ( { position, outcome } ) =>
209+ this . applyParsedDurableTrackOutcome ( { position, outcome } ) ,
210+ ) ,
211+ )
212+ . immediate ( ) ;
213+ }
213214
214- const existingReceipt = readTrackReceipt ( {
215- database : this . database ,
216- identity : parsedOutcome . identity ,
217- commandId : parsedOutcome . commandId ,
218- } ) ;
219- const executed = executeEngineTrack ( {
220- state,
221- outcome : parsedOutcome ,
222- existingReceipt,
223- } ) ;
224- const nextOffset = position . offset + 1n ;
215+ private applyParsedDurableTrackOutcome ( {
216+ position,
217+ outcome,
218+ } : DurableTrackOutcomeRecord ) : DurableTrackOutcomeApplyResult {
219+ const expectedOffset = readNextOffset ( {
220+ database : this . database ,
221+ topic : position . topic ,
222+ partition : position . partition ,
223+ } ) ;
224+ if ( expectedOffset === null ) {
225+ throw new PartitionProgressNotFoundError ( {
226+ topic : position . topic ,
227+ partition : position . partition ,
228+ } ) ;
229+ }
230+ if ( position . offset < expectedOffset ) {
231+ return {
232+ kind : "position_already_applied" ,
233+ nextOffset : expectedOffset ,
234+ } ;
235+ }
225236
226- if ( executed . kind === "applied" ) {
227- const stateUpdate = updateState ( {
228- database : this . database ,
229- partitionKey,
230- revisionBefore : parsedOutcome . revisionBefore ,
231- state : executed . state ,
232- } ) ;
233- if ( stateUpdate . changes !== 1 ) {
234- throw new CorruptBalanceStateError ( { partitionKey } ) ;
235- }
237+ const partitionKey = meteringPartitionKeyOf ( { identity : outcome . identity } ) ;
238+ const state = readState ( {
239+ database : this . database ,
240+ identity : outcome . identity ,
241+ } ) ;
242+ if ( ! state ) throw new MeteringStateNotFoundError ( { partitionKey } ) ;
236243
237- insertTrackReceipt ( {
238- database : this . database ,
239- partitionKey,
240- position,
241- receipt : executed . receipt ,
242- } ) ;
243- }
244+ const existingReceipt = readTrackReceipt ( {
245+ database : this . database ,
246+ identity : outcome . identity ,
247+ commandId : outcome . commandId ,
248+ } ) ;
249+ const executed = executeEngineTrack ( {
250+ state,
251+ outcome,
252+ existingReceipt,
253+ } ) ;
254+ const nextOffset = position . offset + 1n ;
244255
245- const progressUpdate = advancePartitionProgress ( {
246- database : this . database ,
247- topic : position . topic ,
248- partition : position . partition ,
249- expectedOffset,
250- nextOffset,
251- } ) ;
252- if ( progressUpdate . changes !== 1 ) {
253- throw new UnexpectedKafkaOffsetError ( {
254- topic : position . topic ,
255- partition : position . partition ,
256- expectedOffset,
257- receivedOffset : position . offset ,
258- } ) ;
259- }
256+ if ( executed . kind === "applied" ) {
257+ const stateUpdate = updateState ( {
258+ database : this . database ,
259+ partitionKey,
260+ revisionBefore : outcome . revisionBefore ,
261+ state : executed . state ,
262+ } ) ;
263+ if ( stateUpdate . changes !== 1 ) {
264+ throw new CorruptBalanceStateError ( { partitionKey } ) ;
265+ }
260266
261- return {
262- kind : executed . kind ,
263- state : executed . state ,
264- receipt : executed . receipt ,
265- nextOffset,
266- } as const ;
267- } )
268- . immediate ( ) ;
267+ insertTrackReceipt ( {
268+ database : this . database ,
269+ partitionKey,
270+ position,
271+ receipt : executed . receipt ,
272+ } ) ;
273+ }
274+
275+ const progressUpdate = advancePartitionProgress ( {
276+ database : this . database ,
277+ topic : position . topic ,
278+ partition : position . partition ,
279+ expectedOffset,
280+ nextOffset,
281+ } ) ;
282+ if ( progressUpdate . changes !== 1 ) {
283+ throw new UnexpectedKafkaOffsetError ( {
284+ topic : position . topic ,
285+ partition : position . partition ,
286+ expectedOffset,
287+ receivedOffset : position . offset ,
288+ } ) ;
289+ }
290+
291+ return {
292+ kind : executed . kind ,
293+ state : executed . state ,
294+ receipt : executed . receipt ,
295+ nextOffset,
296+ } ;
269297 }
270298
271299 close ( ) : void {
0 commit comments