Skip to content

Commit 6a467e3

Browse files
Cmdvkderme
authored andcommitted
1727 - Improve Tx queries performance
1 parent 0b0942f commit 6a467e3

File tree

24 files changed

+299
-110
lines changed

24 files changed

+299
-110
lines changed

cardano-db-sync/cardano-db-sync.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ library
111111

112112
Cardano.DbSync.Cache
113113
Cardano.DbSync.Cache.Epoch
114+
Cardano.DbSync.Cache.FIFO
114115
Cardano.DbSync.Cache.LRU
115116
Cardano.DbSync.Cache.Types
116117
Cardano.DbSync.Default

cardano-db-sync/src/Cardano/DbSync/Api.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ import qualified Cardano.Chain.Genesis as Byron
5757
import Cardano.Crypto.ProtocolMagic (ProtocolMagicId (..))
5858
import qualified Cardano.Db as DB
5959
import Cardano.DbSync.Api.Types
60-
import Cardano.DbSync.Cache.LRU (LRUCacheCapacity (..))
61-
import Cardano.DbSync.Cache.Types (newEmptyCache, useNoCache)
60+
import Cardano.DbSync.Cache.Types (CacheCapacity (..), newEmptyCache, useNoCache)
6261
import Cardano.DbSync.Config.Cardano
6362
import Cardano.DbSync.Config.Shelley
6463
import Cardano.DbSync.Config.Types
@@ -403,10 +402,11 @@ mkSyncEnv trce backend connectionString syncOptions protoInfo nw nwMagic systemS
403402
if soptCache syncOptions
404403
then
405404
newEmptyCache
406-
LRUCacheCapacity
407-
{ lirCapacityStakeHashRaw = 1600000
408-
, lruCapacityDatum = 250000
409-
, lruCapacityMultiAsset = 250000
405+
CacheCapacity
406+
{ cacheCapacityStakeHashRaw = 1600000
407+
, cacheCapacityDatum = 250000
408+
, cacheCapacityMultiAsset = 250000
409+
, cacheCapacityTx = 350000
410410
}
411411
else pure useNoCache
412412
consistentLevelVar <- newTVarIO Unchecked

cardano-db-sync/src/Cardano/DbSync/Api/Ledger.hs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import Cardano.BM.Trace (logError, logInfo, logWarning)
1010
import qualified Cardano.Db as DB
1111
import Cardano.DbSync.Api
1212
import Cardano.DbSync.Api.Types
13+
import Cardano.DbSync.Cache (queryTxIdWithCache)
1314
import Cardano.DbSync.Era.Shelley.Generic.Tx.Babbage (fromTxOut)
1415
import Cardano.DbSync.Era.Shelley.Generic.Tx.Types (DBPlutusScript)
1516
import qualified Cardano.DbSync.Era.Shelley.Generic.Util as Generic
1617
import Cardano.DbSync.Era.Universal.Insert.Grouped
1718
import Cardano.DbSync.Era.Universal.Insert.Tx (insertTxOut)
18-
import Cardano.DbSync.Era.Util
1919
import Cardano.DbSync.Error
2020
import Cardano.DbSync.Ledger.State
2121
import Cardano.DbSync.Types
@@ -36,7 +36,6 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
3636
import Control.Monad.Trans.Control (MonadBaseControl)
3737
import Control.Monad.Trans.Except (ExceptT)
3838
import Control.Monad.Trans.Reader (ReaderT)
39-
import Data.ByteString (ByteString)
4039
import Data.List.Extra
4140
import Data.Map (Map)
4241
import qualified Data.Map.Strict as Map
@@ -54,20 +53,13 @@ bootStrapMaybe ::
5453
ExceptT SyncNodeError (ReaderT SqlBackend m) ()
5554
bootStrapMaybe syncEnv = do
5655
bts <- liftIO $ readTVarIO (envBootstrap syncEnv)
57-
when bts $ do
58-
migrateBootstrapUTxO syncEnv emptyTxCache -- TODO: hardcoded to empty
59-
60-
newtype TxCache = TxCache {txIdCache :: Map ByteString DB.TxId}
61-
62-
emptyTxCache :: TxCache
63-
emptyTxCache = TxCache mempty
56+
when bts $ migrateBootstrapUTxO syncEnv
6457

6558
migrateBootstrapUTxO ::
6659
(MonadBaseControl IO m, MonadIO m) =>
6760
SyncEnv ->
68-
TxCache ->
6961
ExceptT SyncNodeError (ReaderT SqlBackend m) ()
70-
migrateBootstrapUTxO syncEnv txCache = do
62+
migrateBootstrapUTxO syncEnv = do
7163
case envLedgerEnv syncEnv of
7264
HasLedger lenv -> do
7365
liftIO $ logInfo trce "Starting UTxO bootstrap migration"
@@ -77,7 +69,7 @@ migrateBootstrapUTxO syncEnv txCache = do
7769
liftIO $
7870
logWarning trce $
7971
"Found and deleted " <> textShow count <> " tx_out."
80-
storeUTxOFromLedger syncEnv txCache cls
72+
storeUTxOFromLedger syncEnv cls
8173
lift $ DB.insertExtraMigration DB.BootstrapFinished
8274
liftIO $ logInfo trce "UTxO bootstrap migration done"
8375
liftIO $ atomically $ writeTVar (envBootstrap syncEnv) False
@@ -86,10 +78,10 @@ migrateBootstrapUTxO syncEnv txCache = do
8678
where
8779
trce = getTrace syncEnv
8880

89-
storeUTxOFromLedger :: (MonadBaseControl IO m, MonadIO m) => SyncEnv -> TxCache -> ExtLedgerState CardanoBlock -> ExceptT SyncNodeError (ReaderT SqlBackend m) ()
90-
storeUTxOFromLedger env txCache st = case ledgerState st of
91-
LedgerStateBabbage bts -> storeUTxO env txCache (getUTxO bts)
92-
LedgerStateConway stc -> storeUTxO env txCache (getUTxO stc)
81+
storeUTxOFromLedger :: (MonadBaseControl IO m, MonadIO m) => SyncEnv -> ExtLedgerState CardanoBlock -> ExceptT SyncNodeError (ReaderT SqlBackend m) ()
82+
storeUTxOFromLedger env st = case ledgerState st of
83+
LedgerStateBabbage bts -> storeUTxO env (getUTxO bts)
84+
LedgerStateConway stc -> storeUTxO env (getUTxO stc)
9385
_ -> liftIO $ logError trce "storeUTxOFromLedger is only supported after Babbage"
9486
where
9587
trce = getTrace env
@@ -111,10 +103,9 @@ storeUTxO ::
111103
, NativeScript era ~ Timelock era
112104
) =>
113105
SyncEnv ->
114-
TxCache ->
115106
Map (TxIn StandardCrypto) (BabbageTxOut era) ->
116107
ExceptT SyncNodeError (ReaderT SqlBackend m) ()
117-
storeUTxO env txCache mp = do
108+
storeUTxO env mp = do
118109
liftIO $
119110
logInfo trce $
120111
mconcat
@@ -123,7 +114,7 @@ storeUTxO env txCache mp = do
123114
, " tx_out as pages of "
124115
, textShow pageSize
125116
]
126-
mapM_ (storePage env txCache pagePerc) . zip [0 ..] . chunksOf pageSize . Map.toList $ mp
117+
mapM_ (storePage env pagePerc) . zip [0 ..] . chunksOf pageSize . Map.toList $ mp
127118
where
128119
trce = getTrace env
129120
npages = size `div` pageSize
@@ -142,13 +133,12 @@ storePage ::
142133
, MonadBaseControl IO m
143134
) =>
144135
SyncEnv ->
145-
TxCache ->
146136
Float ->
147137
(Int, [(TxIn StandardCrypto, BabbageTxOut era)]) ->
148138
ExceptT SyncNodeError (ReaderT SqlBackend m) ()
149-
storePage syncEnv cache percQuantum (n, ls) = do
139+
storePage syncEnv percQuantum (n, ls) = do
150140
when (n `mod` 10 == 0) $ liftIO $ logInfo trce $ "Bootstrap in progress " <> prc <> "%"
151-
txOuts <- mapM (prepareTxOut syncEnv cache) ls
141+
txOuts <- mapM (prepareTxOut syncEnv) ls
152142
txOutIds <- lift . DB.insertManyTxOutPlex True False $ etoTxOut . fst <$> txOuts
153143
let maTxOuts = concatMap mkmaTxOuts $ zip txOutIds (snd <$> txOuts)
154144
void . lift $ DB.insertManyMaTxOut maTxOuts
@@ -168,21 +158,14 @@ prepareTxOut ::
168158
, NativeScript era ~ Timelock era
169159
) =>
170160
SyncEnv ->
171-
TxCache ->
172161
(TxIn StandardCrypto, BabbageTxOut era) ->
173162
ExceptT SyncNodeError (ReaderT SqlBackend m) (ExtendedTxOut, [MissingMaTxOut])
174-
prepareTxOut syncEnv txCache (TxIn txHash (TxIx index), txOut) = do
175-
let txHashByteString = Generic.safeHashToByteString $ unTxId txHash
163+
prepareTxOut syncEnv (TxIn txIntxId (TxIx index), txOut) = do
164+
let txHashByteString = Generic.safeHashToByteString $ unTxId txIntxId
176165
let genTxOut = fromTxOut index txOut
177-
txId <- queryTxIdWithCache txCache txHashByteString
166+
txId <- queryTxIdWithCache cache txIntxId txHashByteString "prepareTxOut"
178167
insertTxOut trce cache iopts (txId, txHashByteString) genTxOut
179168
where
180169
trce = getTrace syncEnv
181170
cache = envCache syncEnv
182171
iopts = soptInsertOptions $ envOptions syncEnv
183-
184-
queryTxIdWithCache :: MonadIO m => TxCache -> ByteString -> ExceptT SyncNodeError (ReaderT SqlBackend m) DB.TxId
185-
queryTxIdWithCache (TxCache mp) hsh = do
186-
case Map.lookup hsh mp of
187-
Just txId -> pure txId
188-
Nothing -> liftLookupFail "queryTxIdWithCache" $ DB.queryTxId hsh

cardano-db-sync/src/Cardano/DbSync/Cache.hs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ module Cardano.DbSync.Cache (
2020
queryOrInsertRewardAccount,
2121
insertStakeAddress,
2222
queryStakeAddrWithCache,
23+
resolveInputTxId,
24+
queryTxIdWithCache,
2325
rollbackCache,
26+
tryUpdateCacheTx,
2427

2528
-- * CacheStatistics
2629
getCacheStatistics,
@@ -29,8 +32,11 @@ module Cardano.DbSync.Cache (
2932
import Cardano.BM.Trace
3033
import qualified Cardano.Db as DB
3134
import Cardano.DbSync.Cache.Epoch (rollbackMapEpochInCache)
35+
import qualified Cardano.DbSync.Cache.FIFO as FIFO
3236
import qualified Cardano.DbSync.Cache.LRU as LRU
3337
import Cardano.DbSync.Cache.Types (CacheAction (..), CacheInternal (..), CacheStatistics (..), CacheStatus (..), initCacheStatistics, isCacheActionUpdate)
38+
import Cardano.DbSync.Era.Shelley.Generic.Tx.Types (TxIn (..))
39+
import qualified Cardano.DbSync.Era.Shelley.Generic.Tx.Types as Generic
3440
import qualified Cardano.DbSync.Era.Shelley.Generic.Util as Generic
3541
import Cardano.DbSync.Era.Shelley.Query
3642
import Cardano.DbSync.Era.Util
@@ -39,6 +45,7 @@ import Cardano.DbSync.Types
3945
import qualified Cardano.Ledger.Address as Ledger
4046
import Cardano.Ledger.BaseTypes (Network)
4147
import Cardano.Ledger.Mary.Value
48+
import qualified Cardano.Ledger.TxIn as Ledger
4249
import Cardano.Prelude
4350
import Control.Concurrent.Class.MonadSTM.Strict (
4451
StrictTVar,
@@ -73,6 +80,7 @@ rollbackCache (ActiveCache cache) blockId = do
7380
liftIO $ do
7481
atomically $ writeTVar (cPrevBlock cache) Nothing
7582
atomically $ modifyTVar (cDatum cache) LRU.cleanup
83+
atomically $ modifyTVar (cTxIds cache) FIFO.cleanupCache
7684
void $ rollbackMapEpochInCache cache blockId
7785

7886
getCacheStatistics :: CacheStatus -> IO CacheStatistics
@@ -361,6 +369,72 @@ queryPrevBlockWithCache msg cache hsh =
361369
liftIO $ missPrevBlock (cStats ci)
362370
liftLookupFail msg $ DB.queryBlockId hsh
363371

372+
queryTxIdWithCache ::
373+
MonadIO m =>
374+
CacheStatus ->
375+
Ledger.TxId StandardCrypto ->
376+
ByteString ->
377+
Text ->
378+
ExceptT SyncNodeError (ReaderT SqlBackend m) DB.TxId
379+
queryTxIdWithCache cache ledgerTxId txHash errTxt = do
380+
case cache of
381+
NoCache -> liftLookupFail errTxt $ DB.queryTxId txHash
382+
ActiveCache ci -> do
383+
mp <- liftIO $ readTVarIO (cTxIds ci)
384+
case FIFO.lookup ledgerTxId mp of
385+
Just txId -> do
386+
liftIO $ hitTxIds (cStats ci)
387+
pure txId
388+
Nothing -> do
389+
resTxId <- liftLookupFail errTxt $ DB.queryTxId txHash
390+
liftIO $ missTxIds (cStats ci)
391+
liftIO $ atomically $ modifyTVar (cTxIds ci) $ FIFO.insert ledgerTxId resTxId
392+
pure resTxId
393+
394+
resolveInputTxId ::
395+
MonadIO m =>
396+
Generic.TxIn ->
397+
CacheStatus ->
398+
ReaderT SqlBackend m (Either DB.LookupFail DB.TxId)
399+
resolveInputTxId txIn cache = do
400+
let txHash = Generic.txInHash txIn
401+
case cache of
402+
-- Direct database query if no cache.
403+
NoCache -> DB.queryTxId txHash
404+
ActiveCache cacheInternal -> do
405+
-- Read current cache state.
406+
cacheTx <- liftIO $ readTVarIO (cTxIds cacheInternal)
407+
408+
case FIFO.lookup (txInTxId txIn) cacheTx of
409+
-- Cache hit, return the transaction ID.
410+
Just txId -> do
411+
liftIO $ hitTxIds (cStats cacheInternal)
412+
pure $ Right txId
413+
-- Cache miss.
414+
Nothing -> do
415+
eTxId <- DB.queryTxId txHash
416+
liftIO $ missTxIds (cStats cacheInternal)
417+
case eTxId of
418+
Right txId -> do
419+
-- Update cache.
420+
liftIO $ atomically $ modifyTVar (cTxIds cacheInternal) $ FIFO.insert (txInTxId txIn) txId
421+
-- Return ID after updating cache.
422+
pure $ Right txId
423+
-- Return lookup failure.
424+
Left _ -> pure $ Left $ DB.DbLookupTxHash txHash
425+
426+
tryUpdateCacheTx ::
427+
MonadIO m =>
428+
CacheStatus ->
429+
Ledger.TxId StandardCrypto ->
430+
DB.TxId ->
431+
m ()
432+
tryUpdateCacheTx cache ledgerTxId txId = do
433+
case cache of
434+
NoCache -> pure ()
435+
ActiveCache ci -> do
436+
liftIO $ atomically $ modifyTVar (cTxIds ci) $ FIFO.insert ledgerTxId txId
437+
364438
insertBlockAndCache ::
365439
(MonadIO m, MonadBaseControl IO m) =>
366440
CacheStatus ->
@@ -458,3 +532,12 @@ hitPBlock ref =
458532
missPrevBlock :: StrictTVar IO CacheStatistics -> IO ()
459533
missPrevBlock ref =
460534
atomically $ modifyTVar ref (\cs -> cs {prevBlockQueries = 1 + prevBlockQueries cs})
535+
536+
-- TxIds
537+
hitTxIds :: StrictTVar IO CacheStatistics -> IO ()
538+
hitTxIds ref =
539+
atomically $ modifyTVar ref (\cs -> cs {txIdsHits = 1 + txIdsHits cs, txIdsQueries = 1 + txIdsQueries cs})
540+
541+
missTxIds :: StrictTVar IO CacheStatistics -> IO ()
542+
missTxIds ref =
543+
atomically $ modifyTVar ref (\cs -> cs {txIdsQueries = 1 + txIdsQueries cs})
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE RecordWildCards #-}
3+
{-# LANGUAGE NoImplicitPrelude #-}
4+
5+
module Cardano.DbSync.Cache.FIFO (
6+
FIFOCache (..),
7+
empty,
8+
insert,
9+
lookup,
10+
getSize,
11+
getCapacity,
12+
cleanupCache,
13+
) where
14+
15+
import Cardano.Prelude hiding (empty)
16+
import qualified Data.Map.Strict as Map
17+
import Data.Sequence.Strict (StrictSeq)
18+
import qualified Data.Sequence.Strict as Seq
19+
20+
{-
21+
FIFOCache: A First-In-First-Out cache with fixed capacity, using Map.Strict
22+
for key-value storage and Sequence.Strict for maintaining insertion order.
23+
24+
Key operations and their complexities:
25+
- Insertion: O(log n)
26+
- Lookup: O(log n)
27+
- Removal of oldest element: O(1)
28+
- Size query: O(1)
29+
-}
30+
data FIFOCache k v = FIFOCache
31+
{ maxCapacity :: !Word64
32+
, cacheMap :: !(Map.Map k v)
33+
, keyOrder :: !(StrictSeq k)
34+
}
35+
36+
empty :: Word64 -> FIFOCache k v
37+
empty capacity = FIFOCache capacity Map.empty Seq.empty
38+
39+
insert :: Ord k => k -> v -> FIFOCache k v -> FIFOCache k v
40+
insert key value cache@FIFOCache {..}
41+
| Map.size cacheMap >= fromIntegral maxCapacity =
42+
case Seq.lookup 0 keyOrder of
43+
Nothing -> cache -- This should never happen if invariants are maintained
44+
Just oldestKey ->
45+
let !newKeyOrder = key Seq.<| Seq.drop 1 keyOrder
46+
!newMap = Map.insert key value $ Map.delete oldestKey cacheMap
47+
in cache {cacheMap = newMap, keyOrder = newKeyOrder}
48+
| otherwise =
49+
cache
50+
{ cacheMap = Map.insert key value cacheMap
51+
, keyOrder = keyOrder Seq.|> key
52+
}
53+
54+
lookup :: Ord k => k -> FIFOCache k v -> Maybe v
55+
lookup key FIFOCache {..} = Map.lookup key cacheMap
56+
57+
getSize :: FIFOCache k v -> Int
58+
getSize = Map.size . cacheMap
59+
60+
getCapacity :: FIFOCache k v -> Word64
61+
getCapacity = maxCapacity
62+
63+
cleanupCache :: FIFOCache k v -> FIFOCache k v
64+
cleanupCache cache = cache {cacheMap = Map.empty, keyOrder = Seq.empty}

cardano-db-sync/src/Cardano/DbSync/Cache/LRU.hs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
module Cardano.DbSync.Cache.LRU (
55
LRUCache (..),
6-
LRUCacheCapacity (..),
76
empty,
87
cleanup,
98
trim,
@@ -30,13 +29,6 @@ data LRUCache k v = LRUCache
3029
, cQueue :: !(OrdPSQ k Word64 v) -- The priority search queue storing the cache entries
3130
}
3231

33-
-- LRUCacheCapacity is used to define capacities for different types of cache entries.
34-
data LRUCacheCapacity = LRUCacheCapacity
35-
{ lirCapacityStakeHashRaw :: !Word64
36-
, lruCapacityDatum :: !Word64
37-
, lruCapacityMultiAsset :: !Word64
38-
}
39-
4032
-- empty creates an empty LRUCache with the specified capacity.
4133
empty :: Word64 -> LRUCache k v
4234
empty capacity =

0 commit comments

Comments
 (0)