Interface HighRepJobPolicy

  • All Known Implementing Classes:

    public interface HighRepJobPolicy
    A policy for high replication job application. Implementations can decide whether or not a new job should apply and whether or not a job that initially failed to apply should roll forward on subsequent attempts.

    Some implementation details that are pertinent to implementors of this interface:

    When a user performs a non-transactional Put(), a non-transactional Delete(), or a Commit() of a transaction to which at least one transactional Put() or Delete() was added, the LocalDatastoreService attempts to apply that mutation using a job associated with the entity group of the mutation. The decision of whether or not the job should apply is delegated to shouldApplyNewJob(Key).

    Unapplied jobs may be rolled forward in two ways: when the consistency model dictates it, and when an entity group is groomed. We'll discuss these in turn.

    When the high replication consistency model guarantees that users will see the most up-to-date values for an entity group, we roll forward unapplied jobs before returning any data from the entity group. Specifically, a transactional Get() will roll forward any unapplied jobs in the entity group, as will a non-transactional Get() with the read policy set to STRONG (the default). A transactional Query (which is by definition an ancestor Query and therefore a scan over the entities in a single entity group) will also roll forward any unapplied jobs associated with the entity group of the query before the query executes.

    Unapplied jobs can also be rolled forward when the entity group with these jobs is groomed. In production, the groomer is a background process that is continuously scanning and rolling forward unapplied jobs. We considered implementing something similar, but it's nearly impossible to write tests in an environment where you have a background process randomly adjusting your persistent state, so we opted for a different approach: the local datastore groomer looks at all unapplied jobs on every Get() and every Query(), and for each unapplied job, consults shouldRollForwardExistingJob(Key) to see if that job should be rolled forward. This simulates grooming, but in a deterministic manner that makes testing much more straightforward.

    Note, however, that when the groomer rolls these jobs forward, it does so in such a way that the result of the operation being performed is not affected. This is important, because it guarantees that when a job fails to apply, a user who reads the entity group without any strong consistency guarantees will always see the "old" version of the data in the entity group at least once. Without this guarantee we would have jobs that failed to apply but whose failure was invisible, which defeats the purpose of what we're trying to simulate.

    • Method Detail

      • shouldApplyNewJob

        boolean shouldApplyNewJob(com.google.appengine.api.datastore.Key entityGroup)
        entityGroup - A unique identifier for the entity group.
        true if the new job should apply according to the policy, false otherwise.
      • shouldRollForwardExistingJob

        boolean shouldRollForwardExistingJob(com.google.appengine.api.datastore.Key entityGroup)
        entityGroup - A unique identifier for the entity group.
        true if the existing job should roll forward according to the policy, false otherwise.