Skip to content

Commit a221e25

Browse files
authored
Merge pull request #1987 from IntersectMBO/1986-duplicate-pool-stats
1986 - Fix duplicate pool stats
2 parents 698ffde + 7b11576 commit a221e25

File tree

8 files changed

+193
-4
lines changed

8 files changed

+193
-4
lines changed

cardano-chain-gen/test/Test/Cardano/Db/Mock/Config.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module Test.Cardano.Db.Mock.Config (
3535
configMetadataEnable,
3636
configMetadataDisable,
3737
configMetadataKeys,
38+
configPoolStats,
3839
mkFingerPrint,
3940
mkMutableDir,
4041
mkDBSyncEnv,
@@ -357,6 +358,10 @@ configMetadataKeys :: SyncNodeConfig -> SyncNodeConfig
357358
configMetadataKeys cfg = do
358359
cfg {dncInsertOptions = (dncInsertOptions cfg) {sioMetadata = MetadataKeys $ 1 :| []}}
359360

361+
configPoolStats :: SyncNodeConfig -> SyncNodeConfig
362+
configPoolStats cfg = do
363+
cfg {dncInsertOptions = (dncInsertOptions cfg) {sioPoolStats = PoolStatsConfig True}}
364+
360365
initCommandLineArgs :: CommandLineArgs
361366
initCommandLineArgs =
362367
CommandLineArgs

cardano-chain-gen/test/Test/Cardano/Db/Mock/Unit/Conway.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ unitTests iom knownMigrations =
127127
, test "rollback stake address cache" Rollback.stakeAddressRollback
128128
, test "rollback change order of txs" Rollback.rollbackChangeTxOrder
129129
, test "rollback full tx" Rollback.rollbackFullTx
130+
, test "basic pool stats functionality" Rollback.poolStatBasicTest
131+
, test "pool stat rollback no duplicates" Rollback.poolStatRollbackNoDuplicates
132+
, test "pool stat rollback general" Rollback.poolStatRollbackGeneral
130133
]
131134
, testGroup
132135
"different configs"

cardano-chain-gen/test/Test/Cardano/Db/Mock/Unit/Conway/Rollback.hs

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ module Test.Cardano.Db.Mock.Unit.Conway.Rollback (
1616
rollbackChangeTxOrder,
1717
rollbackFullTx,
1818
drepDistrRollback,
19+
poolStatBasicTest,
20+
poolStatRollbackNoDuplicates,
21+
poolStatRollbackGeneral,
1922
) where
2023

2124
import qualified Cardano.Db as DB
22-
import Cardano.DbSync.Era.Shelley.Generic.Util (unCredentialHash)
25+
import Cardano.DbSync.Era.Shelley.Generic (unCredentialHash)
2326
import Cardano.Ledger.Coin (Coin (..))
2427
import Cardano.Ledger.Conway.TxCert (ConwayDelegCert (..), Delegatee (..))
2528
import Cardano.Mock.ChainSync.Server (IOManager (), addBlock, rollback)
@@ -31,11 +34,11 @@ import Cardano.Mock.Forging.Types (PoolIndex (..), StakeIndex (..), UTxOIndex (.
3134
import Cardano.Prelude
3235
import Data.Maybe.Strict (StrictMaybe (..))
3336
import Ouroboros.Network.Block (blockPoint)
34-
import Test.Cardano.Db.Mock.Config
35-
import Test.Cardano.Db.Mock.Examples (mockBlock0, mockBlock1, mockBlock2)
37+
import Test.Cardano.Db.Mock.Config (claFullMode, configPoolStats, conwayConfigDir, initCommandLineArgs, queryDBSync, startDBSync, stopDBSync, withCustomConfigDropDB, withFullConfigDropDB)
38+
import Test.Cardano.Db.Mock.Examples
3639
import Test.Cardano.Db.Mock.UnifiedApi
3740
import Test.Cardano.Db.Mock.Validate (assertBlockNoBackoff, assertEqQuery, assertTxCount)
38-
import Test.Tasty.HUnit (Assertion ())
41+
import Test.Tasty.HUnit (Assertion (), assertBool, assertEqual)
3942
import Prelude (error, head, last)
4043

4144
simpleRollback :: IOManager -> [(Text, Text)] -> Assertion
@@ -373,3 +376,108 @@ drepDistrRollback =
373376
"DrepDistr for epoch 2 should be re-inserted after replay through boundary"
374377
where
375378
testLabel = "conwayDrepDistrRollback"
379+
380+
poolStatBasicTest :: IOManager -> [(Text, Text)] -> Assertion
381+
poolStatBasicTest =
382+
withCustomConfigDropDB args (Just configPoolStats) conwayConfigDir testLabel $
383+
\interpreter mockServer dbSync -> do
384+
startDBSync dbSync
385+
386+
-- Test basic pool stats functionality
387+
void $ registerAllStakeCreds interpreter mockServer
388+
assertBlockNoBackoff dbSync 1
389+
390+
-- Create some epochs with pool stats
391+
void $ forgeAndSubmitBlocks interpreter mockServer 200
392+
assertBlockNoBackoff dbSync 201
393+
394+
poolStatCount <- queryDBSync dbSync DB.queryPoolStatCount
395+
396+
-- Verify pool stats are created and no duplicates exist
397+
duplicateCount <- queryDBSync dbSync DB.queryPoolStatDuplicates
398+
assertEqual "Should have no duplicate pool stats" 0 duplicateCount
399+
assertBool "Should have some pool stats" (poolStatCount > 0)
400+
where
401+
args = initCommandLineArgs {claFullMode = False}
402+
testLabel = "conwayPoolStatBasicTest"
403+
404+
poolStatRollbackNoDuplicates :: IOManager -> [(Text, Text)] -> Assertion
405+
poolStatRollbackNoDuplicates =
406+
withCustomConfigDropDB args (Just configPoolStats) conwayConfigDir testLabel $
407+
\interpreter mockServer dbSync -> do
408+
startDBSync dbSync
409+
410+
-- Simple setup: create some blocks with pool stats
411+
void $ registerAllStakeCreds interpreter mockServer
412+
void $ forgeAndSubmitBlocks interpreter mockServer 200 -- Fill 2 epochs
413+
assertBlockNoBackoff dbSync 201
414+
415+
-- Create rollback point
416+
rollbackBlks <- forgeAndSubmitBlocks interpreter mockServer 50
417+
assertBlockNoBackoff dbSync 251
418+
419+
-- Add more blocks to create additional pool stats
420+
void $ forgeAndSubmitBlocks interpreter mockServer 100 -- Fill 1 more epoch
421+
assertBlockNoBackoff dbSync 351
422+
423+
-- Rollback (following exact pattern from bigChain test)
424+
atomically $ rollback mockServer (blockPoint $ last rollbackBlks)
425+
assertBlockNoBackoff dbSync 351 -- Delayed rollbackExpand commentComment on line R345ResolvedCode has comments. Press enter to view.
426+
427+
-- Re-sync some blocks
428+
void $ forgeAndSubmitBlocks interpreter mockServer 100
429+
assertBlockNoBackoff dbSync 351 -- Should stay same due to rollbackExpand commentComment on line R349ResolvedCode has comments. Press enter to view.
430+
431+
-- The main test: no duplicates after rollback + re-sync
432+
duplicateCount <- queryDBSync dbSync DB.queryPoolStatDuplicates
433+
assertEqual "Should have no duplicate pool stats after rollback" 0 duplicateCount
434+
where
435+
args = initCommandLineArgs {claFullMode = False}
436+
testLabel = "conwayPoolStatRollbackNoDuplicates"
437+
438+
poolStatRollbackGeneral :: IOManager -> [(Text, Text)] -> Assertion
439+
poolStatRollbackGeneral =
440+
withCustomConfigDropDB args (Just configPoolStats) conwayConfigDir testLabel $
441+
\interpreter mockServer dbSync -> do
442+
startDBSync dbSync
443+
444+
-- Create pools and stake to generate pool stats
445+
void $ registerAllStakeCreds interpreter mockServer
446+
epochBlks1 <- fillEpochs interpreter mockServer 2
447+
448+
-- Create rollback point
449+
rollbackBlks <- forgeAndSubmitBlocks interpreter mockServer 10
450+
let totalBeforeRollback = length epochBlks1 + length rollbackBlks + 1
451+
assertBlockNoBackoff dbSync totalBeforeRollback
452+
453+
-- Check initial pool stat count
454+
initialCount <- queryDBSync dbSync DB.queryPoolStatCount
455+
456+
-- Forge more blocks to create additional pool stats
457+
epochBlks2 <- fillEpochs interpreter mockServer 1
458+
let totalAfterEpoch = totalBeforeRollback + length epochBlks2
459+
assertBlockNoBackoff dbSync totalAfterEpoch
460+
461+
-- Verify pool stats increased
462+
afterCount <- queryDBSync dbSync DB.queryPoolStatCount
463+
assertBool "Pool stats should have increased" (afterCount > initialCount)
464+
465+
-- Rollback to previous point
466+
atomically $ rollback mockServer (blockPoint $ last rollbackBlks)
467+
assertBlockNoBackoff dbSync totalAfterEpoch -- Delayed rollbackExpand commentComment on line R387ResolvedCode has comments. Press enter to view.
468+
469+
-- Re-sync the same blocks - should not create duplicates
470+
epochBlks3 <- fillEpochs interpreter mockServer 1
471+
let finalTotal = totalBeforeRollback + length epochBlks3 + 1
472+
assertBlockNoBackoff dbSync finalTotal
473+
finalCount <- queryDBSync dbSync DB.queryPoolStatCount
474+
475+
-- Verify count matches and no constraint violations occurred
476+
assertEqual "Pool stat count should match after rollback" afterCount finalCount
477+
478+
-- Also verify no duplicates
479+
duplicateCount <- queryDBSync dbSync DB.queryPoolStatDuplicates
480+
assertEqual "Should have no duplicate pool stats" 0 duplicateCount
481+
where
482+
args = initCommandLineArgs {claFullMode = False}
483+
testLabel = "conwayPoolStatRollbackGeneral"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[12,16,18,21,24,30,31,32,33,40,41,42,43,47,52,60,62,70,80,84,86,92,98,100,106,109,110,111,112,127,134,138,146,149,154,166,168,178,183,188,193,194,198,200,202,220,222,223,224,225,231,239,242,247,261,282,283,288,289,301,302,303,308,313,315,316,320,331,334,344,345,363,364,368,369,375,377,381,389,394,407,418,422,425,430,437,438,439,440,447,450,453,454,456,458,461,467,492,499,507,516,524,538,541,544,546,550,567,573,576,577,579,580,586,589,595,597,603,605,609,616,618,619,623,624,634,636,643,644,659,664,665,672,678,692,705,711,712,719,726,730,739,740,743,747,749,751,754,759,762,763,765,767,773,777,786,788,789,794,801,806,807,829,830,832,849,851,853,869,871,874,875,878,882,888,893,895,896,898,899,903,906,908,911,912,913,922,930,932,938,941,944,950,960,963,966,968,972,977,985,986]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[12,16,18,21,24,30,31,32,33,40,41,42,43,47,52,60,62,70,80,84,86,92,98,100,106,109,110,111,112,127,134,138,146,149,154,166,168,178,183,188,193,194,198,200,202,220,222,223,224,225,231,239,242,247,261,282,283,288,289,301,302,303,308,313,315,316,320,331,334,344,345,363,364,368,369,375,377,381,389,394,407,418,422,425,430,437,438,439,440,447,450,453,454,456,458,461,467,492,499,507,516,524,538,541,544,546,550,567,573,576,577,579,580,586,589,595,597,603,605,609,616,618,619,623,624,634,636,643,644,659,664,665,672,678,692,705,711,712,719,726,730,739,740,743,747,749,751,754,759,762,763,765,767,773,777,786,788,789,794,801,806,807,829,830,832,849,851,853,869,871,874,875,878,882,888,893,895,896,898,899,903,906,908,911,912,913,922,930,932,938,941,944,950,960,963,966,968,972,977,985,986,988,990,991,994,997,1002,1034,1035,1036,1039,1041,1051,1059,1061,1063,1068,1076,1081,1082,1091,1101,1102,1104,1106,1123,1126,1138,1141,1143,1144,1149,1162,1167,1172,1177,1181,1184,1185,1188,1190,1203,1205,1214,1215,1221,1233,1234,1245,1249,1250,1251,1261,1265,1266,1267,1272,1281,1283,1289,1294,1300,1304,1307,1308,1310,1314,1315,1319,1325,1334,1350,1353,1359,1362,1370,1371,1373,1375,1381,1399,1404,1415,1416,1419,1420,1425,1426,1435,1436,1437,1441,1444,1448,1458,1461,1462,1467,1470,1479,1486,1489,1494,1496,1497,1513,1519,1528,1529,1538,1549,1551,1553,1555,1567,1580,1583,1595,1601,1603,1613,1614,1616,1625,1637,1638,1639,1640,1643,1653,1654,1655,1658,1667,1672,1674,1683,1692,1700,1706,1709,1712,1714,1715,1717,1726,1733,1750,1756,1758,1759,1771,1778,1781,1783,1789,1797,1800,1801,1816,1823,1828,1831,1840,1847,1852,1858,1872,1873,1874,1876,1893,1894,1895,1900,1905,1912,1918,1923,1930,1940,1944,1949,1952,1953,1958,1965,1966,1968,1972,1974,1975,1994,2005]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[12,16,18,21,24,30,31,32,33,40,41,42,43,47,52,60,62,70,80,84,86,92,98,100,106,109,110,111,112,127,134,138,146,149,154,166,168,178,183,188,193,194,198,200,202,220,222,223,224,225,231,239,242,247,261,282,283,288,289,301,302,303,308,313,315,316,320,331,334,344,345,363,364,368,369,375,377,381,389,394,407,418,422,425,430,437,438,439,440,447,450,453,454,456,458,461,467,492,499,507,516,524,538,541,544,546,550,567,573,576,577,579,580,586,589,595,597,603,605,609,616,618,619,623,624,634,636,643,644,659,664,665,672,678,692,705,711,712,719,726,730,739,740,743,747,749,751,754,759,762,763,765,767,773,777,786,788,789,794,801,806,807,829,830,832,849,851,853,869,871,874,875,878,882,888,893,895,896,898,899,903,906,908,911,912,913,922,930,932,938,941,944,950,960,963,966,968,972,977,985,986,988,990,991,994,997,1002,1034,1035,1036,1039,1041,1051,1059,1061,1063,1068,1076,1081,1082,1091,1101,1102,1104,1106,1123,1126,1138,1141,1143,1144,1149,1162,1167,1172,1177,1181,1184,1185,1188,1190,1203,1205,1214,1215,1221,1233,1234,1245,1249,1250,1251,1261,1265,1266,1267,1272,1281,1283,1289,1294,1300,1304,1307,1308,1310,1314,1315,1319,1325,1334,1350,1353,1359,1362,1370,1371,1373,1375,1381,1399,1404,1415,1416,1419,1420,1425,1426,1435,1436,1437,1441,1444,1448,1458,1461,1462,1467,1470,1479,1486,1489,1494,1496,1497,1513,1519,1528,1529,1538,1549,1551,1553,1555,1567,1580,1583,1595,1601,1603,1613,1614,1616,1625,1637,1638,1639,1640,1643,1653,1654,1655,1658,1667,1672,1674,1683,1692,1700,1706,1709,1712,1714,1715,1717,1726,1733,1750,1756,1758,1759,1771,1778,1781,1783,1789,1797,1800,1801,1816,1823,1828,1831,1840,1847,1852,1858,1872,1873,1874,1876,1893,1894,1895,1900,1905,1912,1918,1923,1930,1940,1944,1949,1952,1953,1958,1965,1966,1968,1972,1974,1975,1994,2005,2011,2020,2028,2037,2038,2043,2046,2050,2051,2058,2059,2062,2063,2064,2074,2077,2078,2082,2085,2094,2104,2110,2112,2121,2122,2161,2167,2169,2172,2175,2180,2184,2186,2192,2197,2205,2210,2215,2224,2225,2233,2234,2236,2239,2240,2243,2257,2266,2273,2285,2294,2299,2302,2305,2307,2310,2315]

cardano-db/src/Cardano/Db/Statement/Pool.hs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,53 @@ insertBulkPoolStat :: [SCP.PoolStat] -> DbM ()
208208
insertBulkPoolStat poolStats =
209209
runSession mkDbCallStack $ HsqlSes.statement poolStats insertBulkPoolStatStmt
210210

211+
--------------------------------------------------------------------------------
212+
queryPoolStatCountStmt :: HsqlStmt.Statement () Int64
213+
queryPoolStatCountStmt =
214+
HsqlStmt.Statement sql encoder decoder True
215+
where
216+
poolStatTable = tableName (Proxy @SCP.PoolStat)
217+
218+
sql =
219+
TextEnc.encodeUtf8 $
220+
Text.concat
221+
[ "SELECT COUNT(*)::bigint FROM "
222+
, poolStatTable
223+
]
224+
225+
encoder = mempty
226+
decoder = HsqlD.singleRow (HsqlD.column $ HsqlD.nonNullable HsqlD.int8)
227+
228+
queryPoolStatCount :: DbM Int64
229+
queryPoolStatCount =
230+
runSession mkDbCallStack $ HsqlSes.statement () queryPoolStatCountStmt
231+
232+
--------------------------------------------------------------------------------
233+
queryPoolStatDuplicatesStmt :: HsqlStmt.Statement () Int64
234+
queryPoolStatDuplicatesStmt =
235+
HsqlStmt.Statement sql encoder decoder True
236+
where
237+
poolStatTable = tableName (Proxy @SCP.PoolStat)
238+
239+
sql =
240+
TextEnc.encodeUtf8 $
241+
Text.concat
242+
[ "SELECT COUNT(*)::bigint FROM ("
243+
, " SELECT pool_hash_id, epoch_no"
244+
, " FROM "
245+
, poolStatTable
246+
, " GROUP BY pool_hash_id, epoch_no"
247+
, " HAVING COUNT(*) > 1"
248+
, ") AS duplicates"
249+
]
250+
251+
encoder = mempty
252+
decoder = HsqlD.singleRow (HsqlD.column $ HsqlD.nonNullable HsqlD.int8)
253+
254+
queryPoolStatDuplicates :: DbM Int64
255+
queryPoolStatDuplicates =
256+
runSession mkDbCallStack $ HsqlSes.statement () queryPoolStatDuplicatesStmt
257+
211258
--------------------------------------------------------------------------------
212259
-- PoolOwner
213260
--------------------------------------------------------------------------------
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CREATE FUNCTION migrate() RETURNS void AS $$
2+
DECLARE
3+
next_version int ;
4+
BEGIN
5+
SELECT stage_two + 1 INTO next_version FROM schema_version ;
6+
IF next_version = 45 THEN
7+
8+
-- Remove duplicates
9+
DELETE FROM pool_stat
10+
WHERE id NOT IN (
11+
SELECT DISTINCT ON (pool_hash_id, epoch_no) id
12+
FROM pool_stat
13+
ORDER BY pool_hash_id, epoch_no, id
14+
);
15+
16+
UPDATE schema_version SET stage_two = next_version ;
17+
RAISE NOTICE 'DB has been migrated to stage_two version %', next_version ;
18+
END IF ;
19+
END ;
20+
$$ LANGUAGE plpgsql ;
21+
22+
SELECT migrate() ;
23+
DROP FUNCTION migrate() ;

0 commit comments

Comments
 (0)