Skip to content
Merged
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
52 changes: 51 additions & 1 deletion UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,57 @@ Template for a bigger topic
#### Impact and Migration

### Minor Improvements
- improvement
* Logging improvements in sequencer (around event signaller and sequencer reader).
* Canton startup logging: it is now possible to configure a startup log level, that will reset after a timeout, i.e.:
```hocon
canton.monitoring.logging.startup {
log-level = "DEBUG"
reset-after = "5 minutes"
}
```
* Sequencer progress supervisor: it is possible now to enable a monitor for the sequencer node progressing on its own subscription.
* False positives related to asynchronous writer has been fixed
* Added a warn action to kill the sequencer node
* Configuration:
```hocon
// Future supervision has to be enabled
canton.monitoring.logging.log-slow-futures = true
canton.sequencers.<sequencer>.parameters.progress-supervisor {
enabled = true
warn-action = enable-debug-logging // set to "restart-sequencer" for sequencer node to exit when stuck
// detection timetout has been bumped in defaults to
// stuck-detection-timeout = 15 minutes
}
```
* Additional sequencer metrics:
* more `daml.sequencer.block.stream-element-count` metric values with `flow` labels from Pekko streams in the sequencer reader
* new `daml.sequencer.public-api.subscription-last-timestamp` metric with the last timestamp read via the member's subscription,
labeled by the `subscriber`
* 2 new metrics to monitor the time interval covered by the events buffer `daml.sequencer.head_timestamp` and `daml.sequencer.last_timestamp`

* If the new connection pool is enabled, the health status of a node will present the following new components:
* `sequencer-connection-pool`
* `internal-sequencer-connection-<alias>` (one per defined sequencer connection)
* `sequencer-subscription-pool`
* `subscription-sequencer-connection-<alias>` (one per active susbcription)

* Sequencer nodes serving many validator subscriptions are not flooded anymore with tasks reading from the database.
The parallelism is configured via the `sequencers.<sequencer>.parameters.batching.parallelism`. Notice that this
config setting is used not just used for limiting the event reading, but elsewhere in the sequencer as well.

* Replaying of ACS changes for the ACS commitment processor has smaller memory overhead:
* Changes are loaded in batches from the DB
* ACS changes are potentially smaller because we remove activations and deactivations that cancel out.
This is particularly useful for short-lived contracts

* New parameter `safeToPruneCommitmentState` in `ParticipantStoreConfig`, enabling to optionally specify
in which conditions counter-participants that have not sent matching commitments cannot block pruning on
the current participant. The parameter affects all pruning commands, including scheduled pruning.

* Additional metrics for the ACS commitment processor: `daml.participant.sync.commitments.last-incoming-received`, `daml.participant.sync.commitments.last-incoming-processed`, `daml.participant.sync.commitments.last-locally-completed`, and `daml.participant.sync.commitments.last-locally-checkpointed`.

* Extended the set of characters allowed in user-id in the ledger api to contain brackets: `()`.
This also makes those characters accepted as part of the `sub` claims in JWT tokens.

### Preview Features
- preview feature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ service PruningService {
message PruneRequest {
// Inclusive offset up to which the ledger is to be pruned.
int64 prune_up_to = 1;
optional SafeToPruneCommitmentState counter_participants_commitments_state = 2;
}

message PruneResponse {
Expand All @@ -80,6 +81,7 @@ message PruneResponse {
message GetSafePruningOffsetRequest {
google.protobuf.Timestamp before_or_at = 1;
int64 ledger_end = 2;
optional SafeToPruneCommitmentState counter_participants_commitments_state = 3;
}

message GetSafePruningOffsetResponse {
Expand All @@ -90,3 +92,25 @@ message GetSafePruningOffsetResponse {
NoSafePruningOffset no_safe_pruning_offset = 2;
}
}

// The states ensure that, once a timestamp is seen as safe to prune, it cannot later be seen as unsafe to prune.
// For this, we assume that the outstanding periods table ensures that a period's state can only be outstanding, mismatch, match:
// - Match supersedes all other states and cannot be changed
// - Mismatch can only change to match
// - Outstanding can change to any other state
// Therefore, in state SAFE_TO_PRUNE_COMMITMENT_STATE_NONE, we declare a state safe to prune because periods are a match, which can never change
// In state SAFE_TO_PRUNE_COMMITMENT_STATE_MATCH_MISMATCH, a state match cannot change later, and a mismatch can only change to a match,
// which is covered by the state. Therefore, an affirmative pruning decision for a timestamp cannot change later.
// A similar argument can be made for SAFE_TO_PRUNE_COMMITMENT_STATE_ALL.

enum SafeToPruneCommitmentState {
// Default enum value that indicates the counter_participants_commitments_state field was not explicitly set.
SAFE_TO_PRUNE_COMMITMENT_STATE_UNSPECIFIED = 0;
// We consider safe to prune the periods where all states match
// This is the default pruning behavior: when the counter_participants_commitments_state field isn't set, we'll have this behavior, but without explicitly setting the value
SAFE_TO_PRUNE_COMMITMENT_STATE_MATCH = 1;
// We consider safe to prune only periods that have matching or mismatching commitments
SAFE_TO_PRUNE_COMMITMENT_STATE_MATCH_MISMATCH = 2;
// All periods are safe to prune, i.e., those that have matching, mismatching or outstanding commitments
SAFE_TO_PRUNE_COMMITMENT_STATE_ALL = 3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import com.digitalasset.canton.participant.pruning.AcsCommitmentProcessor.{
import com.digitalasset.canton.participant.synchronizer.SynchronizerConnectionConfig
import com.digitalasset.canton.protocol.LfContractId
import com.digitalasset.canton.protocol.messages.{AcsCommitment, CommitmentPeriod}
import com.digitalasset.canton.scheduler.SafeToPruneCommitmentState
import com.digitalasset.canton.sequencing.SequencerConnectionValidation
import com.digitalasset.canton.sequencing.protocol.TrafficState
import com.digitalasset.canton.serialization.ProtoConverter
Expand Down Expand Up @@ -2170,7 +2171,7 @@ object ParticipantAdminCommands {
override protected def createRequest(): Either[String, v30.GetSafePruningOffsetRequest] =
for {
beforeOrAt <- CantonTimestamp.fromInstant(beforeOrAt)
} yield v30.GetSafePruningOffsetRequest(Some(beforeOrAt.toProtoTimestamp), ledgerEnd)
} yield v30.GetSafePruningOffsetRequest(Some(beforeOrAt.toProtoTimestamp), ledgerEnd, None)

override protected def submitRequest(
service: PruningServiceStub,
Expand All @@ -2189,10 +2190,19 @@ object ParticipantAdminCommands {
}
}

final case class PruneInternallyCommand(pruneUpTo: Long)
extends Base[v30.PruneRequest, v30.PruneResponse, Unit] {
final case class PruneInternallyCommand(
pruneUpTo: Long,
safeToPruneCommitmentState: Option[SafeToPruneCommitmentState],
) extends Base[v30.PruneRequest, v30.PruneResponse, Unit] {
override protected def createRequest(): Either[String, v30.PruneRequest] =
Right(v30.PruneRequest(pruneUpTo))
Right(
v30.PruneRequest(
pruneUpTo,
safeToPruneCommitmentState.map(
_.toProtoV30
),
)
)

override protected def submitRequest(
service: PruningServiceStub,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import com.digitalasset.canton.platform.config.{
TopologyAwarePackageSelectionConfig,
}
import com.digitalasset.canton.pureconfigutils.SharedConfigReaders.catchConvertError
import com.digitalasset.canton.scheduler.SafeToPruneCommitmentState
import com.digitalasset.canton.sequencing.authentication.{
AuthenticationTokenManagerConfig,
AuthenticationTokenManagerExponentialBackoffConfig,
Expand Down Expand Up @@ -498,6 +499,7 @@ final case class CantonConfig(
commitmentMismatchDebugging = participantParameters.commitmentMismatchDebugging,
commitmentProcessorNrAcsChangesBehindToTriggerCatchUp =
participantParameters.commitmentProcessorNrAcsChangesBehindToTriggerCatchUp,
commitmentReduceParallelism = participantParameters.commitmentReduceParallelism,
autoSyncProtocolFeatureFlags = participantParameters.autoSyncProtocolFeatureFlags,
)
}
Expand Down Expand Up @@ -1306,6 +1308,9 @@ object CantonConfig {
implicit val participantStoreConfigReader: ConfigReader[ParticipantStoreConfig] = {
implicit val journalPruningConfigReader: ConfigReader[JournalPruningConfig] =
deriveReader[JournalPruningConfig]
implicit val safeToPruneCommitmentStateConfigReader
: ConfigReader[SafeToPruneCommitmentState] =
SafeToPruneCommitmentState.reader
deriveReader[ParticipantStoreConfig]
}
implicit val adminWorkflowConfigReader: ConfigReader[AdminWorkflowConfig] =
Expand Down Expand Up @@ -1366,6 +1371,9 @@ object CantonConfig {
deriveReader[CantonFeatures]
lazy implicit final val cantonWatchdogConfigReader: ConfigReader[WatchdogConfig] =
deriveReader[WatchdogConfig]
lazy implicit final val progressSupervisorWarnActionConfigReader
: ConfigReader[ProgressSupervisorConfig.WarnAction] =
deriveEnumerationReader[ProgressSupervisorConfig.WarnAction]
lazy implicit final val progressSupervisorConfigReader: ConfigReader[ProgressSupervisorConfig] =
deriveReader[ProgressSupervisorConfig]

Expand Down Expand Up @@ -1983,6 +1991,9 @@ object CantonConfig {
implicit val participantStoreConfigWriter: ConfigWriter[ParticipantStoreConfig] = {
implicit val journalPruningConfigWriter: ConfigWriter[JournalPruningConfig] =
deriveWriter[JournalPruningConfig]
implicit val safeToPruneCommitmentStateConfigWriter
: ConfigWriter[SafeToPruneCommitmentState] =
SafeToPruneCommitmentState.writer
deriveWriter[ParticipantStoreConfig]
}
implicit val adminWorkflowConfigWriter: ConfigWriter[AdminWorkflowConfig] =
Expand Down Expand Up @@ -2043,6 +2054,9 @@ object CantonConfig {
deriveWriter[CantonFeatures]
lazy implicit final val cantonWatchdogConfigWriter: ConfigWriter[WatchdogConfig] =
deriveWriter[WatchdogConfig]
lazy implicit final val cantonProgressSupervisorWarnActionWriter
: ConfigWriter[ProgressSupervisorConfig.WarnAction] =
deriveEnumerationWriter[ProgressSupervisorConfig.WarnAction]
lazy implicit final val cantonProgressSupervisorConfigWriter
: ConfigWriter[ProgressSupervisorConfig] =
deriveWriter[ProgressSupervisorConfig]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import com.digitalasset.canton.protocol.messages.{
SignedProtocolMessage,
}
import com.digitalasset.canton.protocol.{ContractInstance, LfContractId, LfVersionedTransaction}
import com.digitalasset.canton.scheduler.SafeToPruneCommitmentState
import com.digitalasset.canton.sequencing.*
import com.digitalasset.canton.serialization.ProtoConverter
import com.digitalasset.canton.time.NonNegativeFiniteDuration
Expand Down Expand Up @@ -282,10 +283,9 @@ private[console] object ParticipantCommands {
def reconnect_all(
runner: AdminCommandRunner,
ignoreFailures: Boolean,
): ConsoleCommandResult[Unit] =
runner.adminCommand(
ParticipantAdminCommands.SynchronizerConnectivity.ReconnectSynchronizers(ignoreFailures)
)
): ConsoleCommandResult[Unit] = runner.adminCommand(
ParticipantAdminCommands.SynchronizerConnectivity.ReconnectSynchronizers(ignoreFailures)
)

def disconnect(
runner: AdminCommandRunner,
Expand Down Expand Up @@ -665,10 +665,16 @@ class ParticipantPruningAdministrationGroup(
|performs additional safety checks returning a ``NOT_FOUND`` error if ``pruneUpTo`` is higher than the
|offset returned by ``find_safe_offset`` on any synchronizer with events preceding the pruning offset."""
)
def prune_internally(pruneUpTo: Long): Unit =
def prune_internally(
pruneUpTo: Long,
safeToPruneCommitmentState: Option[SafeToPruneCommitmentState] = None,
): Unit =
check(FeatureFlag.Testing) {
consoleEnvironment.run(
adminCommand(ParticipantAdminCommands.Pruning.PruneInternallyCommand(pruneUpTo))
adminCommand(
ParticipantAdminCommands.Pruning
.PruneInternallyCommand(pruneUpTo, safeToPruneCommitmentState)
)
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --enable-interfaces=yes
name: model-tests
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/CantonLfDev/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.dev
name: CantonLfDev
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/CantonLfV21/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.2
- --enable-interfaces=yes
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/CantonTest/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.2
name: CantonTests
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/CantonTestDev/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.dev
name: CantonTestsDev
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/JsonApiTest/Account/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.2
- --enable-interfaces=yes
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/JsonApiTest/CIou/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.2
- --enable-interfaces=yes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
name: ifoo
source: IFoo.daml
version: 0.0.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
name: foo
data-dependencies:
- ../../../../scala-2.13/resource_managed/test/ifoo-0.0.1.dar
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
name: foo
data-dependencies:
- ../../../../scala-2.13/resource_managed/test/ifoo-0.0.1.dar
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
name: foo
data-dependencies:
- ../../../../scala-2.13/resource_managed/test/ifoo-0.0.1.dar
Expand Down
2 changes: 1 addition & 1 deletion community/app/src/test/daml/JsonApiTest/User/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sdk-version: 3.4.10
sdk-version: 3.4.11-snapshot.20260114.14381.0.va4e7d401
build-options:
- --target=2.2
name: User
Expand Down
Loading