Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class DbAppActivityRecordStore(
select 1
from #${Tables.appActivityRecords} a
join #${Tables.verdicts} v on a.verdict_row_id = v.row_id
where a.round_number = sub.min_round + 1
where a.round_number > sub.min_round
and v.history_id = $historyId
)
""".as[Option[Long]].headOption.map(_.flatten),
Expand All @@ -109,10 +109,10 @@ class DbAppActivityRecordStore(
}

/** Find the latest round with complete app activity.
* A round is complete when ingestion has moved past it, i.e., the next
* A round is complete when ingestion has moved past it, i.e., a later
* round also has records. We return max_round - 1 because max_round
* itself may still be receiving records.
* Returns None if fewer than two consecutive rounds have been ingested.
* Returns None if fewer than two distinct rounds have been ingested.
*/
def latestRoundWithCompleteAppActivity()(implicit
tc: TraceContext
Expand All @@ -130,17 +130,17 @@ class DbAppActivityRecordStore(
select 1
from #${Tables.appActivityRecords} a
join #${Tables.verdicts} v on a.verdict_row_id = v.row_id
where a.round_number = sub.max_round - 1
where a.round_number < sub.max_round
and v.history_id = $historyId
)
""".as[Option[Long]].headOption.map(_.flatten),
"appActivity.latestRoundWithCompleteAppActivity",
)
}

/** Assert that activity records exist for rounds roundNumber - 1 and
* roundNumber + 1, proving ingestion completeness for roundNumber.
* Round N-1 proves ingestion was running before N; round N+1 proves
/** Assert that activity records exist for a round before and a round after
* roundNumber, proving ingestion completeness for roundNumber.
* A prior round proves ingestion was running before N; a later round proves
* ingestion has moved past N, so all of N's records have been ingested.
*/
def assertCompleteActivity(roundNumber: Long)(implicit
Expand All @@ -152,13 +152,13 @@ class DbAppActivityRecordStore(
hasPrev <- sql"""select exists(
select 1 from #${Tables.appActivityRecords} a
join #${Tables.verdicts} v on a.verdict_row_id = v.row_id
where a.round_number = ${roundNumber - 1}
where a.round_number < $roundNumber
and v.history_id = $historyId
)""".as[Boolean].head
hasNext <- sql"""select exists(
select 1 from #${Tables.appActivityRecords} a
join #${Tables.verdicts} v on a.verdict_row_id = v.row_id
where a.round_number = ${roundNumber + 1}
where a.round_number > $roundNumber
and v.history_id = $historyId
)""".as[Boolean].head
_ = if (!hasPrev || !hasNext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,14 @@ class DbAppActivityRecordStoreTest
}
}

"return None when rounds are not consecutive" in {
// #5186: round with zero activity between non-zero rounds should not block progress
"treat a zero-activity round between non-zero rounds as complete" in {
for {
(store, historyId) <- newStore()
baseTs = CantonTimestamp.now()
rowId1 <- insertVerdictRow(historyId, baseTs, "update-gap-10")
rowId2 <- insertVerdictRow(historyId, baseTs.plusSeconds(1L), "update-gap-12")
// Round 10 and 12 have activity, round 11 has zero activity (no records)
_ <- store.insertAppActivityRecords(
Seq(
mkRecord(rowId1, 10L, Seq("app1::provider"), Seq(100L)),
Expand All @@ -333,8 +335,10 @@ class DbAppActivityRecordStoreTest
)
result <- store.earliestRoundWithCompleteAppActivity()
} yield {
// No round has a prior round with records (11 is missing)
result shouldBe None
// Round 11 has zero activity but is bounded by 10 and 12, so 11 is complete.
// Round 12 is also complete (activity exists for a prior round: 10).
// Earliest complete should be 11.
result.value shouldBe 11L
}
}

Expand Down Expand Up @@ -447,12 +451,14 @@ class DbAppActivityRecordStoreTest
}
}

"return None when rounds are not consecutive" in {
// #5186: round with zero activity between non-zero rounds should not block progress
"treat a zero-activity round between non-zero rounds as complete" in {
for {
(store, historyId) <- newStore()
baseTs = CantonTimestamp.now()
rowId1 <- insertVerdictRow(historyId, baseTs, "update-gap-10")
rowId2 <- insertVerdictRow(historyId, baseTs.plusSeconds(1L), "update-gap-12")
// Round 10 and 12 have activity, round 11 has zero activity (no records)
_ <- store.insertAppActivityRecords(
Seq(
mkRecord(rowId1, 10L, Seq("app1::provider"), Seq(100L)),
Expand All @@ -461,7 +467,10 @@ class DbAppActivityRecordStoreTest
)
result <- store.latestRoundWithCompleteAppActivity()
} yield {
result shouldBe None
// Round 12 is the max round. Round 11 has zero activity but is bounded
// by rounds with data, so it is complete. Latest complete should be 11
// (12 cannot be confirmed complete without seeing round 13).
result.value shouldBe 11L
}
}

Expand Down
Loading