!per #18463 Make Persistence Query API explorable

* make the standard queries "single method interfaces" that may be implemented
  by a query journal plugin
* remove hints (major problems with varargs anyway), the hints for standard
  queries  should be given in configuration instead, e.g. refresh-interval
This commit is contained in:
Patrik Nordwall 2015-09-14 11:08:22 +02:00
parent a45f31cecb
commit 5bd245fbc8
46 changed files with 1487 additions and 948 deletions

View file

@ -51,20 +51,20 @@ Read Journals
In order to issue queries one has to first obtain an instance of a ``ReadJournal``.
Read journals are implemented as `Community plugins`_, each targeting a specific datastore (for example Cassandra or JDBC
databases). For example, given a library that provides a ``akka.persistence.query.noop-read-journal`` obtaining the related
databases). For example, given a library that provides a ``akka.persistence.query.my-read-journal`` obtaining the related
journal is as simple as:
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#basic-usage
Journal implementers are encouraged to put this identifier in a variable known to the user, such that one can access it via
``getJournalFor(NoopJournal.identifier)``, however this is not enforced.
``getJournalFor(NoopJournal.class, NoopJournal.identifier)``, however this is not enforced.
Read journal implementations are available as `Community plugins`_.
Predefined queries
------------------
Akka persistence query comes with a number of ``Query`` objects built in and suggests Journal implementors to implement
Akka persistence query comes with a number of query interfaces built in and suggests Journal implementors to implement
them according to the semantics described below. It is important to notice that while these query types are very common
a journal is not obliged to implement all of them - for example because in a given journal such query would be
significantly inefficient.
@ -75,34 +75,37 @@ significantly inefficient.
The predefined queries are:
AllPersistenceIds
^^^^^^^^^^^^^^^^^
AllPersistenceIdsQuery and CurrentPersistenceIdsQuery
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``AllPersistenceIds`` which is designed to allow users to subscribe to a stream of all persistent ids in the system.
``allPersistenceIds`` which is designed to allow users to subscribe to a stream of all persistent ids in the system.
By default this stream should be assumed to be a "live" stream, which means that the journal should keep emitting new
persistence ids as they come into the system:
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#all-persistence-ids-live
If your usage does not require a live stream, you can disable refreshing by using *hints*, providing the built-in
``NoRefresh`` hint to the query:
If your usage does not require a live stream, you can use the ``currentPersistenceIds`` query:
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#all-persistence-ids-snap
EventsByPersistenceId
^^^^^^^^^^^^^^^^^^^^^
EventsByPersistenceIdQuery and CurrentEventsByPersistenceIdQuery
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``EventsByPersistenceId`` is a query equivalent to replaying a :ref:`PersistentActor <event-sourcing-scala>`,
``eventsByPersistenceId`` is a query equivalent to replaying a :ref:`PersistentActor <event-sourcing-java>`,
however, since it is a stream it is possible to keep it alive and watch for additional incoming events persisted by the
persistent actor identified by the given ``persistenceId``. Most journals will have to revert to polling in order to achieve
this, which can be configured using the ``RefreshInterval`` query hint:
persistent actor identified by the given ``persistenceId``.
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#events-by-persistent-id-refresh
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#events-by-persistent-id
EventsByTag
^^^^^^^^^^^
Most journals will have to revert to polling in order to achieve this,
which can typically be configured with a ``refresh-interval`` configuration property.
``EventsByTag`` allows querying events regardless of which ``persistenceId`` they are associated with. This query is hard to
If your usage does not require a live stream, you can use the ``currentEventsByPersistenceId`` query.
EventsByTag and CurrentEventsByTag
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``eventsByTag`` allows querying events regardless of which ``persistenceId`` they are associated with. This query is hard to
implement in some journals or may need some additional preparation of the used data store to be executed efficiently.
The goal of this query is to allow querying for all events which are "tagged" with a specific tag.
That includes the use case to query all domain events of an Aggregate Root type.
@ -134,6 +137,7 @@ query has an optionally supported offset parameter (of type ``Long``) which the
For example a journal may be able to use a WHERE clause to begin the read starting from a specific row, or in a datastore
that is able to order events by insertion time it could treat the Long as a timestamp and select only older events.
If your usage does not require a live stream, you can use the ``currentEventsByTag`` query.
Materialized values of queries
------------------------------
@ -142,11 +146,14 @@ which are a feature of `Akka Streams`_ that allows to expose additional values a
More advanced query journals may use this technique to expose information about the character of the materialized
stream, for example if it's finite or infinite, strictly ordered or not ordered at all. The materialized value type
is defined as the ``M`` type parameter of a query (``Query[T,M]``), which allows journals to provide users with their
is defined as the second type parameter of the returned ``Source``, which allows journals to provide users with their
specialised query object, as demonstrated in the sample below:
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#materialized-query-metadata-classes
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#materialized-query-metadata
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#advanced-journal-query-types
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#advanced-journal-query-definition
.. includecode:: code/docs/persistence/PersistenceQueryDocTest.java#advanced-journal-query-usage
.. _materialized values: http://doc.akka.io/docs/akka-stream-and-http-experimental/1.0/java/stream-quickstart.html#Materialized_values
.. _Akka Streams: http://doc.akka.io/docs/akka-stream-and-http-experimental/1.0/java.html
@ -154,7 +161,7 @@ specialised query object, as demonstrated in the sample below:
Performance and denormalization
===============================
When building systems using :ref:`event-sourcing-scala` and CQRS (`Command & Query Responsibility Segragation`_) techniques
When building systems using :ref:`event-sourcing-java` and CQRS (`Command & Query Responsibility Segragation`_) techniques
it is tremendously important to realise that the write-side has completely different needs from the read-side,
and separating those concerns into datastores that are optimised for either side makes it possible to offer the best
expirience for the write and read sides independently.
@ -232,8 +239,13 @@ Most users will not need to implement journals themselves, except if targeting a
ReadJournal plugin API
----------------------
Journals *MUST* return a *failed* ``Source`` if they are unable to execute the passed in query.
For example if the user accidentally passed in an ``SqlQuery()`` to a key-value journal.
A read journal plugin must implement ``akka.persistence.query.ReadJournalProvider`` which
creates instances of ``akka.persistence.query.scaladsl.ReadJournal`` and
``akka.persistence.query.javaadsl.ReadJournal``. The plugin must implement both the ``scaladsl``
and the ``javadsl`` interfaces because the ``akka.stream.scaladsl.Source`` and
``akka.stream.javadsl.Source`` are different types and even though those types can easily be converted
to each other it is most convenient for the end user to get access to the Java or Scala ``Source`` directly.
As illustrated below one of the implementations can delegate to the other.
Below is a simple journal implementation:
@ -243,6 +255,12 @@ And the ``EventsByTag`` could be backed by such an Actor for example:
.. includecode:: code/docs/persistence/query/MyEventsByTagJavaPublisher.java#events-by-tag-publisher
If the underlying datastore only supports queries that are completed when they reach the
end of the "result set", the journal has to submit new queries after a while in order
to support "infinite" event streams that include events stored after the initial query
has completed. It is recommended that the plugin use a configuration property named
``refresh-interval`` for defining such a refresh interval.
Plugin TCK
----------