Skip to content

Commit 0f96bb8

Browse files
authored
add dso-visible tps stats to bigquery (#2100)
Signed-off-by: Itai Segall <itai.segall@digitalasset.com>
1 parent 4947290 commit 0f96bb8

File tree

3 files changed

+93
-11
lines changed

3 files changed

+93
-11
lines changed

apps/app/src/test/scala/org/lfdecentralizedtrust/splice/integration/tests/ScanTotalSupplyBigQueryIntegrationTest.scala

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import scala.concurrent.Future
2323
import scala.jdk.CollectionConverters.*
2424
import scala.jdk.DurationConverters.*
2525
import scala.sys.process.Process
26+
import java.time.temporal.ChronoUnit
2627

2728
class ScanTotalSupplyBigQueryIntegrationTest
2829
extends SpliceTests.IntegrationTest
@@ -69,6 +70,12 @@ class ScanTotalSupplyBigQueryIntegrationTest
6970
private val unmintedAmount = BigDecimal("570776.255709163")
7071
private val amuletHolders = 5
7172
private val validators = 4 // one SV + 3 validators
73+
// The test currently produces 80 transactions, which is 0.000926 tps over 24 hours,
74+
// so we assert for a range of 70-85 transactions, or 0.0008-0.00099 tps.
75+
private val avgTps = (0.0008, 0.00099)
76+
// The peak is 17 transactions in a (simulated) minute, or 0.28333 tps over a minute,
77+
// so we assert 15-20 transactions, or 0.25-0.34 tps
78+
private val peakTps = (0.25, 0.34)
7279

7380
override def beforeAll() = {
7481
super.beforeAll()
@@ -501,7 +508,8 @@ class ScanTotalSupplyBigQueryIntegrationTest
501508
*/
502509
private def runTotalSupplyQueries()(implicit env: FixtureParam): ExpectedMetrics = {
503510
val project = bigquery.getOptions.getProjectId
504-
val timestamp = getLedgerTime.toInstant.plusSeconds(10).toString
511+
// The TPS query assumes staleness of up to 4 hours, so we query for stats 5 hours after the current ledger time.
512+
val timestamp = getLedgerTime.toInstant.plus(5, ChronoUnit.HOURS).toString
505513
val sql =
506514
s"SELECT * FROM `$project.$functionsDatasetName.all_stats`('$timestamp', 0);"
507515

@@ -536,29 +544,32 @@ class ScanTotalSupplyBigQueryIntegrationTest
536544
burned: BigDecimal,
537545
numAmuletHolders: Long,
538546
numActiveValidators: Long,
547+
avgTps: Double,
548+
peakTps: Double,
539549
)
540550

541551
private def parseQueryResults(result: bq.TableResult) = {
542552
// We expect the final query to return a single row with all metrics
543553
val row = result.iterateAll().iterator().next()
544554
logger.debug(s"Query row: $row; schema ${result.getSchema}")
545555

546-
def bd(column: String) = {
556+
def required(column: String) = {
547557
val field = row get column
548558
if (field.isNull)
549559
fail(s"Column '$column' in all-stats results is null")
550-
else
551-
BigDecimal(field.getStringValue)
560+
field
561+
}
562+
563+
def bd(column: String) = {
564+
BigDecimal(required(column).getStringValue)
552565
}
553566

554567
def int(column: String) = {
555-
val field = row get column
556-
if (field.isNull)
557-
fail(s"Column '$column' in all-stats results is null")
558-
else {
559-
field.getLongValue
560-
}
568+
required(column).getLongValue
569+
}
561570

571+
def float(column: String) = {
572+
required(column).getDoubleValue
562573
}
563574

564575
ExpectedMetrics(
@@ -571,6 +582,8 @@ class ScanTotalSupplyBigQueryIntegrationTest
571582
burned = bd("burned"),
572583
numAmuletHolders = int("num_amulet_holders"),
573584
numActiveValidators = int("num_active_validators"),
585+
avgTps = float("average_tps"),
586+
peakTps = float("peak_tps"),
574587
)
575588
}
576589

@@ -594,6 +607,16 @@ class ScanTotalSupplyBigQueryIntegrationTest
594607
actual shouldBe expected withClue clue
595608
}
596609

610+
forEvery(
611+
Seq(
612+
("average_tps", results.avgTps, avgTps),
613+
("peak_tps", results.peakTps, peakTps),
614+
)
615+
) { case (clue, actual, expected) =>
616+
actual shouldBe >=(expected._1) withClue clue
617+
actual shouldBe <=(expected._2) withClue clue
618+
}
619+
597620
// other derived metrics
598621
(mintedAmount - burnedAmount) shouldBe (
599622
lockedAmount + unlockedAmount

cluster/pulumi/canton-network/src/bigQuery_functions.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
BQFunctionArgument,
99
BQScalarFunction,
1010
BQTableFunction,
11+
FLOAT64,
1112
INT64,
1213
json,
1314
STRING,
@@ -448,6 +449,56 @@ const num_active_validators = new BQScalarFunction(
448449
`
449450
);
450451

452+
const one_day_updates = new BQTableFunction(
453+
'one_day_updates',
454+
as_of_args,
455+
[new BQColumn('update_id', STRING), new BQColumn('record_time', INT64)],
456+
`
457+
-- All update IDs over a period of 24 hours. Since the data might be up to 4 hours stale, we take a window of 4-28 hours ago.
458+
SELECT DISTINCT(update_id), record_time FROM (
459+
SELECT
460+
update_id,
461+
record_time
462+
FROM \`$$SCAN_DATASET$$.scan_sv_1_update_history_exercises\` e
463+
WHERE \`$$FUNCTIONS_DATASET$$.in_time_window\`(TIMESTAMP_SUB(as_of_record_time, INTERVAL 28 HOUR), 0, TIMESTAMP_SUB(as_of_record_time, INTERVAL 4 HOUR), migration_id, e.record_time, e.migration_id)
464+
UNION ALL
465+
SELECT
466+
update_id,
467+
record_time
468+
FROM \`$$SCAN_DATASET$$.scan_sv_1_update_history_creates\` c
469+
WHERE \`$$FUNCTIONS_DATASET$$.in_time_window\`(TIMESTAMP_SUB(as_of_record_time, INTERVAL 28 HOUR), 0, TIMESTAMP_SUB(as_of_record_time, INTERVAL 4 HOUR), migration_id, c.record_time, c.migration_id)
470+
)
471+
`
472+
);
473+
474+
const average_tps = new BQScalarFunction(
475+
'average_tps',
476+
as_of_args,
477+
FLOAT64,
478+
`
479+
(SELECT COUNT(*) / 86400.0
480+
FROM \`$$FUNCTIONS_DATASET$$.one_day_updates\`(as_of_record_time, migration_id)
481+
)
482+
`
483+
);
484+
485+
const peak_tps = new BQScalarFunction(
486+
'peak_tps',
487+
as_of_args,
488+
FLOAT64,
489+
`
490+
(SELECT
491+
MAX(events_per_minute) / 60.0
492+
FROM (
493+
SELECT
494+
COUNT(*) as events_per_minute
495+
FROM \`$$FUNCTIONS_DATASET$$.one_day_updates\`(as_of_record_time, migration_id)
496+
GROUP BY TIMESTAMP_TRUNC(TIMESTAMP_MICROS(record_time), MINUTE)
497+
)
498+
)
499+
`
500+
);
501+
451502
const all_stats = new BQTableFunction(
452503
'all_stats',
453504
as_of_args,
@@ -463,6 +514,8 @@ const all_stats = new BQTableFunction(
463514
new BQColumn('burned', BIGNUMERIC),
464515
new BQColumn('num_amulet_holders', INT64),
465516
new BQColumn('num_active_validators', INT64),
517+
new BQColumn('average_tps', FLOAT64),
518+
new BQColumn('peak_tps', FLOAT64),
466519
],
467520
`
468521
SELECT
@@ -476,7 +529,9 @@ const all_stats = new BQTableFunction(
476529
\`$$FUNCTIONS_DATASET$$.minted\`(as_of_record_time, migration_id) + \`$$FUNCTIONS_DATASET$$.unminted\`(as_of_record_time, migration_id) as allowed_mint,
477530
\`$$FUNCTIONS_DATASET$$.burned\`(as_of_record_time, migration_id) as burned,
478531
\`$$FUNCTIONS_DATASET$$.num_amulet_holders\`(as_of_record_time, migration_id) as num_amulet_holders,
479-
\`$$FUNCTIONS_DATASET$$.num_active_validators\`(as_of_record_time, migration_id) as num_active_validators
532+
\`$$FUNCTIONS_DATASET$$.num_active_validators\`(as_of_record_time, migration_id) as num_active_validators,
533+
\`$$FUNCTIONS_DATASET$$.average_tps\`(as_of_record_time, migration_id) as average_tps,
534+
\`$$FUNCTIONS_DATASET$$.peak_tps\`(as_of_record_time, migration_id) as peak_tps
480535
`
481536
);
482537

@@ -501,5 +556,8 @@ export const allFunctions = [
501556
num_amulet_holders,
502557
all_validators,
503558
num_active_validators,
559+
one_day_updates,
560+
average_tps,
561+
peak_tps,
504562
all_stats,
505563
];

cluster/pulumi/canton-network/src/bigQuery_functions_types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const TIMESTAMP = new BQBasicType('TIMESTAMP');
3131
export const BIGNUMERIC = new BQBasicType('BIGNUMERIC');
3232
export const json = new BQBasicType('JSON'); // JSON is a reserved word in TypeScript
3333
export const BOOL = new BQBasicType('BOOL');
34+
export const FLOAT64 = new BQBasicType('FLOAT64');
3435

3536
export class BQArray extends BQType {
3637
private readonly elementType: BQType;

0 commit comments

Comments
 (0)