package org.sef4j.callstack.stattree.changes;
import java.util.Map;
import java.util.function.Function;
import org.sef4j.callstack.stats.PendingPerfCount;
import org.sef4j.callstack.stats.PerfStats;
import org.sef4j.callstack.stats.dto.PendingCountPropTreeValueProviderDef;
import org.sef4j.core.helpers.export.ExportFragmentsAdder;
import org.sef4j.core.helpers.proptree.changes.AbstractPropTreeValueProvider;
import org.sef4j.core.helpers.proptree.model.PropTreeNode;
import org.sef4j.core.util.factorydef.AbstractSharedObjByDefFactory;
import org.sef4j.core.util.factorydef.DependencyObjectCreationContext;
/**
* Collector of changed PendingPerfCount since previous copy
*
* usage note:
* PendingPerfCount reflects the running activity of the process, and changes could be collected
* in pseudo real-time with a fast period (example: every 10 seconds).
* On the contrary, average statistics could be collected with a slower period (example: every 1 minute)
*
* Changes in PendingPerfCount can be optimized much more than changes in statistics, because of constraints
* between parent and child pendings counters.
*
*
* Optimisation note:
* knowing that sum_child pending counts <= pendings counts
* => can skip part of recursions, when src.pending=0 or prev.pending=0..
*
* indeed:
* <PRE>
* foo() { /foo.pending /foo/bar.pending
* push --> incrPending() "/foo" 1 0
*
* bar(); --> incrPending() "/foo/bar" 1 1
* --> decrPending() "/foo/bar" 1 0
*
* pop() --> decrPending(); "/foo" 0 0
* }
* </PRE>
*/
public final class PendingCountPropTreeValueProvider extends AbstractPropTreeValueProvider<PendingPerfCount> {
public static final Function<PropTreeNode, PendingPerfCount> DEFAULT_PENDING_SRC_COPY_EXTRACTOR =
new Function<PropTreeNode, PendingPerfCount>() {
@Override
public PendingPerfCount apply(PropTreeNode t) {
PerfStats tmpRes = // t.getOrCreateProp("stats", PerfStats.FACTORY);
t.getPropOrNull("stats", PerfStats.class);
return (tmpRes != null)? tmpRes.getPendingCounts().clone() : null;
}
};
public static final Function<PropTreeNode, PendingPerfCount> DEFAULT_PENDING_PREV_EXTRACTOR =
new Function<PropTreeNode, PendingPerfCount>() {
@Override
public PendingPerfCount apply(PropTreeNode t) {
return t.getOrCreateProp("stats", PerfStats.FACTORY).getPendingCounts();
}
};
public static final Function<PropTreeNode, PendingPerfCount> createGetOrCreatePropPendingExtractor(final String propName) {
return new Function<PropTreeNode, PendingPerfCount>() {
@Override
public PendingPerfCount apply(PropTreeNode t) {
return t.getOrCreateProp(propName, PendingPerfCount.FACTORY);
}
};
}
// ------------------------------------------------------------------------
public PendingCountPropTreeValueProvider(PropTreeNode srcRoot) {
super(srcRoot, PropTreeNode.newRoot(), DEFAULT_PENDING_SRC_COPY_EXTRACTOR, DEFAULT_PENDING_PREV_EXTRACTOR);
}
public PendingCountPropTreeValueProvider(
PropTreeNode srcRoot,
PropTreeNode prevRoot,
Function<PropTreeNode, PendingPerfCount> srcCopyExtractor,
Function<PropTreeNode, PendingPerfCount> prevExtractor) {
super(srcRoot, prevRoot, srcCopyExtractor, prevExtractor);
}
// ------------------------------------------------------------------------
@Override
protected void provideFragments(PropTreeNode src, String currPath,
ExportFragmentsAdder<PendingPerfCount> out) {
PendingPerfCount srcPendingsCopy = srcValueCopyExtractor.apply(src);
out.putIdentifiableFragment(this, currPath, srcPendingsCopy, 0);
}
@Override
protected void markAndCollectChanges(PropTreeNode src, PropTreeNode prev, String currPath, ExportFragmentsAdder<PendingPerfCount> res) {
throw new IllegalStateException(); // cf overriden recursiveMarkAndCollectChanges
}
@Override
protected void recursiveMarkAndCollectChanges(PropTreeNode src, PropTreeNode prev,
String currPath, ExportFragmentsAdder<PendingPerfCount> out) {
PendingPerfCount srcPendingsCopy = srcValueCopyExtractor.apply(src);
PendingPerfCount prevPendings = prevValueExtractor.apply(prev);
int srcPendingCount = srcPendingsCopy.getPendingCount();
int prevPendingCount = prevPendings.getPendingCount();
if (srcPendingCount == 0 && prevPendingCount == 0) {
// skip recurse compare: both empty!
return;
}
if (srcPendingCount != prevPendingCount
|| srcPendingsCopy.getPendingSumStartTime() != prevPendings.getPendingSumStartTime()
) {
prevPendings.set(srcPendingsCopy);
out.putIdentifiableFragment(this, currPath, srcPendingsCopy, 0);
}
if (srcPendingCount != 0 && prevPendingCount != 0) {
// recurse
for(Map.Entry<String, PropTreeNode> srcEntry : src.getChildMap().entrySet()) {
String childName = srcEntry.getKey();
PropTreeNode srcChild = srcEntry.getValue();
PropTreeNode prevChild = prev.getOrCreateChild(childName);
String childPath = currPath + "/" + childName;
// *** recurse ***
recursiveMarkAndCollectChanges(srcChild, prevChild, childPath, out);
}
} else if (srcPendingCount == 0) {
// recurse optim removePending
for(Map.Entry<String, PropTreeNode> srcEntry : src.getChildMap().entrySet()) {
String childName = srcEntry.getKey();
PropTreeNode srcChild = srcEntry.getValue();
PropTreeNode prevChild = prev.getOrCreateChild(childName);
String childPath = currPath + "/" + childName;
PendingPerfCount prevChildPendings = prevValueExtractor.apply(prevChild);
// *** recurse ***
recursiveMarkAndCollectChanges_removePending(srcChild, prevChild, prevChildPendings, childPath, out);
}
} else if (prevPendingCount == 0) {
// recurse optim addPending
for(Map.Entry<String, PropTreeNode> srcEntry : src.getChildMap().entrySet()) {
String childName = srcEntry.getKey();
PropTreeNode srcChild = srcEntry.getValue();
PropTreeNode prevChild = prev.getOrCreateChild(childName);
String childPath = currPath + "/" + childName;
PendingPerfCount srcChildPendingsCopy = srcValueCopyExtractor.apply(srcChild);
// *** recurse ***
recursiveMarkAndCollectChanges_addPending(srcChild, srcChildPendingsCopy, prevChild, childPath, out);
}
}
// compare child removal... not used!
}
/** optimized version of recursiveMarkAndCollectChanges() with src.pendingsCount=0 */
protected void recursiveMarkAndCollectChanges_removePending(
PropTreeNode src, PropTreeNode prev, PendingPerfCount prevPendings,
String currPath, ExportFragmentsAdder<PendingPerfCount> res) {
// 0 ... PendingPerfCount srcPendingsCopy = srcValueCopyExtractor.apply(src);
// PendingPerfCount prevPendings = prevValueExtractor.apply(prev);
int prevPendingCount = prevPendings.getPendingCount();
if (prevPendingCount == 0) {
// skip recurse compare: both empty!
return;
}
// collect change
prevPendings.clear();
res.putIdentifiableFragment(this, currPath, prevPendings, 0);
// loop child (until reaching sum=prevPendings)
int remainingMaxChildPrevPendingCount = prevPendingCount;
for(Map.Entry<String, PropTreeNode> srcEntry : src.getChildMap().entrySet()) {
String childName = srcEntry.getKey();
PropTreeNode srcChild = srcEntry.getValue();
PropTreeNode prevChild = prev.getOrCreateChild(childName);
String childPath = currPath + "/" + childName;
PendingPerfCount prevChildPendings = prevValueExtractor.apply(prev);
// *** recurse ***
recursiveMarkAndCollectChanges_removePending(srcChild, prevChild, prevChildPendings,
childPath, res);
remainingMaxChildPrevPendingCount -= prevChildPendings.getPendingCount();
if (remainingMaxChildPrevPendingCount == 0) {
break; // optim.. expecting no more child with prev pendingCount != 0
}
}
}
/** optimized version of recursiveMarkAndCollectChanges() with prev.pendingsCount=0 */
protected void recursiveMarkAndCollectChanges_addPending(
PropTreeNode src, PendingPerfCount srcPendingsCopy, PropTreeNode prev,
String currPath, ExportFragmentsAdder<PendingPerfCount> res) {
// PendingPerfCount srcPendingsCopy = srcValueCopyExtractor.apply(src);
PendingPerfCount prevPendings = prevValueExtractor.apply(prev);
// 0 ... prevPendings.getPendingCount()
int srcPendingCount = srcPendingsCopy.getPendingCount();
if (srcPendingCount == 0) {
// skip recurse compare: both empty!
return;
}
// collect change
prevPendings.set(srcPendingsCopy);
res.putIdentifiableFragment(this, currPath, srcPendingsCopy, 0);
// loop child (until reaching sum=prevPendings)
int remainingMaxChildSrcPendingCount = srcPendingCount;
for(Map.Entry<String, PropTreeNode> srcEntry : src.getChildMap().entrySet()) {
String childName = srcEntry.getKey();
PropTreeNode srcChild = srcEntry.getValue();
PropTreeNode prevChild = prev.getOrCreateChild(childName);
String childPath = currPath + "/" + childName;
PendingPerfCount srcChildPendingsCopy = srcValueCopyExtractor.apply(srcChild);
// *** recurse ***
recursiveMarkAndCollectChanges_addPending(srcChild, srcChildPendingsCopy, prevChild, childPath, res);
remainingMaxChildSrcPendingCount -= srcChildPendingsCopy.getPendingCount();
if (remainingMaxChildSrcPendingCount == 0) {
break; // optim.. expecting no more child with src pendingCount != 0
}
}
}
// ------------------------------------------------------------------------
public static class Factory
extends ExportFragmentsProviderFactory<PendingCountPropTreeValueProviderDef,PendingCountPropTreeValueProvider> {
public Factory() {
super("PendingCountPropTreeValueProvider", PendingCountPropTreeValueProviderDef.class);
}
@Override
public PendingCountPropTreeValueProvider create(
PendingCountPropTreeValueProviderDef def,
DependencyObjectCreationContext ctx) {
PropTreeNode rootNode = ctx.getOrCreateDependencyByDef("rootNode", def.getRootNodeDef());
return new PendingCountPropTreeValueProvider(rootNode);
}
}
}