if (leftEntry != null) { leftNode = leftEntry.getValue(); if (leftNode.contains(procId)) { return leftNode; leftCanGrow = leftNode.canGrow(procId); if (rightEntry != null) { rightNode = rightEntry.getValue(); rightCanGrow = rightNode.canGrow(procId); if (leftNode != null) { if (leftNode.canMerge(rightNode)) { if ((procId - leftNode.getEnd()) <= (rightNode.getStart() - procId)) { return growNode(leftNode, procId); BitSetNode node = new BitSetNode(procId, partial); map.put(node.getStart(), node); return node;
private void setDeleteIf(ProcedureStoreTracker tracker, BiFunction<BitSetNode, Long, Boolean> func) { BitSetNode trackerNode = null; for (BitSetNode node : map.values()) { long minProcId = node.getStart(); long maxProcId = node.getEnd(); for (long procId = minProcId; procId <= maxProcId; ++procId) { if (!node.isModified(procId)) { continue; } trackerNode = tracker.lookupClosestNode(trackerNode, procId); if (func.apply(trackerNode, procId)) { node.delete(procId); } } } }
public void dump() { System.out.printf("%06d:%06d min=%d max=%d%n", getStart(), getEnd(), getActiveMinProcId(), getActiveMaxProcId()); System.out.println("Modified:"); for (int i = 0; i < modified.length; ++i) { for (int j = 0; j < BITS_PER_WORD; ++j) { System.out.print((modified[i] & (1L << j)) != 0 ? "1" : "0"); } System.out.println(" " + i); } System.out.println(); System.out.println("Delete:"); for (int i = 0; i < deleted.length; ++i) { for (int j = 0; j < BITS_PER_WORD; ++j) { System.out.print((deleted[i] & (1L << j)) != 0 ? "1" : "0"); } System.out.println(" " + i); } System.out.println(); }
@Test public void testGrow() { BitSetNode node = new BitSetNode(1000, false); // contains, do not need to grow but should not fail assertTrue(node.canGrow(1024)); assertTrue(node.canGrow(900)); assertTrue(node.canGrow(1100)); assertFalse(node.canGrow(100)); assertFalse(node.canGrow(10000)); // grow to right node.grow(1100); assertTrue(node.contains(1100)); assertTrue(node.isModified(1000)); // grow to left node.grow(900); assertTrue(node.contains(900)); assertTrue(node.isModified(1000)); for (long i = node.getStart(); i <= node.getEnd(); i++) { if (i != 1000) { assertEquals(DeleteState.YES, node.isDeleted(i)); } else { assertEquals(DeleteState.NO, node.isDeleted(i)); } } }
@Test public void testMerge() { BitSetNode node = new BitSetNode(1000, false); assertTrue(node.canMerge(new BitSetNode(1200, false))); assertFalse(node.canMerge(new BitSetNode(10000, false))); BitSetNode rightNode = new BitSetNode(1200, false); node.merge(rightNode); assertTrue(node.isModified(1000)); assertTrue(node.isModified(1200)); for (long i = node.getStart(); i <= node.getEnd(); i++) { if (i != 1000 && i != 1200) { assertEquals(DeleteState.YES, node.isDeleted(i)); } else { assertEquals(DeleteState.NO, node.isDeleted(i)); } } } }
@Test public void testGetActiveMaxMinProcId() { BitSetNode node = new BitSetNode(5L, false); assertEquals(5L, node.getActiveMinProcId()); assertEquals(5L, node.getActiveMaxProcId()); node.insertOrUpdate(10L); assertEquals(5L, node.getActiveMinProcId()); assertEquals(10L, node.getActiveMaxProcId()); node.insertOrUpdate(1L); assertEquals(1L, node.getActiveMinProcId()); assertEquals(10L, node.getActiveMaxProcId()); node.delete(10L); assertEquals(1L, node.getActiveMinProcId()); assertEquals(5L, node.getActiveMaxProcId()); node.delete(1L); assertEquals(5L, node.getActiveMinProcId()); assertEquals(5L, node.getActiveMaxProcId()); node.delete(5L); assertEquals(Procedure.NO_PROC_ID, node.getActiveMinProcId()); assertEquals(Procedure.NO_PROC_ID, node.getActiveMaxProcId()); }
private BitSetNode delete(BitSetNode node, long procId) { node = lookupClosestNode(node, procId); if (node == null || !node.contains(procId)) { LOG.warn("The BitSetNode for procId={} does not exist, maybe a double deletion?", procId); return node; } node.delete(procId); if (!keepDeletes && node.isEmpty()) { // TODO: RESET if (map.size() == 1) map.remove(node.getStart()); } trackProcIds(procId); return node; }
/** * If {@link #partial} is false, returns state from the bitmap. If no state is found for * {@code procId}, returns YES. * If partial is true, tracker doesn't have complete view of system state, so it returns MAYBE * if there is no update for the procedure or if it doesn't have a state in bitmap. Otherwise, * returns state from the bitmap. */ public DeleteState isDeleted(long procId) { Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId); if (entry != null && entry.getValue().contains(procId)) { BitSetNode node = entry.getValue(); DeleteState state = node.isDeleted(procId); return partial && !node.isModified(procId) ? DeleteState.MAYBE : state; } return partial ? DeleteState.MAYBE : DeleteState.YES; }
public void grow(long procId) { assert canGrow(procId); if (procId < start) { long newStart = alignDown(procId); int delta = (int) (start - newStart) >> ADDRESS_BITS_PER_WORD; start = newStart; } else { long newEnd = alignUp(procId + 1); int delta = (int) (newEnd - getEnd()) >> ADDRESS_BITS_PER_WORD; int newSize = modified.length + delta; long[] newModified = Arrays.copyOf(modified, newSize);
private BitSetNode update(BitSetNode node, long procId) { node = lookupClosestNode(node, procId); assert node != null : "expected node to update procId=" + procId; assert node.contains(procId) : "expected procId=" + procId + " in the node"; node.insertOrUpdate(procId); trackProcIds(procId); return node; }
public boolean canMerge(BitSetNode rightNode) { // Can just compare 'starts' since boundaries are aligned to multiples of BITS_PER_WORD. assert start < rightNode.start; return (rightNode.getEnd() - start) < MAX_NODE_SIZE; }
public void resetToProto(ProcedureProtos.ProcedureStoreTracker trackerProtoBuf) { reset(); for (ProcedureProtos.ProcedureStoreTracker.TrackerNode protoNode: trackerProtoBuf.getNodeList()) { final BitSetNode node = new BitSetNode(protoNode); map.put(node.getStart(), node); } }
/** * For the global tracker, we will use this method to build the holdingCleanupTracker, as the * modified flags will be cleared after rolling so we only need to test the deleted flags. * @see #setDeletedIfModifiedInBoth(ProcedureStoreTracker) */ public void setDeletedIfDeletedByThem(ProcedureStoreTracker tracker) { setDeleteIf(tracker, (node, procId) -> node == null || !node.contains(procId) || node.isDeleted(procId) == DeleteState.YES); }
public boolean isModified(long procId) { final Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId); return entry != null && entry.getValue().contains(procId) && entry.getValue().isModified(procId); }
/** * Set the given bit for the procId to delete if it was modified before. * <p/> * This method is used to test whether a procedure wal file can be safely deleted, as if all the * procedures in the given procedure wal file has been modified in the new procedure wal files, * then we can delete it. */ public void setDeletedIfModified(long... procId) { BitSetNode node = null; for (int i = 0; i < procId.length; ++i) { node = lookupClosestNode(node, procId[i]); if (node != null && node.isModified(procId[i])) { node.delete(procId[i]); } } }
/** * This method is used when restarting where we need to rebuild the ProcedureStoreTracker. The * {@link #delete(long)} method above assume that the {@link BitSetNode} exists, but when restart * this is not true, as we will read the wal files in reverse order so a delete may come first. */ public void setDeleted(long procId, boolean isDeleted) { BitSetNode node = getOrCreateNode(procId); assert node.contains(procId) : "expected procId=" + procId + " in the node=" + node; node.updateState(procId, isDeleted); trackProcIds(procId); }
/** * lookup the node containing the specified procId. * @param node cached node to check before doing a lookup * @param procId the procId to lookup * @return the node that may contains the procId or null */ private BitSetNode lookupClosestNode(final BitSetNode node, final long procId) { if (node != null && node.contains(procId)) return node; final Map.Entry<Long, BitSetNode> entry = map.floorEntry(procId); return entry != null ? entry.getValue() : null; }