@@ -7,27 +7,31 @@ import zio.kvstore.protocol.KVServerRequest
77import zio .raft .Raft
88import zio .raft .protocol .*
99import zio .raft .sessionstatemachine .{SessionCommand , PendingServerRequest , SessionStateMachine }
10- import zio .raft .sessionstatemachine .given
10+ import zio .raft .sessionstatemachine .Codecs . given
1111import scodec .Codec
1212import scodec .bits .ByteVector
1313import zio .raft .server .RaftServer
1414import zio .raft .server .RaftServer .RaftAction
1515import java .time .Instant
16+ import zio .kvstore .Codecs .given
17+ import zio .kvstore .protocol .KVServerRequest .given
18+ import zio .kvstore .node .Node .NodeAction
19+ import zio .kvstore .node .Node .NodeAction .*
20+ import zio .raft .Peers
21+ import zio .raft .stores .LmdbStable
22+ import zio .raft .stores .segmentedlog .SegmentedLog
23+ import zio .raft .stores .FileSnapshotStore
24+ import zio .raft .zmq .ZmqRpc
1625
1726/** Node wiring RaftServer, Raft core, and KV state machine. */
1827final case class Node (
1928 raftServer : RaftServer ,
2029 raft : zio.raft.Raft [
2130 zio.raft.HMap [Tuple .Concat [zio.raft.sessionstatemachine.SessionSchema [KVResponse , KVServerRequest ], KVSchema ]],
2231 SessionCommand [KVCommand , KVServerRequest ]
23- ],
24- stateMachine : KVStateMachine
32+ ]
2533):
2634
27- // Bring codecs for (de)serialization of commands/responses/server-requests
28- import zio .kvstore .Codecs .given
29- import zio .kvstore .protocol .KVServerRequest .given
30-
3135 private def encodeServerRequestPayload (req : KVServerRequest ): ByteVector =
3236 summon[Codec [KVServerRequest ]].encode(req).require.bytes
3337
@@ -43,21 +47,6 @@ final case class Node(
4347 raftServer.sendServerRequest(env.sessionId, ServerRequest (env.requestId, payload, now))
4448 }
4549
46- // Unified stream pattern (@stream-architecture-pattern): define actions, merge streams, handle in one fold
47- private sealed trait NodeAction
48- private object NodeAction :
49- case class CreateSession (sessionId : SessionId , capabilities : Map [String , String ]) extends NodeAction
50- case class ClientRequest (
51- sessionId : SessionId ,
52- requestId : RequestId ,
53- lowestPendingRequestId : RequestId ,
54- command : KVCommand
55- ) extends NodeAction
56- case class ServerRequestAck (sessionId : SessionId , requestId : RequestId ) extends NodeAction
57- case class ExpireSession (sessionId : SessionId ) extends NodeAction
58- case class RetryTick (now : Instant ) extends NodeAction
59- case class StateNotificationReceived (notification : zio.raft.StateNotification ) extends NodeAction
60-
6150 private def handleAction (action : NodeAction ): UIO [Unit ] =
6251 action match
6352 // Process raftActions → SessionCommand → raft → publish via RaftServer
@@ -113,7 +102,7 @@ final case class Node(
113102 for
114103 now <- Clock .instant
115104 cmd = SessionCommand .ServerRequestAck [KVServerRequest ](now, sessionId, requestId)
116- _ <- raft.sendCommand(cmd).either.unit
105+ _ <- raft.sendCommand(cmd).ignore
117106 yield ()
118107
119108 case NodeAction .ExpireSession (sessionId) =>
@@ -151,14 +140,11 @@ final case class Node(
151140 case zio.raft.StateNotification .SteppedUp =>
152141 raft.readState.either.flatMap {
153142 case Right (state) =>
154- val sessionsSSM = state.iterator[" metadata" ].toList.collect {
155- case (sid : SessionId , md : zio.raft.sessionstatemachine.SessionMetadata ) => (sid, md)
156- }.toMap
157- val sessionsServer : Map [zio.raft.protocol.SessionId , zio.raft.server.SessionMetadata ] =
158- sessionsSSM.map { case (sid, md) =>
159- (sid, zio.raft.server.SessionMetadata (md.capabilities, md.createdAt))
160- }
161- raftServer.stepUp(sessionsServer)
143+ val sessions = SessionStateMachine .getSessions[KVResponse , KVServerRequest , KVSchema ](state).map {
144+ case (sessionId : SessionId , metadata) =>
145+ (sessionId, zio.raft.server.SessionMetadata (metadata.capabilities, metadata.createdAt))
146+ }
147+ raftServer.stepUp(sessions)
162148 case Left (_) => ZIO .unit
163149 }
164150 case zio.raft.StateNotification .SteppedDown (leaderId) =>
@@ -180,6 +166,7 @@ final case class Node(
180166 case RaftAction .ExpireSession (sessionId) =>
181167 NodeAction .ExpireSession (sessionId)
182168 },
169+ // TODO: filter this is we are not the leader
183170 ZStream .tick(10 .seconds).mapZIO(_ => Clock .instant.map(NodeAction .RetryTick .apply)),
184171 raft.stateNotifications.map(NodeAction .StateNotificationReceived .apply)
185172 )
@@ -220,3 +207,49 @@ final case class Node(
220207 ZIO .logInfo(" Node started" ) *>
221208 unifiedStream.mapZIO(handleAction).runDrain
222209end Node
210+
211+ object Node :
212+
213+ def make (
214+ serverBindAddress : String ,
215+ clusterBindAddress : String ,
216+ logDirectory : String ,
217+ snapshotDirectory : String ,
218+ memberId : zio.raft.MemberId ,
219+ peers : Peers
220+ ) =
221+ for
222+ stable <- LmdbStable .make
223+ logStore <- SegmentedLog .make[SessionCommand [KVCommand , KVServerRequest ]](logDirectory)
224+ snapshotStore <- FileSnapshotStore .make(zio.nio.file.Path (snapshotDirectory))
225+ rpc <- ZmqRpc .make[SessionCommand [KVCommand , KVServerRequest ]](
226+ clusterBindAddress,
227+ peers.map(p => (p, clusterBindAddress)).toMap
228+ )
229+
230+ raft <- Raft .make(
231+ memberId = memberId,
232+ peers = peers,
233+ stable = stable,
234+ logStore = logStore,
235+ snapshotStore = snapshotStore,
236+ rpc = rpc,
237+ stateMachine = new KVStateMachine ()
238+ )
239+ raftServer <- RaftServer .make(serverBindAddress)
240+ node = Node (raftServer, raft)
241+ yield node
242+
243+ sealed trait NodeAction
244+ object NodeAction :
245+ case class CreateSession (sessionId : SessionId , capabilities : Map [String , String ]) extends NodeAction
246+ case class ClientRequest (
247+ sessionId : SessionId ,
248+ requestId : RequestId ,
249+ lowestPendingRequestId : RequestId ,
250+ command : KVCommand
251+ ) extends NodeAction
252+ case class ServerRequestAck (sessionId : SessionId , requestId : RequestId ) extends NodeAction
253+ case class ExpireSession (sessionId : SessionId ) extends NodeAction
254+ case class RetryTick (now : Instant ) extends NodeAction
255+ case class StateNotificationReceived (notification : zio.raft.StateNotification ) extends NodeAction
0 commit comments