+per #3641 Storage plugin API
- Journal plugin API for storage backends with asynchronous client API (default impl: in-memory journal) - Journal plugin API for storage backends with synchronous client API (default impl: LevelDB journal) - Snapshot store plugin API (default impl: local filesystem snapshot store)
This commit is contained in:
parent
1bda2a43d5
commit
da7490bbc9
33 changed files with 1454 additions and 474 deletions
|
|
@ -25,13 +25,20 @@ public class PersistenceDocTest {
|
|||
class MyProcessor extends UntypedProcessor {
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof Persistent) {
|
||||
// message has been written to journal
|
||||
// message successfully written to journal
|
||||
Persistent persistent = (Persistent)message;
|
||||
Object payload = persistent.payload();
|
||||
Long sequenceNr = persistent.sequenceNr();
|
||||
// ...
|
||||
} else if (message instanceof PersistenceFailure) {
|
||||
// message failed to be written to journal
|
||||
PersistenceFailure failure = (PersistenceFailure)message;
|
||||
Object payload = failure.payload();
|
||||
Long sequenceNr = failure.sequenceNr();
|
||||
Throwable cause = failure.cause();
|
||||
// ...
|
||||
} else {
|
||||
// message has not been written to journal
|
||||
// message not written to journal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -179,11 +186,11 @@ public class PersistenceDocTest {
|
|||
public void onReceive(Object message) throws Exception {
|
||||
if (message.equals("snap")) {
|
||||
saveSnapshot(state);
|
||||
} else if (message instanceof SaveSnapshotSucceeded) {
|
||||
SnapshotMetadata metadata = ((SaveSnapshotSucceeded)message).metadata();
|
||||
} else if (message instanceof SaveSnapshotSuccess) {
|
||||
SnapshotMetadata metadata = ((SaveSnapshotSuccess)message).metadata();
|
||||
// ...
|
||||
} else if (message instanceof SaveSnapshotFailed) {
|
||||
SnapshotMetadata metadata = ((SaveSnapshotFailed)message).metadata();
|
||||
} else if (message instanceof SaveSnapshotFailure) {
|
||||
SnapshotMetadata metadata = ((SaveSnapshotFailure)message).metadata();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
|
@ -225,6 +232,5 @@ public class PersistenceDocTest {
|
|||
//#snapshot-criteria
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
package docs.persistence;
|
||||
|
||||
//#plugin-imports
|
||||
import scala.concurrent.Future;
|
||||
import akka.japi.Option;
|
||||
import akka.japi.Procedure;
|
||||
import akka.persistence.*;
|
||||
import akka.persistence.journal.japi.*;
|
||||
import akka.persistence.snapshot.japi.*;
|
||||
//#plugin-imports
|
||||
|
||||
public class PersistencePluginDocTest {
|
||||
static Object o1 = new Object() {
|
||||
abstract class MySnapshotStore extends SnapshotStore {
|
||||
//#snapshot-store-plugin-api
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously loads a snapshot.
|
||||
*
|
||||
* @param processorId processor id.
|
||||
* @param criteria selection criteria for loading.
|
||||
*/
|
||||
public abstract Future<Option<SelectedSnapshot>> doLoadAsync(String processorId, SnapshotSelectionCriteria criteria);
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously saves a snapshot.
|
||||
*
|
||||
* @param metadata snapshot metadata.
|
||||
* @param snapshot snapshot.
|
||||
*/
|
||||
public abstract Future<Void> doSaveAsync(SnapshotMetadata metadata, Object snapshot);
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Called after successful saving of a snapshot.
|
||||
*
|
||||
* @param metadata snapshot metadata.
|
||||
*/
|
||||
public abstract void onSaved(SnapshotMetadata metadata) throws Exception;
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Deletes the snapshot identified by `metadata`.
|
||||
*
|
||||
* @param metadata snapshot metadata.
|
||||
*/
|
||||
public abstract void doDelete(SnapshotMetadata metadata) throws Exception;
|
||||
//#snapshot-store-plugin-api
|
||||
}
|
||||
|
||||
abstract class MySyncWriteJournal extends SyncWriteJournal {
|
||||
//#sync-write-plugin-api
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Synchronously writes a `persistent` message to the journal.
|
||||
*/
|
||||
@Override
|
||||
public abstract void doWrite(PersistentImpl persistent) throws Exception;
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Synchronously marks a `persistent` message as deleted.
|
||||
*/
|
||||
@Override
|
||||
public abstract void doDelete(PersistentImpl persistent) throws Exception;
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Synchronously writes a delivery confirmation to the journal.
|
||||
*/
|
||||
@Override
|
||||
public abstract void doConfirm(String processorId, long sequenceNr, String channelId) throws Exception;
|
||||
//#sync-write-plugin-api
|
||||
}
|
||||
|
||||
abstract class MyAsyncWriteJournal extends AsyncWriteJournal {
|
||||
//#async-write-plugin-api
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously writes a `persistent` message to the journal.
|
||||
*/
|
||||
@Override
|
||||
public abstract Future<Void> doWriteAsync(PersistentImpl persistent);
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously marks a `persistent` message as deleted.
|
||||
*/
|
||||
@Override
|
||||
public abstract Future<Void> doDeleteAsync(PersistentImpl persistent);
|
||||
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously writes a delivery confirmation to the journal.
|
||||
*/
|
||||
@Override
|
||||
public abstract Future<Void> doConfirmAsync(String processorId, long sequenceNr, String channelId);
|
||||
//#async-write-plugin-api
|
||||
}
|
||||
|
||||
abstract class MyAsyncReplay extends AsyncReplay {
|
||||
//#async-replay-plugin-api
|
||||
/**
|
||||
* Plugin Java API.
|
||||
*
|
||||
* Asynchronously replays persistent messages. Implementations replay a message
|
||||
* by calling `replayCallback`. The returned future must be completed when all
|
||||
* messages (matching the sequence number bounds) have been replayed. The future
|
||||
* `Long` value must be the highest stored sequence number in the journal for the
|
||||
* specified processor. The future must be completed with a failure if any of
|
||||
* the persistent messages could not be replayed.
|
||||
*
|
||||
* The `replayCallback` must also be called with messages that have been marked
|
||||
* as deleted. In this case a replayed message's `deleted` field must be set to
|
||||
* `true`.
|
||||
*
|
||||
* The channel ids of delivery confirmations that are available for a replayed
|
||||
* message must be contained in that message's `confirms` sequence.
|
||||
*
|
||||
* @param processorId processor id.
|
||||
* @param fromSequenceNr sequence number where replay should start.
|
||||
* @param toSequenceNr sequence number where replay should end (inclusive).
|
||||
* @param replayCallback called to replay a single message.
|
||||
*/
|
||||
@Override
|
||||
public abstract Future<Long> doReplayAsync(String processorId, long fromSequenceNr, long toSequenceNr, Procedure<PersistentImpl> replayCallback);
|
||||
//#async-replay-plugin-api
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -50,12 +50,12 @@ Configuration
|
|||
By default, journaled messages are written to a directory named ``journal`` in the current working directory. This
|
||||
can be changed by configuration where the specified path can be relative or absolute:
|
||||
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistenceDocSpec.scala#journal-config
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#journal-config
|
||||
|
||||
The default storage location of :ref:`snapshots-java` is a directory named ``snapshots`` in the current working directory.
|
||||
This can be changed by configuration where the specified path can be relative or absolute:
|
||||
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistenceDocSpec.scala#snapshot-config
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#snapshot-config
|
||||
|
||||
.. _processors-java:
|
||||
|
||||
|
|
@ -69,8 +69,12 @@ A processor can be implemented by extending the abstract ``UntypedProcessor`` cl
|
|||
|
||||
Processors only write messages of type ``Persistent`` to the journal, others are received without being persisted.
|
||||
When a processor's ``onReceive`` method is called with a ``Persistent`` message it can safely assume that this message
|
||||
has been successfully written to the journal. A ``UntypedProcessor`` itself is an ``Actor`` and can therefore
|
||||
be instantiated with ``actorOf``.
|
||||
has been successfully written to the journal. If a journal fails to write a ``Persistent`` message then the processor
|
||||
receives a ``PersistenceFailure`` message instead of a ``Persistent`` message. In this case, a processor may want to
|
||||
inform the sender about the failure, so that the sender can re-send the message, if needed, under the assumption that
|
||||
the journal recovered from a temporary failure.
|
||||
|
||||
An ``UntypedProcessor`` itself is an ``Actor`` and can therefore be instantiated with ``actorOf``.
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistenceDocTest.java#usage
|
||||
|
||||
|
|
@ -226,7 +230,7 @@ Snapshots
|
|||
|
||||
Snapshots can dramatically reduce recovery times. Processors can save snapshots of internal state by calling the
|
||||
``saveSnapshot`` method on ``Processor``. If saving of a snapshot succeeds, the processor will receive a
|
||||
``SaveSnapshotSucceeded`` message, otherwise a ``SaveSnapshotFailed`` message.
|
||||
``SaveSnapshotSuccess`` message, otherwise a ``SaveSnapshotFailure`` message.
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistenceDocTest.java#save-snapshot
|
||||
|
||||
|
|
@ -247,11 +251,59 @@ If not specified, they default to ``SnapshotSelectionCriteria.latest()`` which s
|
|||
To disable snapshot-based recovery, applications should use ``SnapshotSelectionCriteria.none()``. A recovery where no
|
||||
saved snapshot matches the specified ``SnapshotSelectionCriteria`` will replay all journaled messages.
|
||||
|
||||
Storage plugins
|
||||
===============
|
||||
|
||||
Storage backends for journals and snapshot stores are plugins in akka-persistence. The default journal plugin writes
|
||||
messages to LevelDB. The default snapshot store plugin writes snapshots as individual files to the local filesystem.
|
||||
Applications can provide their own plugins by implementing a plugin API and activate them by configuration. Plugin
|
||||
development requires the following imports:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#plugin-imports
|
||||
|
||||
Journal plugin API
|
||||
------------------
|
||||
|
||||
A journal plugin either extends ``SyncWriteJournal`` or ``AsyncWriteJournal``. ``SyncWriteJournal`` is an
|
||||
actor that should be extended when the storage backend API only supports synchronous, blocking writes. The
|
||||
methods to be implemented in this case are:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#sync-write-plugin-api
|
||||
|
||||
``AsyncWriteJournal`` is an actor that should be extended if the storage backend API supports asynchronous,
|
||||
non-blocking writes. The methods to be implemented in that case are:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#async-write-plugin-api
|
||||
|
||||
Message replays are always asynchronous, therefore, any journal plugin must implement:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#async-replay-plugin-api
|
||||
|
||||
A journal plugin can be activated with the following minimal configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#journal-plugin-config
|
||||
|
||||
The specified plugin ``class`` must have a no-arg constructor. The ``plugin-dispatcher`` is the dispatcher
|
||||
used for the plugin actor. If not specified, it defaults to ``akka.persistence.dispatchers.default-plugin-dispatcher``
|
||||
for ``SyncWriteJournal`` plugins and ``akka.actor.default-dispatcher`` for ``AsyncWriteJournal`` plugins.
|
||||
|
||||
Snapshot store plugin API
|
||||
-------------------------
|
||||
|
||||
A snapshot store plugin must extend the ``SnapshotStore`` actor and implement the following methods:
|
||||
|
||||
.. includecode:: code/docs/persistence/PersistencePluginDocTest.java#snapshot-store-plugin-api
|
||||
|
||||
A snapshot store plugin can be activated with the following minimal configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/persistence/PersistencePluginDocSpec.scala#snapshot-store-plugin-config
|
||||
|
||||
The specified plugin ``class`` must have a no-arg constructor. The ``plugin-dispatcher`` is the dispatcher
|
||||
used for the plugin actor. If not specified, it defaults to ``akka.persistence.dispatchers.default-plugin-dispatcher``.
|
||||
|
||||
Upcoming features
|
||||
=================
|
||||
|
||||
* Journal plugin API
|
||||
* Snapshot store plugin API
|
||||
* Reliable channels
|
||||
* Custom serialization of messages and snapshots
|
||||
* Extended deletion of messages and snapshots
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue