Skip to content

Commit ff990ea

Browse files
authored
Merge pull request #176 from lerna-stack/add-entity-diagnostic-logs
Add entity diagnostic logs
2 parents 0f22a3e + 9d0227b commit ff990ea

File tree

6 files changed

+222
-5
lines changed

6 files changed

+222
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- Add diagnostic logs
1212
[PR#164](https://github.com/lerna-stack/akka-entity-replication/pull/164),
13+
[PR#176](https://github.com/lerna-stack/akka-entity-replication/pull/176),
1314
[PR#177](https://github.com/lerna-stack/akka-entity-replication/pull/177)
1415
- Add function extracting shard id from entity id to lerna.akka.entityreplication.typed.ClusterReplication
1516
[PR#172](https://github.com/lerna-stack/akka-entity-replication/pull/172)

src/main/scala/lerna/akka/entityreplication/typed/internal/behavior/Inactive.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,46 @@ private[entityreplication] class Inactive[Command, Event, State](
1919
protected val setup: BehaviorSetup[Command, Event, State],
2020
) extends ReplicationOperations[Command, Event, State] {
2121

22+
override def stateName: String = "Inactive"
23+
2224
def createBehavior(): Behavior[EntityCommand] =
2325
Behaviors
2426
.receiveMessage[EntityCommand] {
2527
case command: RaftProtocol.Activate =>
2628
receiveActivate(command)
2729
case command: RaftProtocol.ProcessCommand =>
30+
if (setup.context.log.isTraceEnabled) {
31+
setup.context.log.trace(
32+
"[{}] Stashing ProcessCommand: commandType=[{}]",
33+
stateName,
34+
command.command.getClass.getName,
35+
)
36+
}
2837
setup.stashBuffer.stash(command)
2938
Behaviors.same
3039
case command: RaftProtocol.Replica =>
40+
if (setup.context.log.isTraceEnabled) {
41+
setup.context.log.trace(
42+
"[{}] Stashing Replica: index=[{}], term=[{}], entityId=[{}], eventType=[{}]",
43+
stateName,
44+
command.logEntry.index,
45+
command.logEntry.term.term,
46+
command.logEntry.event.entityId.map(_.raw),
47+
command.logEntry.event.event.getClass.getName,
48+
)
49+
}
3150
setup.stashBuffer.stash(command)
3251
Behaviors.same
3352
case command: RaftProtocol.TakeSnapshot =>
53+
if (setup.context.log.isTraceEnabled) {
54+
setup.context.log.trace(
55+
"[{}] Stashing TakeSnapshot: index=[{}], entityId=[{}], replyTo=[{}]",
56+
stateName,
57+
command.metadata.logEntryIndex,
58+
command.metadata.entityId.raw,
59+
command.replyTo,
60+
)
61+
}
3462
setup.stashBuffer.stash(command)
3563
Behaviors.same
3664
case _: RaftProtocol.ApplySnapshot => Behaviors.unhandled
@@ -41,6 +69,14 @@ private[entityreplication] class Inactive[Command, Event, State](
4169
}.receiveSignal(setup.onSignal(setup.emptyState))
4270

4371
def receiveActivate(command: RaftProtocol.Activate): Behavior[EntityCommand] = {
72+
if (setup.context.log.isTraceEnabled) {
73+
setup.context.log.trace(
74+
"[{}] Received Activate: recoveryIndex=[{}], shardSnapshotStore=[{}]",
75+
stateName,
76+
command.recoveryIndex,
77+
command.shardSnapshotStore,
78+
)
79+
}
4480
Recovering.behavior(setup, command.shardSnapshotStore, command.recoveryIndex)
4581
}
4682
}

src/main/scala/lerna/akka/entityreplication/typed/internal/behavior/Ready.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ private[entityreplication] class Ready[Command, Event, State](
6363

6464
import Ready._
6565

66+
override def stateName: String = "Ready"
67+
6668
private[this] type BehaviorState = ReadyState[State]
6769

6870
def createBehavior(readyState: BehaviorState): Behavior[EntityCommand] =
@@ -90,6 +92,16 @@ private[entityreplication] class Ready[Command, Event, State](
9092
}
9193

9294
def receiveReplica(command: RaftProtocol.Replica, state: BehaviorState): Behavior[EntityCommand] = {
95+
if (context.log.isTraceEnabled) {
96+
context.log.trace(
97+
"[{}] Received Replica: index=[{}], term=[{}], entityId=[{}], eventType=[{}]",
98+
stateName,
99+
command.logEntry.index,
100+
command.logEntry.term.term,
101+
command.logEntry.event.entityId.map(_.raw),
102+
command.logEntry.event.event.getClass.getName,
103+
)
104+
}
93105
val logEntry = command.logEntry
94106
createBehavior(state.applyEvent(setup, logEntry.event.event, logEntry.index))
95107
}
@@ -116,6 +128,17 @@ private[entityreplication] class Ready[Command, Event, State](
116128
state: BehaviorState,
117129
effect: EffectImpl[Event, State],
118130
): Behavior[EntityCommand] = {
131+
if (context.log.isTraceEnabled) {
132+
context.log.trace(
133+
"[{}] Sending Replicate: entityId=[{}], instanceId=[{}], eventType=[{}], replyTo=[{}], to=[{}]",
134+
stateName,
135+
setup.entityContext.entityId,
136+
setup.instanceId.underlying,
137+
event.getClass.getName,
138+
context.self.toClassic,
139+
setup.shard,
140+
)
141+
}
119142
setup.shard ! RaftProtocol.Replicate(
120143
event = event,
121144
replyTo = context.self.toClassic,

src/main/scala/lerna/akka/entityreplication/typed/internal/behavior/Recovering.scala

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import akka.actor.typed.scaladsl.Behaviors
44
import akka.actor.typed.scaladsl.adapter._
55
import lerna.akka.entityreplication.raft.RaftProtocol
66
import lerna.akka.entityreplication.raft.RaftProtocol.EntityCommand
7-
import lerna.akka.entityreplication.raft.model.LogEntryIndex
7+
import lerna.akka.entityreplication.raft.model.{ LogEntry, LogEntryIndex }
88
import lerna.akka.entityreplication.raft.protocol.{ FetchEntityEvents, FetchEntityEventsResponse }
99
import lerna.akka.entityreplication.raft.snapshot.SnapshotProtocol
1010
import lerna.akka.entityreplication.raft.snapshot.SnapshotProtocol.EntitySnapshot
@@ -29,6 +29,8 @@ private[entityreplication] class Recovering[Command, Event, State](
2929

3030
import Recovering._
3131

32+
override def stateName: String = "Recovering"
33+
3234
def createBehavior(
3335
shardSnapshotStore: ActorRef[SnapshotProtocol.Command],
3436
recoveryIndex: LogEntryIndex,
@@ -45,12 +47,27 @@ private[entityreplication] class Recovering[Command, Event, State](
4547
case FetchEntityEventsResponse(events) => RaftProtocol.RecoveryState(events, snapshot)
4648
}
4749

50+
if (context.log.isTraceEnabled) {
51+
context.log.trace(
52+
"[{}] Sending FetchSnapshot: entityId=[{}], to=[{}]",
53+
stateName,
54+
setup.replicationId.entityId.raw,
55+
shardSnapshotStore,
56+
)
57+
}
4858
shardSnapshotStore ! SnapshotProtocol.FetchSnapshot(
4959
setup.replicationId.entityId,
5060
replyTo = fetchSnapshotResponseMapper.toClassic,
5161
)
5262

5363
Behaviors.withTimers { scheduler =>
64+
if (context.log.isTraceEnabled) {
65+
context.log.trace(
66+
"[{}] Starting single RecoveryTimeoutTimer: delay=[{}]",
67+
stateName,
68+
setup.settings.recoveryEntityTimeout,
69+
)
70+
}
5471
scheduler.startSingleTimer(
5572
RecoveryTimeoutTimer,
5673
RaftProtocol.RecoveryTimeout,
@@ -59,35 +76,103 @@ private[entityreplication] class Recovering[Command, Event, State](
5976
Behaviors
6077
.receiveMessage[EntityCommand] {
6178
case command: RaftProtocol.ApplySnapshot =>
79+
if (context.log.isTraceEnabled) {
80+
context.log.trace(
81+
"[{}] Received ApplySnapshot: index=[{}], entityId=[{}], stateType=[{}]",
82+
stateName,
83+
command.entitySnapshot.map(_.metadata.logEntryIndex),
84+
command.entitySnapshot.map(_.metadata.entityId.raw),
85+
command.entitySnapshot.map(_.state.underlying.getClass.getName),
86+
)
87+
}
6288
val snapshotIndex = command.entitySnapshot match {
6389
case Some(snapshot) => snapshot.metadata.logEntryIndex
6490
case None => LogEntryIndex.initial()
6591
}
92+
val replyTo =
93+
fetchEntityEventsResponseMapper(command.entitySnapshot)
94+
if (context.log.isTraceEnabled) {
95+
context.log.trace(
96+
"[{}] Sending FetchEntityEvents: entityId=[{}], fromIndex=[{}], toIndex=[{}], replyTo=[{}], to=[{}]",
97+
stateName,
98+
setup.replicationId.entityId.raw,
99+
snapshotIndex.next(),
100+
recoveryIndex,
101+
replyTo,
102+
setup.shard,
103+
)
104+
}
66105
setup.shard ! FetchEntityEvents(
67106
setup.replicationId.entityId,
68107
from = snapshotIndex.next(),
69108
to = recoveryIndex,
70-
fetchEntityEventsResponseMapper(command.entitySnapshot),
109+
replyTo,
71110
)
72111
Behaviors.same
73112
case command: RaftProtocol.RecoveryState =>
113+
if (context.log.isTraceEnabled) {
114+
def toLogMessage(logEntry: LogEntry): String = {
115+
val entityId = logEntry.event.entityId.map(_.raw)
116+
val eventType = logEntry.event.event.getClass.getName
117+
s"index=${logEntry.index}, term=${logEntry.term.term}, entityId=$entityId, eventType=$eventType"
118+
}
119+
context.log.trace(
120+
"[{}] Received RecoveryState: " +
121+
"snapshot.index=[{}], snapshot.entityId=[{}], snapshot.stateType=[{}], " +
122+
"events.size=[{}], events.head=[{}], events.last=[{}]",
123+
stateName,
124+
command.snapshot.map(_.metadata.logEntryIndex),
125+
command.snapshot.map(_.metadata.entityId.raw),
126+
command.snapshot.map(_.state.underlying.getClass),
127+
command.events.size,
128+
command.events.headOption.map(toLogMessage),
129+
command.events.lastOption.map(toLogMessage),
130+
)
131+
}
74132
scheduler.cancel(RecoveryTimeoutTimer)
75133
receiveRecoveryState(command)
76134
case RaftProtocol.RecoveryTimeout =>
77135
if (context.log.isInfoEnabled)
78136
context.log.info(
79-
"Entity (name: {}) recovering timed out. It will be retried later.",
137+
"[{}] Entity (name: [{}]) recovering timed out. It will be retried later.",
138+
stateName,
80139
setup.entityContext.entityId,
81140
)
82141
// TODO: Enable backoff to prevent cascade failures
83142
throw RaftProtocol.EntityRecoveryTimeoutException(context.self.path)
84143
case command: RaftProtocol.ProcessCommand =>
144+
if (context.log.isTraceEnabled) {
145+
context.log.trace(
146+
"[{}] Stashing ProcessCommand: commandType=[{}]",
147+
stateName,
148+
command.command.getClass.getName,
149+
)
150+
}
85151
setup.stashBuffer.stash(command)
86152
Behaviors.same
87153
case command: RaftProtocol.Replica =>
154+
if (context.log.isTraceEnabled) {
155+
context.log.trace(
156+
"[{}] Stashing Replica: index=[{}], term=[{}], entityId=[{}], eventType=[{}]",
157+
stateName,
158+
command.logEntry.index,
159+
command.logEntry.term.term,
160+
command.logEntry.event.entityId.map(_.raw),
161+
command.logEntry.event.event.getClass.getName,
162+
)
163+
}
88164
setup.stashBuffer.stash(command)
89165
Behaviors.same
90166
case command: RaftProtocol.TakeSnapshot =>
167+
if (context.log.isTraceEnabled) {
168+
setup.context.log.trace(
169+
"[{}] Stashing TakeSnapshot: index=[{}], entityId=[{}], replyTo=[{}]",
170+
stateName,
171+
command.metadata.logEntryIndex,
172+
command.metadata.entityId.raw,
173+
command.replyTo,
174+
)
175+
}
91176
setup.stashBuffer.stash(command)
92177
Behaviors.same
93178
case _: RaftProtocol.Activate => Behaviors.unhandled
@@ -107,10 +192,26 @@ private[entityreplication] class Recovering[Command, Event, State](
107192
}
108193
val snapshotAppliedState =
109194
ReadyState(entityState, lastAppliedLogIndex)
195+
if (context.log.isTraceEnabled) {
196+
context.log.trace(
197+
"[{}] Recovering with initial state: index=[{}], stateType=[{}]",
198+
stateName,
199+
snapshotAppliedState.lastAppliedLogEntryIndex,
200+
snapshotAppliedState.entityState.getClass.getName,
201+
)
202+
}
110203
val eventAppliedState =
111204
command.events.foldLeft(snapshotAppliedState)((state, entry) =>
112205
state.applyEvent(setup, entry.event.event, entry.index),
113206
)
207+
if (context.log.isTraceEnabled) {
208+
context.log.trace(
209+
"[{}] Recovered with state: index=[{}], stateType=[{}]",
210+
stateName,
211+
eventAppliedState.lastAppliedLogEntryIndex,
212+
eventAppliedState.entityState.getClass.getName,
213+
)
214+
}
114215
Ready.behavior(setup, eventAppliedState)
115216
}
116217
}

src/main/scala/lerna/akka/entityreplication/typed/internal/behavior/ReplicationOperations.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,32 @@ private[entityreplication] trait ReplicationOperations[Command, Event, State] {
1919

2020
protected def context: ActorContext[EntityCommand] = setup.context
2121

22+
/* The state name of a Behavior that executes replication operations.
23+
* This state name is only for diagnostic logging.
24+
*/
25+
protected def stateName: String
26+
2227
def receiveTakeSnapshot(command: TakeSnapshot, entityState: State): Behavior[EntityCommand] = {
28+
if (context.log.isTraceEnabled) {
29+
context.log.trace(
30+
"[{}] Received TakeSnapshot: index=[{}], entityId=[{}], replyTo=[{}]",
31+
stateName,
32+
command.metadata.logEntryIndex,
33+
command.metadata.entityId.raw,
34+
command.replyTo,
35+
)
36+
}
2337
val TakeSnapshot(metadata, replyTo) = command
38+
if (context.log.isTraceEnabled) {
39+
context.log.trace(
40+
"[{}] Sending Snapshot: index=[{}], entityId=[{}], stateType=[{}], to=[{}]",
41+
stateName,
42+
command.metadata.logEntryIndex,
43+
command.metadata.entityId.raw,
44+
entityState.getClass.getName,
45+
replyTo,
46+
)
47+
}
2448
replyTo ! Snapshot(metadata, EntityState(entityState))
2549
Behaviors.same
2650
}

src/main/scala/lerna/akka/entityreplication/typed/internal/behavior/WaitForReplication.scala

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,29 @@ private[entityreplication] class WaitForReplication[Command, Event, State](
3232

3333
import WaitForReplication._
3434

35+
override def stateName: String = "WaitForReplication"
36+
3537
private[this] type BehaviorState = WaitForReplicationState[State]
3638

3739
def createBehavior(state: BehaviorState): Behavior[EntityCommand] =
3840
Behaviors
3941
.receiveMessage[EntityCommand] {
4042
case command: RaftProtocol.Replica => receiveReplica(command, state)
4143
case command: RaftProtocol.ReplicationSucceeded => receiveReplicationSucceeded(command, state)
42-
case RaftProtocol.ReplicationFailed => Ready.behavior(setup, transformReadyState(state)) // Discard side effects
43-
case command: RaftProtocol.TakeSnapshot => receiveTakeSnapshot(command, state.entityState)
44+
case RaftProtocol.ReplicationFailed =>
45+
if (context.log.isTraceEnabled) {
46+
context.log.trace("[{}] Received ReplicationFailed", stateName)
47+
}
48+
Ready.behavior(setup, transformReadyState(state)) // Discard side effects
49+
case command: RaftProtocol.TakeSnapshot => receiveTakeSnapshot(command, state.entityState)
4450
case command: RaftProtocol.ProcessCommand =>
51+
if (context.log.isTraceEnabled) {
52+
context.log.trace(
53+
"[{}] Stashing ProcessCommand: commandType=[{}]",
54+
stateName,
55+
command.command.getClass.getName,
56+
)
57+
}
4558
setup.stashBuffer.stash(command)
4659
Behaviors.same
4760
case _: RaftProtocol.Activate => Behaviors.unhandled
@@ -51,6 +64,16 @@ private[entityreplication] class WaitForReplication[Command, Event, State](
5164
}.receiveSignal(setup.onSignal(state.entityState))
5265

5366
private[this] def receiveReplica(command: RaftProtocol.Replica, state: BehaviorState): Behavior[EntityCommand] = {
67+
if (context.log.isTraceEnabled) {
68+
context.log.trace(
69+
"[{}] Received Replica: index=[{}], term=[{}], entityId=[{}], eventType=[{}]",
70+
stateName,
71+
command.logEntry.index,
72+
command.logEntry.term.term,
73+
command.logEntry.event.entityId.map(_.raw),
74+
command.logEntry.event.event.getClass.getName,
75+
)
76+
}
5477
// ReplicatedEntityBehavior can receive Replica message when RaftActor demoted to Follower while replicating an event
5578
Ready.behavior(
5679
setup,
@@ -67,6 +90,15 @@ private[entityreplication] class WaitForReplication[Command, Event, State](
6790
"ReplicationSucceeded received by the Entity should contain a instanceId",
6891
// Entity sends a Replicate command which contains the instanceId
6992
)
93+
if (context.log.isTraceEnabled) {
94+
context.log.trace(
95+
"[{}] Received ReplicationSucceeded: index=[{}], instanceId=[{}], eventType=[{}]",
96+
stateName,
97+
command.logEntryIndex,
98+
command.instanceId.map(_.underlying),
99+
command.event.getClass.getName,
100+
)
101+
}
70102
if (command.instanceId.contains(setup.instanceId)) {
71103
val event = EntityEvent(Option(setup.replicationId.entityId), command.event)
72104
val newState = transformReadyState(state).applyEvent(setup, event.event, command.logEntryIndex)

0 commit comments

Comments
 (0)