Skip to content

Commit

Permalink
Moved aggregation logic to API PR
Browse files Browse the repository at this point in the history
Signed-off-by: Peter Alfonsi <[email protected]>
  • Loading branch information
Peter Alfonsi committed Apr 11, 2024
1 parent 2c7f431 commit fddd56e
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,74 +131,6 @@ public long getTotalEntries() {
return getTotalStats().getEntries();
}

/**
* Returns a new tree containing the stats aggregated by the levels passed in. The root node is a dummy node,
* whose name and value are null. The new tree only has dimensions matching the levels passed in.
*/
MDCSDimensionNode aggregateByLevels(List<String> levels) {
List<String> filteredLevels = filterLevels(levels);
MDCSDimensionNode newRoot = new MDCSDimensionNode("", true, statsRoot.getStats());
for (MDCSDimensionNode child : statsRoot.children.values()) {
aggregateByLevelsHelper(newRoot, child, filteredLevels, 0);
}
return newRoot;
}

void aggregateByLevelsHelper(
MDCSDimensionNode parentInNewTree,
MDCSDimensionNode currentInOriginalTree,
List<String> levels,
int depth
) {
if (levels.contains(dimensionNames.get(depth))) {
// If this node is in a level we want to aggregate, create a new dimension node with the same value and stats, and connect it to
// the last parent node in the new tree. If it already exists, increment it instead.
String dimensionValue = currentInOriginalTree.getDimensionValue();
MDCSDimensionNode nodeInNewTree = parentInNewTree.children.get(dimensionValue);
if (nodeInNewTree == null) {
// Create new node with stats matching the node from the original tree
int indexOfLastLevel = dimensionNames.indexOf(levels.get(levels.size() - 1));
boolean isLeafNode = depth == indexOfLastLevel; // If this is the last level we aggregate, the new node should be a leaf
// node
nodeInNewTree = new MDCSDimensionNode(dimensionValue, !isLeafNode, currentInOriginalTree.getStats());
parentInNewTree.children.put(dimensionValue, nodeInNewTree);
} else {
// Otherwise increment existing stats
CacheStatsCounterSnapshot newStats = CacheStatsCounterSnapshot.addSnapshots(
nodeInNewTree.getStats(),
currentInOriginalTree.getStats()
);
nodeInNewTree.setStats(newStats);
}
// Finally set the parent node to be this node for the next callers of this function
parentInNewTree = nodeInNewTree;
}

if (!currentInOriginalTree.children.isEmpty()) {
// Not a leaf node
for (Map.Entry<String, MDCSDimensionNode> childEntry : currentInOriginalTree.children.entrySet()) {
MDCSDimensionNode child = childEntry.getValue();
aggregateByLevelsHelper(parentInNewTree, child, levels, depth + 1);
}
}
}

/**
* Filters out levels that aren't in dimensionNames. Unrecognized levels are ignored.
*/
private List<String> filterLevels(List<String> levels) {
List<String> filtered = new ArrayList<>();
for (String level : levels) {
if (dimensionNames.contains(level)) {
filtered.add(level);
}
}
if (filtered.isEmpty()) {
throw new IllegalArgumentException("Levels cannot have size 0");
}
return filtered;
}

public CacheStatsCounterSnapshot getStatsForDimensionValues(List<String> dimensionValues) {
MDCSDimensionNode current = statsRoot;
for (String dimensionValue : dimensionValues) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,83 +95,6 @@ public void testEmptyDimsList() throws Exception {
assertEquals(stats.getTotalStats(), statsRoot.getStats());
}

public void testAggregateByAllDimensions() throws Exception {
// Aggregating with all dimensions as levels should just give us the same values that were in the original map
List<String> dimensionNames = List.of("dim1", "dim2", "dim3", "dim4");
StatsHolder statsHolder = new StatsHolder(dimensionNames);
Map<String, List<String>> usedDimensionValues = getUsedDimensionValues(statsHolder, 10);
Map<List<String>, CacheStatsCounter> expected = populateStats(statsHolder, usedDimensionValues, 1000, 10);
MultiDimensionCacheStats stats = (MultiDimensionCacheStats) statsHolder.getCacheStats();

MultiDimensionCacheStats.MDCSDimensionNode aggregated = stats.aggregateByLevels(dimensionNames);
for (Map.Entry<List<String>, CacheStatsCounter> expectedEntry : expected.entrySet()) {
List<String> dimensionValues = new ArrayList<>();
for (String dimValue : expectedEntry.getKey()) {
dimensionValues.add(dimValue);
}
assertEquals(expectedEntry.getValue().snapshot(), getNode(dimensionValues, aggregated).getStats());
}
assertSumOfChildrenStats(aggregated);
}

public void testAggregateBySomeDimensions() throws Exception {
List<String> dimensionNames = List.of("dim1", "dim2", "dim3", "dim4");
StatsHolder statsHolder = new StatsHolder(dimensionNames);
Map<String, List<String>> usedDimensionValues = getUsedDimensionValues(statsHolder, 10);
Map<List<String>, CacheStatsCounter> expected = populateStats(statsHolder, usedDimensionValues, 1000, 10);
MultiDimensionCacheStats stats = (MultiDimensionCacheStats) statsHolder.getCacheStats();

for (int i = 0; i < (1 << dimensionNames.size()); i++) {
// Test each combination of possible levels
List<String> levels = new ArrayList<>();
for (int nameIndex = 0; nameIndex < dimensionNames.size(); nameIndex++) {
if ((i & (1 << nameIndex)) != 0) {
levels.add(dimensionNames.get(nameIndex));
}
}
if (levels.size() == 0) {
assertThrows(IllegalArgumentException.class, () -> stats.aggregateByLevels(levels));
} else {
MultiDimensionCacheStats.MDCSDimensionNode aggregated = stats.aggregateByLevels(levels);
Map<List<String>, MultiDimensionCacheStats.MDCSDimensionNode> aggregatedLeafNodes = getAllLeafNodes(aggregated);

for (Map.Entry<List<String>, MultiDimensionCacheStats.MDCSDimensionNode> aggEntry : aggregatedLeafNodes.entrySet()) {
CacheStatsCounter expectedCounter = new CacheStatsCounter();
for (List<String> expectedDims : expected.keySet()) {
if (expectedDims.containsAll(aggEntry.getKey())) {
expectedCounter.add(expected.get(expectedDims));
}
}
assertEquals(expectedCounter.snapshot(), aggEntry.getValue().getStats());
}
assertSumOfChildrenStats(aggregated);
}
}
}

// Get a map from the list of dimension values to the corresponding leaf node.
private Map<List<String>, MultiDimensionCacheStats.MDCSDimensionNode> getAllLeafNodes(MultiDimensionCacheStats.MDCSDimensionNode root) {
Map<List<String>, MultiDimensionCacheStats.MDCSDimensionNode> result = new HashMap<>();
getAllLeafNodesHelper(result, root, new ArrayList<>());
return result;
}

private void getAllLeafNodesHelper(
Map<List<String>, MultiDimensionCacheStats.MDCSDimensionNode> result,
MultiDimensionCacheStats.MDCSDimensionNode current,
List<String> pathToCurrent
) {
if (current.children.isEmpty()) {
result.put(pathToCurrent, current);
} else {
for (Map.Entry<String, MultiDimensionCacheStats.MDCSDimensionNode> entry : current.children.entrySet()) {
List<String> newPath = new ArrayList<>(pathToCurrent);
newPath.add(entry.getKey());
getAllLeafNodesHelper(result, entry.getValue(), newPath);
}
}
}

private void assertSumOfChildrenStats(MultiDimensionCacheStats.MDCSDimensionNode current) {
if (!current.children.isEmpty()) {
CacheStatsCounter expectedTotal = new CacheStatsCounter();
Expand Down

0 comments on commit fddd56e

Please sign in to comment.