All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.zeno.diff.history.DiffHistoryTracker Maven / Gradle / Ivy

The newest version!
/*
 *
 *  Copyright 2013 Netflix, Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *
 */
package com.netflix.zeno.diff.history;

import com.netflix.zeno.diff.DiffInstruction;
import com.netflix.zeno.diff.TypeDiffInstruction;
import com.netflix.zeno.fastblob.FastBlobStateEngine;
import com.netflix.zeno.util.SimultaneousExecutor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A data structure to track the history of changes in a single FastBlobStateEngine.

* * Each time the state engine consumes a blob file, call "addState()". Then, at any time, pass in a key for an object identified in the * TypeDiffInstruction to retrieve a historical record of that data.

* * This data structure retains a history of all changes in a data set across some specified number of rolling updates. Because this retains a * large amount of data, it can consume a significant memory footprint, and resource availability should be planned accordingly.

* * This class takes advantage of the guarantee that two identical objects across adjacent data states will be the same instance. Comparisons * can therefore be done with ==, rather than checking for identical serialized representations. * * @author dkoszewnik * */ public class DiffHistoryTracker { private final int historySizeToKeep; private final FastBlobStateEngine stateEngine; private final LinkedList historicalStates; private final Map> historicalStateHeaderTags; private final TypeDiffInstruction typeDiffInstructions[]; private DiffHistoryDataState currentDataState; /** * * @param numStatesToKeep - The number of historical states to keep * @param stateEngine - The state engine to track the history of * @param diffInstruction - The set of key extractions for types in the object model. */ public DiffHistoryTracker(int numStatesToKeep, FastBlobStateEngine stateEngine, DiffInstruction diffInstruction) { this.historySizeToKeep = numStatesToKeep; this.stateEngine = stateEngine; this.historicalStates = new LinkedList(); this.historicalStateHeaderTags = new ConcurrentHashMap>(); this.typeDiffInstructions = diffInstruction.getTypeInstructions(); } /** * Call this method after new data has been loaded by the FastBlobStateEngine. This will add a historical record * of the differences between the previous state and this new state. */ public void addState() { DiffHistoryDataState nextState = new DiffHistoryDataState(stateEngine, typeDiffInstructions); if(currentDataState != null) newHistoricalState(currentDataState, nextState); currentDataState = nextState; } private void newHistoricalState(final DiffHistoryDataState from, final DiffHistoryDataState to) { final DiffHistoricalState historicalState = new DiffHistoricalState(to.getVersion()); SimultaneousExecutor executor = new SimultaneousExecutor(); for(final TypeDiffInstruction typeInstruction : from.getTypeDiffInstructions()) { executor.execute(new Runnable() { public void run() { Map fromTypeState = from.getTypeState(typeInstruction.getTypeIdentifier()); Map toTypeState = to.getTypeState(typeInstruction.getTypeIdentifier()); historicalState.addTypeState(typeInstruction, fromTypeState, toTypeState); } }); } executor.awaitUninterruptibly(); historicalStates.addFirst(historicalState); historicalStateHeaderTags.put(to.getVersion(), new HashMap(stateEngine.getHeaderTags())); /// trim historical entries beyond desired size. if(historicalStates.size() > historySizeToKeep) { DiffHistoricalState removedState = historicalStates.removeLast(); historicalStateHeaderTags.remove(removedState.getVersion()); } } /** * Return the history of the object identified by the supplied type / key combination.

* * The returned list will contain one entry for each state in the rolling history retained by this DiffHistoryTracker. * The latest entry will be at index 0, and the earliest entry will be the last in the list. Not all entries in the returned * list must reflect a transition; an entry is included in the list whether or not the instance changed for a given state. * */ public List> getObjectHistory(String type, Object key) { List> states = new ArrayList>(historicalStates.size()); Map typeState = currentDataState.getTypeState(type); /// start with the currently available item (if available) T currentItem = typeState.get(key); /// and work backwards through history. for(DiffHistoricalState state : historicalStates) { DiffHistoricalTypeState historicalState = state.getTypeState(type); Map diffObjects = historicalState.getDiffObjects(); Map deletedObjects = historicalState.getDeletedObjects(); Set newObjects = historicalState.getNewObjects(); T previous; if(diffObjects.containsKey(key)) { previous = diffObjects.get(key); } else if(deletedObjects.containsKey(key)) { previous = deletedObjects.get(key); } else if(newObjects.contains(key)) { previous = null; } else { previous = currentItem; } /// adding the from -> to objects (whether identical or not) to a list. states.add(new DiffObjectHistoricalTransition(state.getVersion(), previous, currentItem)); currentItem = previous; } return states; } /** * Returns a list of the historical states, starting with the most recent and ending with the oldest. */ public List getHistoricalStates() { return Collections.unmodifiableList(historicalStates); } /** * Returns the header tags which were attached to the given version. */ public Map getHistoricalStateHeaderTags(String stateVersion) { return historicalStateHeaderTags.get(stateVersion); } }