package com.neverwinterdp.tool.message; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import com.neverwinterdp.util.text.TabularFormater; public class PartitionMessageTracker { private int partition; private int logCount; private List<SequenceMap> map = new ArrayList<>(); private int idealSequenceMapSize = 100; public PartitionMessageTracker(int partition) { this.partition = partition; } public int getLogCount() { return this.logCount; } public int getDuplicatedCount() { //sum of duplicates in all maps int dups = 0; for (SequenceMap sequenceMap : map) { dups += sequenceMap.getDuplicatedCount(); } return dups; } public int getMinMessageId() { // min in topmost map return map.get(0).from; } public int getMaxMessageId() { //max in lowest map return map.get(map.size() - 1).current; } public boolean isInSequence() { boolean inSequence = true; SequenceMap prevSeqMap = null; for (SequenceMap seqMap : map) { if (prevSeqMap != null) { inSequence &= prevSeqMap.getCurrent() + 1 == seqMap.getFrom(); } //return on first false. it will always be false after that if (!inSequence) return false; prevSeqMap = seqMap; } return inSequence; } public SequenceMap getSequenceMap(int idx) { return map.get(idx); } //TODO: remove this code public List<SequenceMap> getSequenceMap() { return map; } public int getPartition() { return this.partition; } public void setIdealSequenceMapSize(int size) { this.idealSequenceMapSize = size; } synchronized public void log(int trackId) { try { for (int i = 0; i < map.size(); i++) { SequenceMap seqMap = map.get(i); if (seqMap.isInRange(trackId)) { if (i + 1 < map.size()) { SequenceMap nextSeqMap = map.get(i + 1); if (nextSeqMap.isInRange(trackId)) { nextSeqMap.log(trackId); return; } } seqMap.log(trackId); return; } if (seqMap.isSmallerRange(trackId)) { seqMap = new SequenceMap(trackId); map.add(i, seqMap); return; } } SequenceMap seqMap = new SequenceMap(trackId); map.add(seqMap); } finally { logCount++; if (map.size() > idealSequenceMapSize) { optimize(); } } } synchronized public void optimize() { SequenceMap prevSeqMap = null; List<SequenceMap> newMap = new ArrayList<>(); for (int i = 0; i < map.size(); i++) { SequenceMap seqMap = map.get(i); if (prevSeqMap != null) { if (prevSeqMap.canAppend(seqMap)) { prevSeqMap.append(seqMap); } else { newMap.add(seqMap); prevSeqMap = seqMap; } } else { newMap.add(seqMap); prevSeqMap = seqMap; } } map = newMap; } public void dump(Appendable out, String title) throws IOException { String[] header = { "From", "To", "In Sequence", "Duplication" }; TabularFormater formater = new TabularFormater(header); formater.setTitle(title); SequenceMap prevSeqMap = null; for (int i = 0; i < map.size(); i++) { SequenceMap seqMap = map.get(i); boolean inSequence = true; if (prevSeqMap != null) { inSequence = prevSeqMap.getCurrent() + 1 == seqMap.getFrom(); } Object[] cells = { seqMap.getFrom(), seqMap.getCurrent(), inSequence, seqMap.getDuplicatedDescription() }; formater.addRow(cells); prevSeqMap = seqMap; } out.append(formater.getFormatText()); } static public class SequenceMap { private int from; private int current; private int duplicatedCount; private Set<Integer> duplicated = new HashSet<Integer>(); public SequenceMap(int num) { from = num; current = num; } public int getFrom() { return this.from; } public int getCurrent() { return this.current; } public int getDuplicatedCount() { return duplicatedCount; } public boolean canAppend(SequenceMap other) { return current + 1 == other.getFrom(); } public void append(SequenceMap other) { if (!canAppend(other)) { throw new RuntimeException("Cannot append"); } current = other.current; duplicatedCount += other.duplicatedCount; duplicated.addAll(other.duplicated); } public List<Integer> getDuplicatedNumbers() { List<Integer> holder = new ArrayList<>(); holder.addAll(duplicated); Collections.sort(holder); return holder; } public String getDuplicatedDescription() { StringBuilder b = new StringBuilder(); b.append(duplicatedCount); if (duplicatedCount > 0) { b.append("["); List<Integer> numbers = getDuplicatedNumbers(); if (numbers.size() < 10) { for (int i = 0; i < numbers.size(); i++) { if (i > 0) b.append(","); b.append(numbers.get(i)); } } else { for (int i = 0; i < 5; i++) { if (i > 0) b.append(","); b.append(numbers.get(i)); } b.append("..."); for (int i = numbers.size() - 5; i < numbers.size(); i++) { if (i > numbers.size() - 5) b.append(","); b.append(numbers.get(i)); } } b.append("]"); } return b.toString(); } public boolean isInRange(int num) { return num >= from && num <= current + 1; } public boolean isSmallerRange(int num) { return num < from; } public void log(int num) { if (num < from || num > current + 1) { throw new RuntimeException("The log number " + num + " is not in the range " + from + " - " + (current + 1)); } if (num == current + 1) { current++; } else { duplicatedCount++; duplicated.add(num); } } } }