Annotations

io.baratine.core

@AfterBatch

@AfterBatch marks a method to be called when a batch of messages has completed. Because Baratine services have an inbox queue, calls are processed in a batch that lasts as long as the inbox queue has entries. When the inbox queue is empty, the @AfterBatch is called.

A typical use of @AfterBatch is flushing data to a stream.

@AfterBatch
private void afterBatch()
{
  _stream.flush();
}

@BeforeBatch

@BeforeBatch is a method annotation used with @AfterBatch to set a thread-context for a batch. For example, a database service might allocate a Connection in the @BeforeBatch and free it in the @AfterBatch.

@Direct

@Direct is a method annotation used to bypass the inbox.

@Direct
public String getValue(String key)
{
  return _concurrentHashMap.get(key);
}

@Journal

@Journal is a class annotation that adds a journal to a persistent service. @Journal works together with @OnSave and @Modify to add reliability, by saving method calls before they are executed, and replaying them after a server crash.

Attaches a journal to the inbox for reliability. Requests are saved to the journal before the the service receives the requests. When Baratine rolls over the journal, the service’s @OnSave is called (if @Modify has been called at least once). @Journal and @OnSave work hand-in-hand to batch many service state changes into a single save/write.

@Lookup

@Lookup is a field annotation used with @Inject to inject a service proxy with a given URL.

@Inject @Lookup("pod://my-pod/my-service")
MyService _service;

@Modify

@Modify is a method annotation that marks a method as modifying persistent state. @Modify works together with @OnSave and optionally @Journal to save the service state to its backing database when modified.

After each batch for a persistent service, the service will call its @OnSave method if at least one @Modify method has been called in the batch. If no @Modify methods have been called, the @OnSave will not be called.

@Modify
public int getAndIncrement()
{
  return _counter++;
}

@OnActive

@OnActive is a method annotation called as the last startup lifecycle method, after all replay methods have been called. Applications that need to distinguish between normal methods and replay methods can use @OnActive to set the service into its active state.

@OnActiveNew

@OnActiveNew is a method annotation used for very specific applications that need to distinguish between replay methods that were called before shutdown and replay methods that were never called.

@OnConsume

@OnConsume is a method annotation used to register consumers. Consumers are subscribers that consume a published message. Only one consumer will receive a published message.

@OnDestroy

@OnDestroy is a method annotation called when the service shuts down.

@OnInit

@OnInit is a method annotation called when the service initializes. @OnInit is the first lifecycle method called.

@OnLoad

@OnLoad is a method annotation called when the service is loaded, before any application methods. When @OnLoad is slow, pending application methods are saved and executed only when the @OnLoad completes.

@OnLoad
public void onLoad(Result<Void> result)
{
  // load the service's state from the Store
  _store.get(myKey, v->afterLoad(v));
}

private Void afterLoad(AuctionData data)
{
  _data = data;

  return null;
}

@OnLookup

@OnLookup is a method annotation called for a ServiceManager.lookup call.

@OnLookup is not an inbox method. Because it uses the caller’s context, it must not block, call service methods, or load from a database. Any initialization of the child must occur in the child’s own @OnInit or @OnLoad.

Generally, @OnLookup will only call a constructor for the new item, where the constructor only saves the path and possibly a reference to the parent.

Called to lookup a child url. For example, if your service is located at /myService, then @OnLookup would be called for calls to /myService/123. If the parent service does not define @OnLookup, then calls to child services would return an error. @OnLookup is called only once and the result is LRU-cached. Child services share the parent’s Inbox. @OnLookup may return the parent itself.

Note: contrary to other annotations, @OnLookup must be a blocking call (i.e. needs to return an object). Therefore, it should not do any expensive operations and should just return a skeleton object. Baratine will later call that skeleton’s @OnLoad to initialize it.

@OnLookup
public MyServiceChild onLookup(String url)
{
  return new MyServiceChild(url);
}

@OnSave

@OnSave is a method annotation called after a batch to save any changes to the service. @OnSave is only called when the batch includes at least one @Modify method.

@OnSave
public void onSave(Result<Void> result)
{
  // save the service's state to the Store
  _store.put(myKey, _myList, result.from(x->null));
}

@OnSubscribe

@OnSubscribe is a method annotation used to register subscribers. Unlike consumers, all subscribers will receive a copy of a published message.

@Queue

@Queue is a class annotation to configure properties of the inbox’s queue.

@Service

@Service marks a class as a service implementation. On startup, Baratine will scan for @Service classes and bind their address to the current service manager.

@Service("public:///my-service")
public class MyServiceImpl {
}

The URL will typically either be one of:

  • public:
  • pod:
  • local:
  • session:

The following are valid service URLs:

@Service("public:///myService")   // publicly accessible from remote clients
@Service("pod:///myService")      // accessible only from within the same pod
@Service("local:///myService")    // local to the JVM instance
@Service("/myService")            // defaults to local

@SessionId

SessionId is a field annotation for session services. In a session service, a new service instance is created for each session and assigned a session id that typically matches a HTTP cookie. The @SessionId annotation marks a field to be willed with the HTTP cookie when the session is created.

@Startup

@Startup is a class annotation for a service that starts when the system starts. A service marked with @Startup will start when the pod starts. Other services will only start lazily.

@Workers

@Workers is a class annotation for a multi-threaded gateway service. All workers share the same inbox, but only one worker will process each method call.

Multi-worker services are typically used as gateway services to existing blocking libraries, where each thread might block waiting for a response.

For example, a data access service is a gateway that load data from an external JDBC database. Because a JDBC query will block until the response, the multiple workers can give the illusion of a single non-blocking service to callers of the gateway service.

@Workers(10)  // 10 threads
@Service("public:///threadedService")
public class MultiThreadedService
{
  ...
}

Non-persistent Lifecycle

A non-persistent service is a service without @OnLoad or @OnSave methods. Facades, computation services, gateways, and routing services are examples of non-persistent services.

The @OnInit method starts the service, called before any other methods, and called within the service context, i.e. on the service thread. Typically the @OnInit is used to lookup dependent service and register for any events or subscriptions.

The @OnDestroy method closes the service, called when the application shuts down gracefully. For testing purposes, the --immediate argument to the stop command bypasses destroy methods, allowing testing of non-graceful shutdown or craches.

The @BeforeBatch and @AfterBatch methods are called before and after batches of application methods. @AfterBatch is typically for flushing applications, and @BeforeBatch can setup thread-local contexts. Batching becomes more efficient under heavy load because more application methods are called within a batch.

@OnInit
@OnActive
...
@BeforeBatch
method1
method2
method3
@AfterBatch
...
@OnDestroy

Persistent Lifecycle

Key concepts for Baratine persistence:

  • Because services own their data, @OnLoad called once.
  • Only @Modify methods are saved.
  • @Journal replays are after @OnLoad and before @OnActive

Data ownership is strict in Baratine; data is owned by exactly one service. @OnLoad is called once when the service starts to load its data. After the load, the in-memory value is authoritative because the service is the only owner.

@OnSave is at the end of a batch for efficiency. Under heavy load, multiple updates are batched into a single write. Services with children, such as a user service, batch all child updates into a single save because services with children use a single service thread and context.

Only @Modify methods trigger an @OnSave. If a batch of methods are non-modify methods, no save is required.

When a service has a @Journal annotation, and the service restarts without a graceful shutdown, the pending journalled methods are replayed after the @OnLoad and before the @OnActive. For testing, the --immediate flag to stop can simulate a non-graceful shutdown, allowing the replay to be tested.

A @Journal annotation can have a delay=120000 argument, which delays the @OnSave call. The delay argument can be used to improve write efficiency by increasing write batching and reducing @OnSave calls.

@OnInit and @OnActive can be used to control side-effects during journal replay. The journal replay is intended to restore the in-memory state, but it should not replay side-effects. The @OnActive can be used to enable application side-effects after the replay is complete.

@OnInit
@BeforeBatch
@OnLoad
replay-method1
replay-method2
@OnActive
@AfterBatch
...
@BeforeBatch
modify-method1
method2
method3
@OnSave
@AfterBatch
...
@BeforeBatch
non-modify-method1
@AfterBatch
...
@OnDestroy