-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
The most prominent changes compared to eventsourced are: - No central processor and channel registry any more - Auto-recovery of processors on start and restart (can be disabled) - Recovery of processor networks doesn't require coordination - Explicit channel activation not needed any more - Message sequence numbers generated per processor (no gaps) - Sender references are journaled along with messages - Processors can determine their recovery status - No custom API on extension object, only messages - Journal created by extension from config, not by application - Applications only interact with processors and channels via messages - Internal design prepared for having processor-specific journal actors (for later optimization possibilities) Further additions and changes during review: - Allow processor implementation classes to use inherited stash - Channel support to resolve (potentially invalid) sender references - Logical intead of physical deletion of messages - Pinned dispatcher for LevelDB journal - Processor can handle failures during recovery - Message renamed to Persistent This prototype has the following limitations: - Serialization of persistent messages and their payload via JavaSerializer only (will be configurable later) - The LevelDB journal implementation based on a LevelDB Java port, not the native LevelDB (will be configurable later) The following features will be added later using separate tickets: - Snapshot-based recovery - Reliable channels - Journal plugin API - Optimizations - ...
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package docs.persistence; | ||
|
||
import scala.Option; | ||
|
||
import akka.actor.*; | ||
import akka.persistence.*; | ||
|
||
public class PersistenceDocTest { | ||
|
||
public interface ProcessorMethods { | ||
//#processor-id | ||
public String processorId(); | ||
//#processor-id | ||
//#recovery-status | ||
public boolean recoveryRunning(); | ||
public boolean recoveryFinished(); | ||
//#recovery-status | ||
//#current-message | ||
public Persistent getCurrentPersistentMessage(); | ||
//#current-message | ||
} | ||
|
||
static Object o1 = new Object() { | ||
//#definition | ||
class MyProcessor extends UntypedProcessor { | ||
public void onReceive(Object message) throws Exception { | ||
if (message instanceof Persistent) { | ||
// message has been written to journal | ||
Persistent persistent = (Persistent)message; | ||
Object payload = persistent.payload(); | ||
Long sequenceNr = persistent.sequenceNr(); | ||
// ... | ||
} else { | ||
// message has not been written to journal | ||
} | ||
} | ||
} | ||
//#definition | ||
|
||
class MyActor extends UntypedActor { | ||
ActorRef processor; | ||
|
||
public MyActor() { | ||
//#usage | ||
processor = getContext().actorOf(Props.create(MyProcessor.class), "myProcessor"); | ||
|
||
processor.tell(Persistent.create("foo"), null); | ||
processor.tell("bar", null); | ||
//#usage | ||
} | ||
|
||
public void onReceive(Object message) throws Exception { | ||
// ... | ||
} | ||
|
||
private void recover() { | ||
//#recover-explicit | ||
processor.tell(Recover.create(), null); | ||
//#recover-explicit | ||
} | ||
} | ||
}; | ||
|
||
static Object o2 = new Object() { | ||
abstract class MyProcessor1 extends UntypedProcessor { | ||
//#recover-on-start-disabled | ||
@Override | ||
public void preStartProcessor() {} | ||
//#recover-on-start-disabled | ||
|
||
//#recover-on-restart-disabled | ||
@Override | ||
public void preRestartProcessor(Throwable reason, Option<Object> message) {} | ||
//#recover-on-restart-disabled | ||
} | ||
|
||
abstract class MyProcessor2 extends UntypedProcessor { | ||
//#recover-on-start-custom | ||
@Override | ||
public void preStartProcessor() { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jboner
Member
|
||
getSelf().tell(Recover.create(457L), null); | ||
} | ||
//#recover-on-start-custom | ||
} | ||
|
||
abstract class MyProcessor3 extends UntypedProcessor { | ||
//#deletion | ||
@Override | ||
public void preRestartProcessor(Throwable reason, Option<Object> message) throws Exception { | ||
if (message.isDefined() && message.get() instanceof Persistent) { | ||
delete((Persistent) message.get()); | ||
} | ||
super.preRestartProcessor(reason, message); | ||
} | ||
//#deletion | ||
} | ||
|
||
class MyProcessor4 extends UntypedProcessor implements ProcessorMethods { | ||
//#processor-id-override | ||
@Override | ||
public String processorId() { | ||
return "my-stable-processor-id"; | ||
} | ||
//#processor-id-override | ||
@Override | ||
public void onReceive(Object message) throws Exception {} | ||
} | ||
}; | ||
|
||
static Object o3 = new Object() { | ||
//#channel-example | ||
class MyProcessor extends UntypedProcessor { | ||
private final ActorRef destination; | ||
private final ActorRef channel; | ||
|
||
public MyProcessor() { | ||
this.destination = getContext().actorOf(Props.create(MyDestination.class)); | ||
this.channel = getContext().actorOf(Channel.props(), "myChannel"); | ||
} | ||
|
||
public void onReceive(Object message) throws Exception { | ||
if (message instanceof Persistent) { | ||
Persistent p = (Persistent)message; | ||
Persistent out = p.withPayload("done " + p.payload()); | ||
channel.tell(Deliver.create(out, destination), getSelf()); | ||
} | ||
} | ||
} | ||
|
||
class MyDestination extends UntypedActor { | ||
public void onReceive(Object message) throws Exception { | ||
if (message instanceof Persistent) { | ||
Persistent p = (Persistent)message; | ||
System.out.println("received " + p.payload()); | ||
p.confirm(); | ||
This comment has been minimized.
Sorry, something went wrong.
jboner
Member
|
||
} | ||
} | ||
} | ||
//#channel-example | ||
|
||
class MyProcessor2 extends UntypedProcessor { | ||
private final ActorRef destination; | ||
private final ActorRef channel; | ||
|
||
public MyProcessor2(ActorRef destination) { | ||
this.destination = getContext().actorOf(Props.create(MyDestination.class)); | ||
//#channel-id-override | ||
this.channel = getContext().actorOf(Channel.props("my-stable-channel-id")); | ||
//#channel-id-override | ||
} | ||
|
||
public void onReceive(Object message) throws Exception { | ||
if (message instanceof Persistent) { | ||
Persistent p = (Persistent)message; | ||
Persistent out = p.withPayload("done " + p.payload()); | ||
channel.tell(Deliver.create(out, destination), getSelf()); | ||
|
||
//#channel-example-reply | ||
channel.tell(Deliver.create(out, getSender()), getSelf()); | ||
//#channel-example-reply | ||
//#resolve-destination | ||
channel.tell(Deliver.create(out, getSender(), Resolve.destination()), getSelf()); | ||
//#resolve-destination | ||
//#resolve-sender | ||
channel.tell(Deliver.create(out, destination, Resolve.sender()), getSender()); | ||
//#resolve-sender | ||
|
||
} | ||
} | ||
} | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,4 +11,5 @@ Actors | |
mailboxes | ||
routing | ||
fsm | ||
persistence | ||
testing |
1 comment
on commit cdeea92
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is epic! :)
Why call it
preStartProcessor
? Why not something likepreStartRecovery
orpreStartReplay
or something with semantic meaning? That is of course if the main purpose of it is to perform recovery—as the docs state.