Skip to content

Tracked Views

Jan Wiemer edited this page Dec 27, 2020 · 10 revisions

Tracked views

Tracked views can be used to track values derived from the original stored object automatically up to date. This avoids the need to compute the values again and again. When creating the store the tracked views (implementing the TrackedView<ObjectType> interface where ObjectType is the class of the stored objects) are registered at the tracked view registry of the store. Later it is possible to access a snapshot of the tracked view by calling the getView method at the tracked view registry. Note that the snapshot includes all changes done inside the transaction where it was taken and all committed changes by other transactions.

First we show an example of a tracked view tracking the total balance of all stored accounts:

  public static class TotalBalanceView implements TrackedView<Account> {

    private long totalBalance = 0;

    // tracked views must be cloneable to create the snapshots...
    @SuppressWarnings("unchecked")
    @Override
    public TrackedView<Account> clone() {
      try {
        return (TrackedView<Account>) super.clone();
      } catch (CloneNotSupportedException e) {
        throw new RuntimeException("clone failed");
      }
    }

    // the method actually tracking the modification of an object
    // (for creations / deletions the oldValue / newValue is null)
    @Override
    public void trackModification(Account oldValue, Account newValue) {
      totalBalance += newValue == null ? 0 : newValue.getBalance();
      totalBalance -= oldValue == null ? 0 : oldValue.getBalance();
    }

    // An (optional) method to check the value of the view.
    // This is called after commit if configured in the JacisObjectTypeSpec passed to create the store.
    @Override
    public void checkView(List<Account> values) {
      long checkValue = values.stream().mapToLong(a -> a.getBalance()).sum();
      if (totalBalance != checkValue) {
        throw new IllegalStateException("Corrupt view! Tracked value=" + totalBalance + " computed value=" + checkValue);
      }
    }

    @Override
    public void clear() {
      totalBalance = 0;
    }

    public long getTotalBalance() {
      return totalBalance;
    }

  }

The tracked view is registered after creation of the store. Later it is possible to get a snapshot of the view either inside or without a transaction.

    JacisContainer container = new JacisContainer();
    JacisObjectTypeSpec<String, Account> objectTypeSpec = new JacisObjectTypeSpec<>(String.class, Account.class);
    JacisStore<String, Account> store = container.createStore(objectTypeSpec);

    // First register the tracked view

    store.getTrackedViewRegistry().registerTrackedView(new TotalBalanceView());

    // First we create some accounts to have some test data...

    container.withLocalTx(() -> {
      store.update("account1", new Account("account1").deposit(-100));
      store.update("account2", new Account("account2").deposit(10));
      store.update("account3", new Account("account3").deposit(100));
    });

    // on commit the tracked view is updated automatically
    TotalBalanceView view0 = store.getTrackedViewRegistry().getView(TotalBalanceView.class);
    System.out.println("tracked balance=" + view0.getTotalBalance());

    // inside a transaction the transaction local view of the values is respected
    container.withLocalTx(() -> {
      store.update("account1", store.get("account1").deposit(1000));
      store.update("account4", new Account("account4").deposit(101));
      // note that the getView method takes a snapshot at the time it is called...
        TotalBalanceView view1 = store.getTrackedViewRegistry().getView(TotalBalanceView.class);
        System.out.println("tracked balance=" + view1.getTotalBalance());
        // later updates are not tracked by this snapshot
        store.update("account1", store.get("account1").deposit(1000));
        System.out.println("tracked balance old snapshot=" + view1.getTotalBalance());
        TotalBalanceView view2 = store.getTrackedViewRegistry().getView(TotalBalanceView.class);
        System.out.println("tracked balance new snapshot=" + view2.getTotalBalance());
      });