mssf_core/runtime/
stateful.rs

1// ------------------------------------------------------------
2// Copyright (c) Microsoft Corporation.  All rights reserved.
3// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
4// ------------------------------------------------------------
5
6// stateful contains rs definition of stateful traits that user needs to implement
7
8use crate::runtime::executor::BoxedCancelToken;
9use crate::types::ReplicaRole;
10
11use crate::types::{Epoch, OpenMode, ReplicaInformation, ReplicaSetConfig, ReplicaSetQuorumMode};
12
13use super::stateful_proxy::StatefulServicePartition;
14
15/// Represents a stateful service factory that is responsible for creating replicas
16/// of a specific type of stateful service. Stateful service factories are registered with
17/// the FabricRuntime by service hosts via register_stateful_service_factory().
18pub trait StatefulServiceFactory {
19    /// Called by Service Fabric to create a stateful service replica for a particular service.
20    fn create_replica(
21        &self,
22        servicetypename: &crate::WString,
23        servicename: &crate::WString,
24        initializationdata: &[u8],
25        partitionid: &crate::GUID,
26        replicaid: i64,
27    ) -> crate::Result<impl StatefulServiceReplica>;
28}
29
30/// Defines behavior that governs the lifecycle of a replica, such as startup, initialization, role changes, and shutdown.
31/// Remarks:
32/// Stateful service types must implement this interface. The logic of a stateful service type includes behavior that is
33/// invoked on primary replicas and behavior that is invoked on secondary replicas.
34#[trait_variant::make(StatefulServiceReplica: Send)]
35pub trait LocalStatefulServiceReplica: Send + Sync + 'static {
36    /// Opens an initialized service replica so that additional actions can be taken.
37    /// Returns PrimaryReplicator that is used by the stateful service.
38    /// Note:
39    /// Most user calls IFabricStatefulServicePartition.CreateReplicator instead of
40    /// writing their own replicator (TODO: not supported in mssf yet),
41    /// or use FabricCreateKeyValueStoreReplica.
42    async fn open(
43        &self,
44        openmode: OpenMode,
45        partition: &StatefulServicePartition,
46        cancellation_token: BoxedCancelToken,
47    ) -> crate::Result<impl PrimaryReplicator>;
48
49    /// Changes the role of the service replica to one of the ReplicaRole.
50    /// Returns the service’s new connection address that is to be associated with the replica via Service Fabric Naming.
51    /// Remarks:
52    /// The new role is indicated as a parameter. When the service transitions to the new role,
53    /// the service has a chance to update its current listening address. The listening address is the address
54    /// where clients connect to it and the one returned via the ResolveAsync API. This enables the service when
55    /// it is a primary replica to only claim some resources such as ports when communication from clients is expected.
56    async fn change_role(
57        &self,
58        newrole: ReplicaRole,
59        cancellation_token: BoxedCancelToken,
60    ) -> crate::Result<crate::WString>;
61
62    /// Closes the service replica gracefully when it is being shut down.
63    async fn close(&self, cancellation_token: BoxedCancelToken) -> crate::Result<()>;
64
65    /// Ungracefully terminates the service replica.
66    /// Remarks: Network issues resulting in Service Fabric process shutdown
67    /// and the use of ReportFault(FaultType) to report a Permanent fault are examples of ungraceful termination.
68    /// When this method is invoked, the service replica should immediately release and clean up all references and return.
69    fn abort(&self);
70}
71
72/// TODO: replicator has no public documentation
73#[trait_variant::make(Replicator: Send)]
74pub trait LocalReplicator: Send + Sync + 'static {
75    /// Opens replicator, and returns the replicator address that is visible to primary
76    /// in ReplicaInformation.
77    /// Remarks:
78    /// Replicator does not have an assigned role yet and should setup listening endpoint.
79    async fn open(&self, cancellation_token: BoxedCancelToken) -> crate::Result<crate::WString>;
80    async fn close(&self, cancellation_token: BoxedCancelToken) -> crate::Result<()>;
81
82    /// Change the replicator role.
83    ///
84    /// Remarks:
85    /// Replicator change_role is called before Replica change_role.
86    async fn change_role(
87        &self,
88        epoch: &Epoch,
89        role: &ReplicaRole,
90        cancellation_token: BoxedCancelToken,
91    ) -> crate::Result<()>;
92
93    /// (TODO: This doc is from IStateProvider but not Replicator.)
94    /// Indicates to a replica that the configuration of a replica set has changed due to
95    /// a change or attempted change to the primary replica. The change occurs due to failure
96    /// or load balancing of the previous primary replica. Epoch changes act as a barrier by
97    /// segmenting operations into the exact configuration periods in which they were sent
98    /// by a specific primary replica.
99    ///
100    /// Called only on active/idle secondary replicas. Primary replica gets new epoch via change_role call.
101    async fn update_epoch(
102        &self,
103        epoch: &Epoch,
104        cancellation_token: BoxedCancelToken,
105    ) -> crate::Result<()>;
106
107    /// Get the current LSN, end of log, called on secondaries.
108    /// SF uses this to do primary selection. It is also passed to update_catch_up_replica_set_configuration()
109    /// on primary. Primary uses this for catchup.
110    fn get_current_progress(&self) -> crate::Result<i64>;
111
112    /// Get the first LSN, beginning of log.
113    /// Remarks:
114    /// SF uses this to determine if other replicas can catch up from this replica.
115    /// Other replica's end of log must be higher than this replica's beginning of log
116    /// in order for the other replica to catchup, otherwise SF needs to drop the other
117    /// replica (if the current replica is chosen to be primary).
118    fn get_catch_up_capability(&self) -> crate::Result<i64>;
119    fn abort(&self);
120}
121
122// Remarks:
123// Adding a secondary into the partition involves the following steps:
124// * SF brings up an idle secondary replica S which can be empty or contain
125// (partial) previous data.
126// * build_replica is called on primary to copy data into the S.
127// * S is changed to active secondary
128// * update_catch_up_replica_set_configuration to include S in the current configuration,
129// and wait_for_catch_up_quorum are called on primary for final synchronization before SF
130// grants ReadStatus to S.
131// * SF grants ReadStatus to S, and replica build completes.
132//
133// For primary failover, all active or idle secondaries gets update_epoch() call, and new
134// primary gets the new epoch from change_role() call. Secondary should fence/reject
135// operations from the old primary with an older epoch.
136
137/// TODO: primary replicator has no public documentation, this is gathered unofficially and
138/// is subject to change/correction.
139/// IFabricPrimaryReplicator com interface wrapper.
140#[trait_variant::make(PrimaryReplicator: Send)]
141pub trait LocalPrimaryReplicator: Replicator {
142    // SF calls this to indicate that possible data loss has occurred (write quorum loss),
143    // returns is isStateChanged. If true, SF will re-create other secondaries.
144    // The default SF impl might be a pass through to the state provider.
145    async fn on_data_loss(&self, cancellation_token: BoxedCancelToken) -> crate::Result<u8>;
146
147    // Remarks on replicator configuration:
148    // At any time the replicator can have one or two configurations. There is always a current
149    // configuration which represents the set of replicas that are participating in replication
150    // along with the current write quorum. In addition there can be a previous configuration
151    // which represents the set of replicas that were in the previous configuration.
152    // When there is both a current and previous configuration the replicator must ensure that
153    // writes are acknowledeged by a write quroum of both configurations.
154
155    /// Informs the replicator there there is a current configuration and a previous configuration.
156    /// Called on primary to inform the set of active secondary replicas that may
157    /// begin to catchup. Idle secondary replicas are not included here.
158    ///
159    /// The total number of replica marked with must_catchup will not exceed the write quorum.
160    /// Secondary to be promoted to new primary is guaranteed to have must_catchup set,
161    /// i.e. it must catch up (have all the data) to be promoted to new primary.
162    ///
163    /// ReplicaInformation:
164    /// current_progress -> The LSN of the replica. -1 if the replicator is already aware of the replica
165    /// (it is in configuration or has been built) otherwise it will be the progress of the remote replica.
166    /// catch_up_capability -> The first LSN of the replica. Similar to current_progress.
167    /// must_catchup -> Set to true only for one replica in the current configuration.
168    fn update_catch_up_replica_set_configuration(
169        &self,
170        currentconfiguration: &ReplicaSetConfig,
171        previousconfiguration: &ReplicaSetConfig,
172    ) -> crate::Result<()>;
173
174    /// Informs the replicator about the current replica set configuration, and there
175    /// is no longer a previous configuration.
176    /// Remarks:
177    /// Replicas here are not marked as must_catchup.
178    fn update_current_replica_set_configuration(
179        &self,
180        currentconfiguration: &ReplicaSetConfig,
181    ) -> crate::Result<()>;
182
183    /// Called on primary to wait for replicas to catch up, before
184    /// accepting writes.
185    ///
186    /// mssf-core enables IFabricReplicatorCatchupSpecificQuorum for replicators,
187    /// so ReplicaSetQuarumMode::Write can be used.
188    ///
189    /// catchupmode:
190    /// All -> full quorum. All replicas needs to catch up.
191    /// Write -> write quorum, for replicas specified in update_catch_up_replica_set_configuration(currentconfiguration...),
192    ///     a subset of replicas that can form a write quorum must catchup, and the subset must include
193    ///     the replica with must_catchup set to true (primary candidate).
194    ///     This is used only in primary swap case in SF, to avoid slow replica preventing/slowing down the swap.
195    /// Remarks:
196    /// Catchup (or quorum catchup) in SF means that the lowest LSN among all replicas (or quorum of replicas
197    /// including the must catchup replica) in the current configuration is equal or greater than
198    /// the current committed LSN.
199    ///
200    /// For swap primary case, double catchup feature is enabled by default.
201    /// SF can first call this api before initiating write status revokation. SF then revoke write status,
202    /// and call this again. This allows replicator to catch up with write status granted to make necessary writes for
203    /// catch up. There is a chance that replicator takes forever to complete this api with mode ReplicaSetQuarumMode::All
204    /// since client/user can keep writing and advancing the committed LSN, but for it most likely would not
205    /// stall in mode ReplicaSetQuarumMode::Write.
206    /// In other cases when client write is not impacted (like secondary restart),
207    /// SF may call this api only once with write status granted.
208    ///
209    /// Implementor should not assume when this is called in relation to other api calls,
210    /// but instead follow the semantics of what catchup should do.
211    async fn wait_for_catch_up_quorum(
212        &self,
213        catchupmode: ReplicaSetQuorumMode,
214        cancellation_token: BoxedCancelToken,
215    ) -> crate::Result<()>;
216
217    /// Transferring state up to the current quorum LSN to a new or existing replica
218    /// that is outside the current configuration. (not included in update_catch_up_replica_set_configuration)
219    ///
220    /// replica:
221    /// role is IdleSecondary
222    /// status set to up or down
223    /// current progress is -1
224    /// catchup capability is -1
225    /// must catchup is false
226    ///
227    /// remarks:
228    /// SF can cancel the replica build operation by calling the cancellation token.
229    /// Replica being built or completed built does not count towards quorum and is
230    /// not part of the current configuration. Replica cannot be in build and be in the
231    /// configuration at the same time. Idle replica it maybe added by SF to the configuration
232    /// by calling update_x_configuration().
233    async fn build_replica(
234        &self,
235        replica: &ReplicaInformation,
236        cancellation_token: BoxedCancelToken,
237    ) -> crate::Result<()>;
238
239    /// Notifies primary that an idle replica built by build_replica() api call
240    /// has gone down and replicator should not send more operations to that replica
241    /// and should release all resources.
242    /// Remarks:
243    /// Removing replicas already in the partition, update_catch_up_replica_set_configuration
244    /// is called instead with ReplicaSetConfig not containng the to be removed replica.
245    /// SF does not call remove_replica on the replica where build_replica is still running.
246    fn remove_replica(&self, replicaid: i64) -> crate::Result<()>;
247}