From b141b126fcf96cdf2a56a7a3ef12beff10fb108b Mon Sep 17 00:00:00 2001 From: Thibault Jeandet Date: Fri, 1 May 2026 14:06:04 +0000 Subject: [PATCH 1/3] Add physical synchronizer id to periodic topology snapshot metadata --- .../PeriodicTopologySnapshotTrigger.scala | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala index b5da8e74f9..731efe86be 100644 --- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala +++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala @@ -7,7 +7,7 @@ import com.daml.grpc.adapter.ExecutionSequencerFactory import com.digitalasset.canton.SynchronizerAlias import com.digitalasset.canton.data.CantonTimestamp import com.digitalasset.canton.time.Clock -import com.digitalasset.canton.topology.SynchronizerId +import com.digitalasset.canton.topology.{PhysicalSynchronizerId, SynchronizerId} import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId import com.digitalasset.canton.tracing.TraceContext import io.grpc.{Status, StatusRuntimeException} @@ -23,7 +23,7 @@ import org.lfdecentralizedtrust.splice.sv.LocalSynchronizerNode import org.lfdecentralizedtrust.splice.util.BackupDump import java.nio.file.Paths -import scala.concurrent.{blocking, ExecutionContext, Future} +import scala.concurrent.{ExecutionContext, Future, blocking} import scala.util.{Failure, Success} /** As taking a topology snapshot is not a cheap operation, we limit this trigger to produce at most one snapshot per day. @@ -46,12 +46,12 @@ class PeriodicTopologySnapshotTrigger( task: PeriodicTaskTrigger.PeriodicTask )(implicit traceContext: TraceContext): Future[TaskOutcome] = { participantAdminConnection - .getSynchronizerId(synchronizerAlias) + .getPhysicalSynchronizerId(synchronizerAlias) .transformWith { case Failure(s: StatusRuntimeException) if s.getStatus.getCode == Status.Code.NOT_FOUND => Future.successful(TaskNoop) case Failure(e) => Future.failed(e) - case Success(synchronizerId) => + case Success(physicalSynchronizerId) => val now = clock.now val utcDate = now.toInstant.toString.split("T").head val folderName = s"topology_snapshot_${now.toInstant}" @@ -63,7 +63,7 @@ class PeriodicTopologySnapshotTrigger( folderName, now, utcDate, - synchronizerId, + physicalSynchronizerId, ) else Future.successful(TaskSuccess("Today's topology snapshot already exists.")) } yield res @@ -83,7 +83,7 @@ class PeriodicTopologySnapshotTrigger( folderName: String, now: CantonTimestamp, utcDate: String, - synchronizerId: SynchronizerId, + physicalSynchronizerId: PhysicalSynchronizerId, )(implicit traceContext: TraceContext, esf: ExecutionSequencerFactory, @@ -110,13 +110,18 @@ class PeriodicTopologySnapshotTrigger( authorizedStore <- sequencerAdminConnection.exportAuthorizedStoreSnapshot(sequencerId.uid) // list a summary of the transactions state at the time of the snapshot to validate further imports summary <- sequencerAdminConnection.getTopologyTransactionsSummary( - TopologyStoreId.Synchronizer(synchronizerId), + TopologyStoreId.Synchronizer(physicalSynchronizerId.logical), clock.now, ) // we create a single metadata file to store the amounts of the different transactions along the sequencerId - metadata = summary.map(e => - (e._1.code, e._2.toString) - ) + ("sequencerId" -> sequencerId.toProtoPrimitive) + metadataMap = summary.map(e => (e._1.code, e._2.toString)) + + ("sequencerId" -> sequencerId.toProtoPrimitive) + + ("physicalSynchronizerId" -> physicalSynchronizerId.toProtoPrimitive) + metadataKv = metadataMap + .map { case (key, value) => + s"$key,$value" + } + .mkString("\n") _ <- Future { blocking { val fileDesc = @@ -132,7 +137,7 @@ class PeriodicTopologySnapshotTrigger( BackupDump.write( config.location, Paths.get(s"$folderName/metadata"), - metadata.toString(), + metadataKv, loggerFactory, ), ) From 84581842b09b4ecdee940b19069b2c0e905d0a3d Mon Sep 17 00:00:00 2001 From: Thibault Jeandet Date: Tue, 5 May 2026 08:00:54 +0000 Subject: [PATCH 2/3] Make it JSON --- .../automation/PeriodicTopologySnapshotTrigger.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala index 731efe86be..ea1e0432ac 100644 --- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala +++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala @@ -10,6 +10,8 @@ import com.digitalasset.canton.time.Clock import com.digitalasset.canton.topology.{PhysicalSynchronizerId, SynchronizerId} import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId import com.digitalasset.canton.tracing.TraceContext +import io.circe.Json +import io.circe.syntax.* import io.grpc.{Status, StatusRuntimeException} import io.opentelemetry.api.trace.Tracer import org.apache.pekko.actor.ActorSystem @@ -117,11 +119,9 @@ class PeriodicTopologySnapshotTrigger( metadataMap = summary.map(e => (e._1.code, e._2.toString)) + ("sequencerId" -> sequencerId.toProtoPrimitive) + ("physicalSynchronizerId" -> physicalSynchronizerId.toProtoPrimitive) - metadataKv = metadataMap - .map { case (key, value) => - s"$key,$value" - } - .mkString("\n") + metadataJson = Json + .obj(metadataMap.map { case (k, v) => k -> Json.fromString(v) }.toSeq*) + .spaces2 _ <- Future { blocking { val fileDesc = @@ -137,7 +137,7 @@ class PeriodicTopologySnapshotTrigger( BackupDump.write( config.location, Paths.get(s"$folderName/metadata"), - metadataKv, + metadataJson, loggerFactory, ), ) From fa7e2c3a455008a6ce45feda06d99e8e445e3149 Mon Sep 17 00:00:00 2001 From: Thibault Jeandet Date: Tue, 5 May 2026 09:03:53 +0000 Subject: [PATCH 3/3] Fix imports --- .../splice/sv/automation/PeriodicTopologySnapshotTrigger.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala index ea1e0432ac..ca5c9fb261 100644 --- a/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala +++ b/apps/sv/src/main/scala/org/lfdecentralizedtrust/splice/sv/automation/PeriodicTopologySnapshotTrigger.scala @@ -7,11 +7,10 @@ import com.daml.grpc.adapter.ExecutionSequencerFactory import com.digitalasset.canton.SynchronizerAlias import com.digitalasset.canton.data.CantonTimestamp import com.digitalasset.canton.time.Clock -import com.digitalasset.canton.topology.{PhysicalSynchronizerId, SynchronizerId} +import com.digitalasset.canton.topology.PhysicalSynchronizerId import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId import com.digitalasset.canton.tracing.TraceContext import io.circe.Json -import io.circe.syntax.* import io.grpc.{Status, StatusRuntimeException} import io.opentelemetry.api.trace.Tracer import org.apache.pekko.actor.ActorSystem