@@ -6,6 +6,7 @@ import com.digitalasset.canton.concurrent.FutureSupervisor
66import com .digitalasset .canton .crypto .Fingerprint
77import com .digitalasset .canton .data .CantonTimestamp
88import com .digitalasset .canton .lifecycle .FutureUnlessShutdown
9+ import com .digitalasset .canton .logging .SuppressionRule
910import com .digitalasset .canton .resource .DbStorage
1011import com .digitalasset .canton .topology .*
1112import com .digitalasset .canton .tracing .TraceContext
@@ -58,6 +59,7 @@ import scala.jdk.OptionConverters.*
5859import scala .math .BigDecimal .javaBigDecimal2bigDecimal
5960import scala .reflect .ClassTag
6061import org .lfdecentralizedtrust .splice .config .IngestionConfig
62+ import org .slf4j .event .Level
6163
6264abstract class ScanStoreTest
6365 extends StoreTest
@@ -812,125 +814,8 @@ abstract class ScanStoreTest
812814 " listVoteRequestResults" should {
813815
814816 " list all past VoteRequestResult" in {
815- for {
816- store <- mkStore()
817- voteRequestContract1 = voteRequest(
818- requester = userParty(1 ),
819- votes = (1 to 4 )
820- .map(n =>
821- new Vote (
822- userParty(n).toProtoPrimitive,
823- true ,
824- new Reason (" " , " " ),
825- Optional .empty(),
826- )
827- ),
828- )
829- _ <- dummyDomain.create(voteRequestContract1)(store.multiDomainAcsStore)
830- result1 = mkVoteRequestResult(
831- voteRequestContract1
832- )
833- _ <- dummyDomain.exercise(
834- contract = dsoRules(dsoParty),
835- interfaceId = Some (DsoRules .TEMPLATE_ID_WITH_PACKAGE_ID ),
836- choiceName = DsoRulesCloseVoteRequest .choice.name,
837- mkCloseVoteRequest(
838- voteRequestContract1.contractId
839- ),
840- result1.toValue,
841- )(
842- store.multiDomainAcsStore
843- )
844- voteRequestContract2 = voteRequest(
845- requester = userParty(2 ),
846- votes = (1 to 4 )
847- .map(n =>
848- new Vote (
849- userParty(n).toProtoPrimitive,
850- true ,
851- new Reason (" " , " " ),
852- Optional .empty(),
853- )
854- ),
855- )
856- _ <- dummyDomain.create(voteRequestContract2)(store.multiDomainAcsStore)
857- result2 = mkVoteRequestResult(
858- voteRequestContract2,
859- effectiveAt = Instant .now().plusSeconds(1 ).truncatedTo(ChronoUnit .MICROS ),
860- )
861- _ <- dummyDomain.exercise(
862- contract = dsoRules(dsoParty),
863- interfaceId = Some (DsoRules .TEMPLATE_ID_WITH_PACKAGE_ID ),
864- choiceName = DsoRulesCloseVoteRequest .choice.name,
865- mkCloseVoteRequest(
866- voteRequestContract2.contractId
867- ),
868- result2.toValue,
869- )(
870- store.multiDomainAcsStore
871- )
872- } yield {
873- store
874- .listVoteRequestResults(
875- Some (" AddSv" ),
876- Some (true ),
877- None ,
878- None ,
879- None ,
880- PageLimit .tryCreate(1 ),
881- )
882- .futureValue
883- .toList
884- .loneElement shouldBe result2
885- store
886- .listVoteRequestResults(
887- Some (" SRARC_AddSv" ),
888- Some (false ),
889- None ,
890- None ,
891- None ,
892- PageLimit .tryCreate(1 ),
893- )
894- .futureValue
895- .toList
896- .size shouldBe (0 )
897- store
898- .listVoteRequestResults(
899- None ,
900- None ,
901- None ,
902- None ,
903- None ,
904- PageLimit .tryCreate(1 ),
905- )
906- .futureValue
907- .toList
908- .size shouldBe (1 )
909- store
910- .listVoteRequestResults(
911- None ,
912- None ,
913- None ,
914- Some (Instant .now().truncatedTo(ChronoUnit .MICROS ).plusSeconds(3600 ).toString),
915- None ,
916- PageLimit .tryCreate(1 ),
917- )
918- .futureValue
919- .toList
920- .size shouldBe (0 )
921- store
922- .listVoteRequestResults(
923- None ,
924- None ,
925- None ,
926- Some (Instant .now().truncatedTo(ChronoUnit .MICROS ).minusSeconds(3600 ).toString),
927- None ,
928- PageLimit .tryCreate(1 ),
929- )
930- .futureValue
931- .toList
932- .size shouldBe (1 )
933- }
817+ val store = mkStore().futureValue
818+ assertListOfAllPastVoteRequestResults(store)
934819 }
935820 }
936821
@@ -1497,8 +1382,133 @@ abstract class ScanStoreTest
14971382 }
14981383 }
14991384
1385+ def assertListOfAllPastVoteRequestResults (store : ScanStore ) = {
1386+ val voteRequestContract1 = voteRequest(
1387+ requester = userParty(1 ),
1388+ votes = (1 to 4 )
1389+ .map(n =>
1390+ new Vote (
1391+ userParty(n).toProtoPrimitive,
1392+ true ,
1393+ new Reason (" " , " " ),
1394+ Optional .empty(),
1395+ )
1396+ ),
1397+ )
1398+
1399+ for {
1400+ _ <- dummyDomain.create(voteRequestContract1)(store.multiDomainAcsStore)
1401+ result1 = mkVoteRequestResult(
1402+ voteRequestContract1
1403+ )
1404+ _ <- dummyDomain.exercise(
1405+ contract = dsoRules(dsoParty),
1406+ interfaceId = Some (DsoRules .TEMPLATE_ID_WITH_PACKAGE_ID ),
1407+ choiceName = DsoRulesCloseVoteRequest .choice.name,
1408+ mkCloseVoteRequest(
1409+ voteRequestContract1.contractId
1410+ ),
1411+ result1.toValue,
1412+ )(
1413+ store.multiDomainAcsStore
1414+ )
1415+ voteRequestContract2 = voteRequest(
1416+ requester = userParty(2 ),
1417+ votes = (1 to 4 )
1418+ .map(n =>
1419+ new Vote (
1420+ userParty(n).toProtoPrimitive,
1421+ true ,
1422+ new Reason (" " , " " ),
1423+ Optional .empty(),
1424+ )
1425+ ),
1426+ )
1427+ _ <- dummyDomain.create(voteRequestContract2)(store.multiDomainAcsStore)
1428+ result2 = mkVoteRequestResult(
1429+ voteRequestContract2,
1430+ effectiveAt = Instant .now().plusSeconds(1 ).truncatedTo(ChronoUnit .MICROS ),
1431+ )
1432+ _ <- dummyDomain.exercise(
1433+ contract = dsoRules(dsoParty),
1434+ interfaceId = Some (DsoRules .TEMPLATE_ID_WITH_PACKAGE_ID ),
1435+ choiceName = DsoRulesCloseVoteRequest .choice.name,
1436+ mkCloseVoteRequest(
1437+ voteRequestContract2.contractId
1438+ ),
1439+ result2.toValue,
1440+ )(
1441+ store.multiDomainAcsStore
1442+ )
1443+ } yield {
1444+ store
1445+ .listVoteRequestResults(
1446+ Some (" AddSv" ),
1447+ Some (true ),
1448+ None ,
1449+ None ,
1450+ None ,
1451+ PageLimit .tryCreate(1 ),
1452+ )
1453+ .futureValue
1454+ .toList
1455+ .loneElement shouldBe result2
1456+ store
1457+ .listVoteRequestResults(
1458+ Some (" SRARC_AddSv" ),
1459+ Some (false ),
1460+ None ,
1461+ None ,
1462+ None ,
1463+ PageLimit .tryCreate(1 ),
1464+ )
1465+ .futureValue
1466+ .toList
1467+ .size shouldBe (0 )
1468+ store
1469+ .listVoteRequestResults(
1470+ None ,
1471+ None ,
1472+ None ,
1473+ None ,
1474+ None ,
1475+ PageLimit .tryCreate(1 ),
1476+ )
1477+ .futureValue
1478+ .toList
1479+ .size shouldBe (1 )
1480+ store
1481+ .listVoteRequestResults(
1482+ None ,
1483+ None ,
1484+ None ,
1485+ Some (Instant .now().truncatedTo(ChronoUnit .MICROS ).plusSeconds(3600 ).toString),
1486+ None ,
1487+ PageLimit .tryCreate(1 ),
1488+ )
1489+ .futureValue
1490+ .toList
1491+ .size shouldBe (0 )
1492+ store
1493+ .listVoteRequestResults(
1494+ None ,
1495+ None ,
1496+ None ,
1497+ Some (Instant .now().truncatedTo(ChronoUnit .MICROS ).minusSeconds(3600 ).toString),
1498+ None ,
1499+ PageLimit .tryCreate(1 ),
1500+ )
1501+ .futureValue
1502+ .toList
1503+ .size shouldBe (1 )
1504+ }
1505+ }
1506+
15001507 protected def mkStore (
1501- dsoParty : PartyId = dsoParty
1508+ dsoParty : PartyId = dsoParty,
1509+ acsStoreDescriptorUserVersion : Option [Long ] = None ,
1510+ txLogStoreDescriptorUserVersion : Option [Long ] = None ,
1511+ skipIngestAcs : Boolean = false ,
15021512 ): Future [ScanStore ]
15031513
15041514 protected def mkUpdateHistory (
@@ -1923,7 +1933,10 @@ class DbScanStoreTest
19231933 with AcsTables {
19241934
19251935 override protected def mkStore (
1926- dsoParty : PartyId
1936+ dsoParty : PartyId ,
1937+ acsStoreDescriptorUserVersion : Option [Long ] = None ,
1938+ txLogStoreDescriptorUserVersion : Option [Long ] = None ,
1939+ skipIngestAcs : Boolean = false ,
19271940 ): Future [ScanStore ] = {
19281941 val packageSignatures =
19291942 ResourceTemplateDecoder .loadPackageSignaturesFromResources(
@@ -1958,12 +1971,17 @@ class DbScanStoreTest
19581971 IngestionConfig (),
19591972 new DbScanStoreMetrics (new NoOpMetricsFactory (), loggerFactory, timeouts),
19601973 initialRound = 0 ,
1974+ acsStoreDescriptorUserVersion,
1975+ txLogStoreDescriptorUserVersion,
19611976 )(parallelExecutionContext, implicitly, implicitly)
19621977
19631978 for {
19641979 _ <- store.multiDomainAcsStore.testIngestionSink.initialize()
1965- _ <- store.multiDomainAcsStore.testIngestionSink
1966- .ingestAcs(nextOffset(), Seq .empty, Seq .empty, Seq .empty)
1980+ _ <-
1981+ if (! skipIngestAcs) {
1982+ store.multiDomainAcsStore.testIngestionSink
1983+ .ingestAcs(nextOffset(), Seq .empty, Seq .empty, Seq .empty)
1984+ } else Future .unit
19671985 _ <- store.domains.ingestionSink.ingestConnectedDomains(
19681986 Map (SynchronizerAlias .tryCreate(domain) -> dummyDomain)
19691987 )
@@ -2069,4 +2087,77 @@ class DbScanStoreTest
20692087 }
20702088 }
20712089 }
2090+ " Changing the acsStoreDescriptorUserVersion" should {
2091+ val alice = userParty(443 )
2092+ val aliceValidatorLicense = validatorLicense(
2093+ alice,
2094+ dsoParty,
2095+ Some (new FaucetState (new Round (0 ), new Round (1000 ), 0L )),
2096+ )
2097+
2098+ " force re-ingestion of acs" in {
2099+ for {
2100+ // create store, ingest an update with aliceValidatorLicense
2101+ store <- mkStore()
2102+ _ <- dummyDomain.create(aliceValidatorLicense)(store.multiDomainAcsStore)
2103+ result <- store.getValidatorLicenseByValidator(
2104+ Vector (alice)
2105+ )
2106+ // create store again but now with new storeDescriptor userVersion, ingest an update with aliceValidatorLicense again
2107+ storeReingest <- mkStore(dsoParty = dsoParty, acsStoreDescriptorUserVersion = Some (1L ))
2108+ // ingestUpdate would fail on the same storeId for aliceValidatorLicense without a new user version.
2109+ _ <- dummyDomain.create(aliceValidatorLicense)(storeReingest.multiDomainAcsStore)
2110+ resultAfter <- storeReingest.getValidatorLicenseByValidator(
2111+ Vector (alice)
2112+ )
2113+ } yield {
2114+ result should contain(aliceValidatorLicense)
2115+ resultAfter should contain(aliceValidatorLicense)
2116+ }
2117+ }
2118+ }
2119+
2120+ " Changing the txLogStoreDescriptorUserVersion" should {
2121+ " force re-ingestion of txLog" in {
2122+ for {
2123+ store <- mkStore()
2124+ _ <- assertListOfAllPastVoteRequestResults(store)
2125+ // create the store with a new txLogStoreDescriptorUserVersion and check that it logs that TxLog backfilling will start restoring previous entries
2126+ storeReingest <- loggerFactory.assertLogsSeq(SuppressionRule .Level (Level .INFO ))(
2127+ {
2128+ mkStore(
2129+ dsoParty = dsoParty,
2130+ txLogStoreDescriptorUserVersion = Some (1L ),
2131+ // not calling ingestAcs in this test code cause that fails because finishedAcsIngestion completing when it identifies
2132+ // that StoreHasData(acsStoreId, acsOffset) and StoreHasNoData(txLogStoreId)
2133+ skipIngestAcs = true ,
2134+ )
2135+ },
2136+ lines => {
2137+ forAll(lines) { line =>
2138+ line.message should include(
2139+ s " has not ingested any data, presumably because it was reset. "
2140+ )
2141+ line.message should include(
2142+ " TxLog backfilling will start restoring previous entries."
2143+ )
2144+ }
2145+ },
2146+ )
2147+ } yield {
2148+ // new store should not have txLog entry from previous store
2149+ storeReingest
2150+ .listVoteRequestResults(
2151+ Some (" AddSv" ),
2152+ Some (true ),
2153+ None ,
2154+ None ,
2155+ None ,
2156+ PageLimit .tryCreate(1 ),
2157+ )
2158+ .futureValue
2159+ .toList should have size 0
2160+ }
2161+ }
2162+ }
20722163}
0 commit comments