1
+ {-# LANGUAGE ApplicativeDo #-}
1
2
{-# LANGUAGE FlexibleContexts #-}
2
3
{-# LANGUAGE OverloadedStrings #-}
4
+ {-# LANGUAGE RankNTypes #-}
3
5
{-# LANGUAGE RecordWildCards #-}
4
6
{-# LANGUAGE NoImplicitPrelude #-}
5
7
@@ -9,6 +11,7 @@ module Cardano.DbSync.Era.Universal.Insert.Grouped (
9
11
ExtendedTxIn (.. ),
10
12
ExtendedTxOut (.. ),
11
13
insertBlockGroupedData ,
14
+ insertBlockGroupedDataSequential ,
12
15
insertReverseIndex ,
13
16
resolveTxInputs ,
14
17
resolveScriptHash ,
@@ -87,12 +90,13 @@ instance Semigroup BlockGroupedData where
87
90
(groupedTxFees tgd1 + groupedTxFees tgd2)
88
91
(groupedTxOutSum tgd1 + groupedTxOutSum tgd2)
89
92
90
- insertBlockGroupedData ::
93
+ -- | Original sequential implementation (kept for fallback)
94
+ insertBlockGroupedDataSequential ::
91
95
MonadIO m =>
92
96
SyncEnv ->
93
97
BlockGroupedData ->
94
98
DB. DbAction m DB. MinIdsWrapper
95
- insertBlockGroupedData syncEnv grouped = do
99
+ insertBlockGroupedDataSequential syncEnv grouped = do
96
100
disInOut <- liftIO $ getDisableInOutState syncEnv
97
101
98
102
let txOutChunks = chunksOf maxBulkSize $ etoTxOut . fst <$> groupedTxOut grouped
@@ -137,47 +141,54 @@ insertBlockGroupedData syncEnv grouped = do
137
141
mapM_ (DB. insertBulkTxMetadata removeJsonbFromSchema) txMetadataChunks
138
142
mapM_ DB. insertBulkMaTxMint txMintChunks
139
143
140
- pure $ makeMinId txInIds txOutIds maTxOutIds
144
+ pure $ makeMinId syncEnv txInIds txOutIds maTxOutIds
141
145
where
142
146
tracer = getTrace syncEnv
143
147
txOutVariantType = getTxOutVariantType syncEnv
144
148
removeJsonbFromSchema = ioRemoveJsonbFromSchema $ soptInsertOptions $ envOptions syncEnv
145
149
146
- categorizeResolvedInputs :: [ExtendedTxIn ] -> ([DB. BulkConsumedByHash ], [(DB. TxOutIdW , DB. TxId )], [ExtendedTxIn ])
147
- categorizeResolvedInputs etis =
148
- let (hashBased, idBased, failed) = foldr categorizeOne ([] , [] , [] ) etis
149
- in (hashBased, idBased, failed)
150
- where
151
- categorizeOne ExtendedTxIn {.. } (hAcc, iAcc, fAcc) =
152
- case etiTxOutId of
153
- Right txOutId ->
154
- (hAcc, (txOutId, DB. txInTxInId etiTxIn) : iAcc, fAcc)
155
- Left genericTxIn ->
156
- let bulkData =
157
- DB. BulkConsumedByHash
158
- { bchTxHash = unTxHash (Generic. txInTxId genericTxIn)
159
- , bchOutputIndex = Generic. txInIndex genericTxIn
160
- , bchConsumingTxId = DB. txInTxInId etiTxIn
161
- }
162
- in (bulkData : hAcc, iAcc, fAcc)
163
-
164
- makeMinId :: [DB. TxInId ] -> [DB. TxOutIdW ] -> [DB. MaTxOutIdW ] -> DB. MinIdsWrapper
165
- makeMinId txInIds txOutIds maTxOutIds =
166
- case txOutVariantType of
167
- DB. TxOutVariantCore -> do
168
- DB. CMinIdsWrapper $
169
- DB. MinIds
170
- { minTxInId = listToMaybe txInIds
171
- , minTxOutId = listToMaybe txOutIds
172
- , minMaTxOutId = listToMaybe maTxOutIds
173
- }
174
- DB. TxOutVariantAddress ->
175
- DB. VMinIdsWrapper $
176
- DB. MinIds
177
- { minTxInId = listToMaybe txInIds
178
- , minTxOutId = listToMaybe txOutIds
179
- , minMaTxOutId = listToMaybe maTxOutIds
180
- }
150
+ -- | Parallel implementation with single connection coordination
151
+ insertBlockGroupedData ::
152
+ MonadIO m =>
153
+ SyncEnv ->
154
+ BlockGroupedData ->
155
+ DB. DbAction m DB. MinIdsWrapper
156
+ insertBlockGroupedData syncEnv grouped = do
157
+ disInOut <- liftIO $ getDisableInOutState syncEnv
158
+
159
+ -- Parallel preparation of independent data
160
+ (preparedTxIn, preparedMetadata, preparedMint, txOutChunks) <- liftIO $ do
161
+ a1 <- async $ pure $ prepareTxInProcessing syncEnv grouped
162
+ a2 <- async $ pure $ prepareMetadataProcessing syncEnv grouped
163
+ a3 <- async $ pure $ prepareMintProcessing syncEnv grouped
164
+ a4 <- async $ pure $ chunksOf maxBulkSize $ etoTxOut . fst <$> groupedTxOut grouped
165
+
166
+ r1 <- wait a1
167
+ r2 <- wait a2
168
+ r3 <- wait a3
169
+ r4 <- wait a4
170
+ pure (r1, r2, r3, r4)
171
+
172
+ -- Sequential TxOut processing (generates required IDs)
173
+ txOutIds <- concat <$> mapM (DB. insertBulkTxOut disInOut) txOutChunks
174
+
175
+ -- PHASE 3: Execute independent operations (TxIn, Metadata, Mint) in parallel
176
+ txInIds <- executePreparedTxIn preparedTxIn
177
+
178
+ -- PHASE 4: Pipeline TxOut-dependent operations (MaTxOut + UTxO consumption)
179
+ maTxOutIds <- processMaTxOuts syncEnv txOutIds grouped
180
+
181
+ -- PHASE 5: Execute remaining independent operations in parallel
182
+ liftIO $ do
183
+ a1 <- async $ DB. runDbActionIO (envDbEnv syncEnv) (executePreparedMetadata preparedMetadata)
184
+ a2 <- async $ DB. runDbActionIO (envDbEnv syncEnv) (executePreparedMint preparedMint)
185
+ _ <- wait a1
186
+ void $ wait a2
187
+
188
+ -- PHASE 6: Process UTxO consumption (depends on txOutIds)
189
+ processUtxoConsumption syncEnv grouped txOutIds
190
+
191
+ pure $ makeMinId syncEnv txInIds txOutIds maTxOutIds
181
192
182
193
mkmaTxOuts :: DB. TxOutVariantType -> (DB. TxOutIdW , [MissingMaTxOut ]) -> [DB. MaTxOutW ]
183
194
mkmaTxOuts _txOutVariantType (txOutId, mmtos) = mkmaTxOut <$> mmtos
@@ -341,3 +352,141 @@ matches txIn eutxo =
341
352
getTxOutIndex txOutWrapper = case txOutWrapper of
342
353
DB. VCTxOutW cTxOut -> VC. txOutCoreIndex cTxOut
343
354
DB. VATxOutW vTxOut _ -> VA. txOutAddressIndex vTxOut
355
+
356
+ -----------------------------------------------------------------------------------------------------------------------------------
357
+ -- PARALLEL PROCESSING HELPER FUNCTIONS
358
+ -----------------------------------------------------------------------------------------------------------------------------------
359
+
360
+ -- | Prepared TxIn data for async execution
361
+ data PreparedTxIn = PreparedTxIn
362
+ { ptiChunks :: ! [[DB. TxIn ]]
363
+ , ptiSkip :: ! Bool
364
+ }
365
+
366
+ -- | Prepared Metadata data for async execution
367
+ data PreparedMetadata = PreparedMetadata
368
+ { pmChunks :: ! [[DB. TxMetadata ]]
369
+ , pmRemoveJsonb :: ! Bool
370
+ }
371
+
372
+ -- | Prepared Mint data for async execution
373
+ data PreparedMint = PreparedMint
374
+ { pmtChunks :: ! [[DB. MaTxMint ]]
375
+ }
376
+
377
+ -- | Prepare TxIn processing (can run in parallel with TxOut)
378
+ prepareTxInProcessing :: SyncEnv -> BlockGroupedData -> PreparedTxIn
379
+ prepareTxInProcessing syncEnv grouped =
380
+ PreparedTxIn
381
+ { ptiChunks = chunksOf maxBulkSize $ etiTxIn <$> groupedTxIn grouped
382
+ , ptiSkip = getSkipTxIn syncEnv
383
+ }
384
+
385
+ -- | Prepare Metadata processing (fully independent)
386
+ prepareMetadataProcessing :: SyncEnv -> BlockGroupedData -> PreparedMetadata
387
+ prepareMetadataProcessing syncEnv grouped =
388
+ PreparedMetadata
389
+ { pmChunks = chunksOf maxBulkSize $ groupedTxMetadata grouped
390
+ , pmRemoveJsonb = ioRemoveJsonbFromSchema $ soptInsertOptions $ envOptions syncEnv
391
+ }
392
+
393
+ -- | Prepare Mint processing (fully independent)
394
+ prepareMintProcessing :: SyncEnv -> BlockGroupedData -> PreparedMint
395
+ prepareMintProcessing _syncEnv grouped =
396
+ PreparedMint
397
+ { pmtChunks = chunksOf maxBulkSize $ groupedTxMint grouped
398
+ }
399
+
400
+ -- | Execute prepared TxIn operations
401
+ executePreparedTxIn :: MonadIO m => PreparedTxIn -> DB. DbAction m [DB. TxInId ]
402
+ executePreparedTxIn prepared =
403
+ if ptiSkip prepared
404
+ then pure []
405
+ else concat <$> mapM DB. insertBulkTxIn (ptiChunks prepared)
406
+
407
+ -- | Execute prepared Metadata operations
408
+ executePreparedMetadata :: MonadIO m => PreparedMetadata -> DB. DbAction m ()
409
+ executePreparedMetadata prepared =
410
+ mapM_ (DB. insertBulkTxMetadata (pmRemoveJsonb prepared)) (pmChunks prepared)
411
+
412
+ -- | Execute prepared Mint operations
413
+ executePreparedMint :: MonadIO m => PreparedMint -> DB. DbAction m ()
414
+ executePreparedMint prepared =
415
+ mapM_ DB. insertBulkMaTxMint (pmtChunks prepared)
416
+
417
+ -- | Process MaTxOut operations (depends on TxOut IDs)
418
+ processMaTxOuts :: MonadIO m => SyncEnv -> [DB. TxOutIdW ] -> BlockGroupedData -> DB. DbAction m [DB. MaTxOutIdW ]
419
+ processMaTxOuts syncEnv txOutIds grouped = do
420
+ let txOutVariantType = getTxOutVariantType syncEnv
421
+ maTxOuts = concatMap (mkmaTxOuts txOutVariantType) $
422
+ zip txOutIds (snd <$> groupedTxOut grouped)
423
+ maTxOutChunks = chunksOf maxBulkSize maTxOuts
424
+ concat <$> mapM DB. insertBulkMaTxOut maTxOutChunks
425
+
426
+ -- | Process UTxO consumption updates (depends on TxOut IDs)
427
+ processUtxoConsumption :: MonadIO m => SyncEnv -> BlockGroupedData -> [DB. TxOutIdW ] -> DB. DbAction m ()
428
+ processUtxoConsumption syncEnv grouped txOutIds = do
429
+ let tracer = getTrace syncEnv
430
+ txOutVariantType = getTxOutVariantType syncEnv
431
+
432
+ whenConsumeOrPruneTxOut syncEnv $ do
433
+ -- Resolve remaining inputs
434
+ etis <- resolveRemainingInputs (groupedTxIn grouped) $ zip txOutIds (fst <$> groupedTxOut grouped)
435
+ -- Categorise resolved inputs for bulk vs individual processing
436
+ let (hashBasedUpdates, idBasedUpdates, failedInputs) = categorizeResolvedInputs etis
437
+ hashUpdateChunks = chunksOf maxBulkSize hashBasedUpdates
438
+ idUpdateChunks = chunksOf maxBulkSize idBasedUpdates
439
+
440
+ -- Bulk process hash-based updates
441
+ unless (null hashBasedUpdates) $
442
+ mapM_ (DB. updateConsumedByTxHashBulk txOutVariantType) hashUpdateChunks
443
+ -- Individual process ID-based updates
444
+ unless (null idBasedUpdates) $
445
+ mapM_ DB. updateListTxOutConsumedByTxId idUpdateChunks
446
+ -- Log failures
447
+ mapM_ (liftIO . logWarning tracer . (" Failed to find output for " <> ) . Text. pack . show ) failedInputs
448
+
449
+ -- | Helper function to categorize resolved inputs for parallel processing
450
+ categorizeResolvedInputs :: [ExtendedTxIn ] -> ([DB. BulkConsumedByHash ], [(DB. TxOutIdW , DB. TxId )], [ExtendedTxIn ])
451
+ categorizeResolvedInputs etis =
452
+ let (hashBased, idBased, failed) = foldr categorizeOne ([] , [] , [] ) etis
453
+ in (hashBased, idBased, failed)
454
+ where
455
+ categorizeOne ExtendedTxIn {.. } (hAcc, iAcc, fAcc) =
456
+ case etiTxOutId of
457
+ Right txOutId ->
458
+ (hAcc, (txOutId, DB. txInTxInId etiTxIn) : iAcc, fAcc)
459
+ Left genericTxIn ->
460
+ let bulkData =
461
+ DB. BulkConsumedByHash
462
+ { bchTxHash = unTxHash (Generic. txInTxId genericTxIn)
463
+ , bchOutputIndex = Generic. txInIndex genericTxIn
464
+ , bchConsumingTxId = DB. txInTxInId etiTxIn
465
+ }
466
+ in (bulkData : hAcc, iAcc, fAcc)
467
+
468
+ -----------------------------------------------------------------------------------------------------------------------------------
469
+ -- PARALLEL PROCESSING HELPER FUNCTIONS (NO PIPELINES)
470
+ -----------------------------------------------------------------------------------------------------------------------------------
471
+
472
+ -- Note: After analysis, pipelines aren't suitable here due to data dependencies.
473
+ -- The current approach using async for truly independent operations is optimal.
474
+
475
+ -- | Helper function to create MinIds result
476
+ makeMinId :: SyncEnv -> [DB. TxInId ] -> [DB. TxOutIdW ] -> [DB. MaTxOutIdW ] -> DB. MinIdsWrapper
477
+ makeMinId syncEnv txInIds txOutIds maTxOutIds =
478
+ case getTxOutVariantType syncEnv of
479
+ DB. TxOutVariantCore ->
480
+ DB. CMinIdsWrapper $
481
+ DB. MinIds
482
+ { minTxInId = listToMaybe txInIds
483
+ , minTxOutId = listToMaybe txOutIds
484
+ , minMaTxOutId = listToMaybe maTxOutIds
485
+ }
486
+ DB. TxOutVariantAddress ->
487
+ DB. VMinIdsWrapper $
488
+ DB. MinIds
489
+ { minTxInId = listToMaybe txInIds
490
+ , minTxOutId = listToMaybe txOutIds
491
+ , minMaTxOutId = listToMaybe maTxOutIds
492
+ }
0 commit comments