Lifecycle of a Freshener
Lifecycle of a Freshener
For this example we will create a Freshener with stock AlwaysFreshen policy and an imaginary ScoreFunction
implementation called com.mycompany.RecommendingScoreFunction.
First we register the Freshener to a column in our table using the Fresh tool CLI.
kiji fresh --target=kiji://.env/default/users/derived:recommendations \
--policy-class=org.kiji.scoring.lib.AlwaysFreshen \
--score-function-class=com.mycompany.RecommendingScoreFunction \
--instantiate-classes
This command registers a Freshener to the column 'derived:recommendations' in the table 'users' with the policy class AlwaysFreshen and ScoreFunction
class RecommendingScoreFunction. The --instantiate-classes flag tells the tool that it should invoke the empty default constructors of both classes and call serializeToParameters
on the resulting objects during registration. The output of each serializeToParameters
and any manually specified parameters (here there are none) are merged with key collisions resolved by the following order of precedence: manually specified parameters have highest precedence followed by the policy’s serialized parameters, with the ScoreFunction
’s serialized parameters last.
Now that our Freshener is registered, FreshKijiTableReader
s can load it and use it to freshen data. Regular KijiTableReader
instances will not freshen, regardless of Fresheners attached to requested columns. Opening a FreshKijiTableReader
configured to freshen 'derived:recommendations' will cause it to load and setup both the policy and ScoreFunction
. We can open a reader to freshen 'derived:recommendations' as follows:
final KijiColumnName recsCol = new KijiColumnName("derived", "recommendations");
final FreshKijiTableReader freshReader = FreshKijiTableReader.Builder.create()
.withTable(usersTable)
.withColumnsToFreshen(Lists.newArrayList(recsCol))
.build();
As part of the initialization of the reader, it will retrieve the Freshener record we made earlier and setup the classes found there. The Freshener record contains the names of the policy and score function classes as well as any serialized parameters provided during registration. Once it has retrieved the record, the reader first invokes the empty default constructors of both classes. With the newly created KijiFreshnessPolicy
and ScoreFunction
objects and a FreshenerGetStoresContext
created from the Freshener record it calls getRequiredStores
on the policy and ScoreFunction
. If both classes define a KeyValueStore
with the same name, the policy’s store will override the ScoreFunction
’s. The FreshenerGetStoresContext
is then combined with the newly created KeyValueStores
to create a FreshenerSetupContext
, which is passed to the policy and then ScoreFunction
setup methods. The now setup policy and ScoreFunction
are cached in the reader to minimize the cost of read requests which they may freshen.
Once all Fresheners have been loaded and setup, the reader is open for requests. When the reader receives a request that includes 'derived:recommendations' the Freshener setup earlier will run. First, the policy’s shouldUseClientDataRequest
method will be called. The AlwaysFreshen policy's logic is very simple: It always returns false from isFresh
, regardless of the data, so as an optimization shouldUseClientDataRequest
returns false. This causes AlwaysFreshen's getDataRequest
to be called for the KijiDataRequest
that will be used to read data from the backing Kiji table. In the case of AlwaysFreshen, however, this data request is empty, allowing the system to generate an empty KijiRowData
without a trip to Kiji.
NOTE: A request to a fresh reader may include parameter overrides which are merged with existing parameters created earlier during registration. These overrides have the highest priority if there are key conflicts.
This empty row data will be passed to isFresh
which is hard coded to return false every time. Because the policy indicates that data is stale, the ScoreFunction
will run to refresh the data. First the ScoreFunction
’s getDataRequest
method is called and applied to the table to retrieve any data necessary for scoring. In the case of RecommendingScoreFunction this data request may include a user’s purchasing history and other distinguishing information. The row data containing this user information is passed to score
which uses it to calculate a new recommendation for the user. The score
method returns this new recommendation and the FreshKijiTableReader
writes it to the Kiji table and returns it to the client who requested it. Requests like this may run many times and, in the presence of multiple threads, may run concurrently with the same policy and ScoreFunction
objects.
When a fresh reader is closed, or a Freshener record is changed or removed, loaded Fresheners may be unloaded and cleaned up. The cleanup process involves creating a FreshenerSetupContext
from the configuration in the Freshener record and calling the policy and ScoreFunction
cleanup methods with that context.
KijiScoring User Guide
- What is Scoring
- KijiFreshnessPolicy
- ScoreFunction
- FreshenerContext
- Lifecycle of a Freshener
- FreshKijiTableReader
- Metadata Management
- PMML ScoreFunction