public FireHydrant(Segment adapter, int count) { this.index = null; this.adapter = new AtomicReference<>(new ReferenceCountingSegment(adapter)); this.count = count; }
@Override public Sequence<T> run(final QueryPlus<T> queryPlus, Map<String, Object> responseContext) { if (adapter.increment()) { try { final Sequence<T> baseSequence = factory.createRunner(adapter).run(queryPlus, responseContext); return Sequences.withBaggage(baseSequence, adapter.decrementOnceCloseable()); } catch (Throwable t) { try { adapter.decrement(); } catch (Exception e) { t.addSuppressed(e); } throw t; } } else { // Segment was closed before we had a chance to increment the reference count return new ReportTimelineMissingSegmentQueryRunner<T>(descriptor).run(queryPlus, responseContext); } } }
Assert.assertFalse(segment.isClosed()); Assert.assertTrue(segment.increment()); Assert.assertEquals(1, segment.getNumReferences()); Closeable closeable = segment.decrementOnceCloseable(); closeable.close(); closeable.close(); Assert.assertEquals(0, segment.getNumReferences()); Assert.assertFalse(segment.isClosed()); segment.close(); segment.close(); exec.submit( () -> { try { segment.close(); Assert.assertEquals(0, segment.getNumReferences()); Assert.assertTrue(segment.isClosed()); segment.increment(); segment.increment(); segment.increment(); Assert.assertEquals(0, segment.getNumReferences()); segment.close(); Assert.assertEquals(0, segment.getNumReferences());
public void swapSegment(@Nullable Segment newSegment) { while (true) { ReferenceCountingSegment currentSegment = adapter.get(); if (currentSegment == null && newSegment == null) { return; } if (currentSegment != null && newSegment != null && !newSegment.getId().equals(currentSegment.getId())) { // Sanity check: identifier should not change throw new ISE( "WTF?! Cannot swap identifier[%s] -> [%s]!", currentSegment.getId(), newSegment.getId() ); } if (currentSegment == newSegment) { throw new ISE("Cannot swap to the same segment"); } ReferenceCountingSegment newReferenceCountingSegment = newSegment != null ? new ReferenceCountingSegment(newSegment) : null; if (adapter.compareAndSet(currentSegment, newReferenceCountingSegment)) { if (currentSegment != null) { currentSegment.close(); } index = null; return; } } }
ReferenceCountingSegment segment = hydrant.getIncrementedSegment(); try { numRowsExcludingCurrIndex.addAndGet(segment.asQueryableIndex().getNumRows()); segment.decrement();
public Segment getBaseSegment() { return !isClosed() ? baseSegment : null; }
public void swapSegment(@Nullable Segment newSegment) { while (true) { ReferenceCountingSegment currentSegment = adapter.get(); if (currentSegment == null && newSegment == null) { return; } if (currentSegment != null && newSegment != null && !newSegment.getIdentifier().equals(currentSegment.getIdentifier())) { // Sanity check: identifier should not change throw new ISE( "WTF?! Cannot swap identifier[%s] -> [%s]!", currentSegment.getIdentifier(), newSegment.getIdentifier() ); } if (currentSegment == newSegment) { throw new ISE("Cannot swap to the same segment"); } ReferenceCountingSegment newReferenceCountingSegment = newSegment != null ? new ReferenceCountingSegment(newSegment) : null; if (adapter.compareAndSet(currentSegment, newReferenceCountingSegment)) { if (currentSegment != null) { currentSegment.close(); } index = null; return; } } }
@Override public QueryRunner<Result<SearchResultValue>> createRunner(Segment adapter) { if (!(adapter instanceof ReferenceCountingSegment)) { throw new IAE("Expected instance of ReferenceCountingSegment, got %s", adapter.getClass()); } final ReferenceCountingSegment segment = (ReferenceCountingSegment) adapter; Assert.assertTrue(segment.getNumReferences() > 0); segmentReferences.add(segment); adapters.add((SegmentForTesting) segment.getBaseSegment()); return new BlockingQueryRunner<>(new NoopQueryRunner<>(), waitLatch, waitYieldLatch, notifyLatch); }
public Pair<Segment, Closeable> getAndIncrementSegment() { ReferenceCountingSegment segment = getIncrementedSegment(); return new Pair<>(segment, segment.decrementOnceCloseable()); }
/** * Returns a {@link Closeable} which action is to call {@link #decrement()} only once. If close() is called on the * returned Closeable object for the second time, it won't call {@link #decrement()} again. */ public Closeable decrementOnceCloseable() { AtomicBoolean decremented = new AtomicBoolean(false); return () -> { if (decremented.compareAndSet(false, true)) { decrement(); } else { log.warn("close() is called more than once on ReferenceCountingSegment.decrementOnceCloseable()"); } }; }
public ReferenceCountingSegment getIncrementedSegment() { ReferenceCountingSegment segment = adapter.get(); while (true) { if (segment.increment()) { return segment; } // segment.increment() returned false, means it is closed. Since close() in swapSegment() happens after segment // swap, the new segment should already be visible. ReferenceCountingSegment newSegment = adapter.get(); if (segment == newSegment) { throw new ISE("segment.close() is called somewhere outside FireHydrant.swapSegment()"); } if (newSegment == null) { throw new ISE("FireHydrant was 'closed' by swapping segment to null while acquiring a segment"); } segment = newSegment; // Spin loop. } }
@Test public void testReferenceCounting() throws Exception { loadQueryable("test", "3", Intervals.of("2011-04-04/2011-04-05")); Future future = assertQueryable( Granularities.DAY, "test", Intervals.of("2011-04-04/2011-04-06"), ImmutableList.of( new Pair<String, Interval>("3", Intervals.of("2011-04-04/2011-04-05")) ) ); queryNotifyLatch.await(1000, TimeUnit.MILLISECONDS); Assert.assertEquals(1, factory.getSegmentReferences().size()); for (ReferenceCountingSegment referenceCountingSegment : factory.getSegmentReferences()) { Assert.assertEquals(1, referenceCountingSegment.getNumReferences()); } queryWaitYieldLatch.countDown(); Assert.assertTrue(factory.getAdapters().size() == 1); for (SegmentForTesting segmentForTesting : factory.getAdapters()) { Assert.assertFalse(segmentForTesting.isClosed()); } queryWaitLatch.countDown(); future.get(); dropQueryable("test", "3", Intervals.of("2011-04-04/2011-04-05")); for (SegmentForTesting segmentForTesting : factory.getAdapters()) { Assert.assertTrue(segmentForTesting.isClosed()); } }
@Override public <T> T as(Class<T> clazz) { return getBaseSegment().as(clazz); } }
oldQueryable.close(); } else { log.info(
ReferenceCountingSegment segment = hydrant.getIncrementedSegment(); try { qindex = segment.asQueryableIndex(); Assert.assertEquals(i, hydrant.getCount()); Assert.assertEquals(expectedDims.get(i), ImmutableList.copyOf(qindex.getAvailableDimensions())); segment.decrement();
@Override public Interval getDataInterval() { return !isClosed() ? baseSegment.getDataInterval() : null; }
public Pair<Segment, Closeable> getAndIncrementSegment() { ReferenceCountingSegment segment = getIncrementedSegment(); return new Pair<>(segment, segment.decrementOnceCloseable()); }
/** * Returns a {@link Closeable} which action is to call {@link #decrement()} only once. If close() is called on the * returned Closeable object for the second time, it won't call {@link #decrement()} again. */ public Closeable decrementOnceCloseable() { AtomicBoolean decremented = new AtomicBoolean(false); return () -> { if (decremented.compareAndSet(false, true)) { decrement(); } else { log.warn("close() is called more than once on ReferenceCountingSegment.decrementOnceCloseable()"); } }; }
public ReferenceCountingSegment getIncrementedSegment() { ReferenceCountingSegment segment = adapter.get(); while (true) { if (segment.increment()) { return segment; } // segment.increment() returned false, means it is closed. Since close() in swapSegment() happens after segment // swap, the new segment should already be visible. ReferenceCountingSegment newSegment = adapter.get(); if (segment == newSegment) { throw new ISE("segment.close() is called somewhere outside FireHydrant.swapSegment()"); } if (newSegment == null) { throw new ISE("FireHydrant was 'closed' by swapping segment to null while acquiring a segment"); } segment = newSegment; // Spin loop. } }
Assert.assertEquals(1, referenceCountingSegment.getNumReferences());