Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions common/src/main/java/io/druid/concurrent/LifecycleLock.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ void exitStop()
}
}

void reset()
void exitStopAndReset()
{
if (!compareAndSetState(STOPPED, NOT_STARTED)) {
if (!compareAndSetState(STOPPING, NOT_STARTED)) {
throw new IllegalMonitorStateException("Not called exitStop() before reset()");
}
}
Expand All @@ -187,7 +187,7 @@ void reset()

/**
* Start latch, only one canStart() call in any thread on this LifecycleLock object could return true, if {@link
* #reset()} is not called in between.
* #exitStopAndReset()} is not called in between.
*/
public boolean canStart()
{
Expand Down Expand Up @@ -257,8 +257,8 @@ public boolean canStop()
}

/**
* If this LifecycleLock is used in a restartable object, which uses {@link #reset()}, exitStop() must be called
* before exit from stop() on this object, usually in a finally block.
* Finalizes stopping the the LifecycleLock. This method must be called before exit from stop() on this object,
* usually in a finally block. If you're using a restartable object, use {@link #exitStopAndReset()} instead.
*
* @throws IllegalMonitorStateException if {@link #canStop()} is not yet called on this LifecycleLock
*/
Expand All @@ -268,12 +268,14 @@ public void exitStop()
}

/**
* Resets the LifecycleLock after {@link #exitStop()}, so that {@link #canStart()} could be called again.
* Finalizes stopping the LifecycleLock and resets it, so that {@link #canStart()} could be called again. If this
* LifecycleLock is used in a restartable object, this method must be called before exit from stop() on this object,
* usually in a finally block.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment after this about IllegalMonitorStateException should refer to canStop() instead of exitStop()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Updated javadoc.

*
* @throws IllegalMonitorStateException if {@link #exitStop()} is not yet called on this LifecycleLock
* @throws IllegalMonitorStateException if {@link #canStop()} is not yet called on this LifecycleLock
*/
public void reset()
public void exitStopAndReset()
{
sync.reset();
sync.exitStopAndReset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ public void testRestart()
lifecycleLock.started();
lifecycleLock.exitStart();
Assert.assertTrue(lifecycleLock.canStop());
lifecycleLock.exitStop();
lifecycleLock.reset();
lifecycleLock.exitStopAndReset();
Assert.assertTrue(lifecycleLock.canStart());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import com.google.inject.Inject;
import io.druid.client.DruidDataSource;
import io.druid.client.ImmutableDruidDataSource;
import io.druid.concurrent.LifecycleLock;
import io.druid.guice.ManageLifecycle;
import io.druid.java.util.common.DateTimes;
import io.druid.java.util.common.Intervals;
Expand Down Expand Up @@ -86,7 +85,10 @@ public class SQLMetadataSegmentManager implements MetadataSegmentManager
private static final Interner<DataSegment> DATA_SEGMENT_INTERNER = Interners.newWeakInterner();
private static final EmittingLogger log = new EmittingLogger(SQLMetadataSegmentManager.class);

private final LifecycleLock lifecycleLock = new LifecycleLock();
// Use to synchronize start() and stop(). These methods should be synchronized to prevent from being called at the
// same time if two different threads are calling them. This might be possible if a druid coordinator gets and drops
// leadership repeatedly in quick succession.
private final Object lock = new Object();

private final ObjectMapper jsonMapper;
private final Supplier<MetadataSegmentManagerConfig> config;
Expand All @@ -96,6 +98,7 @@ public class SQLMetadataSegmentManager implements MetadataSegmentManager

private volatile ListeningScheduledExecutorService exec = null;
private volatile ListenableFuture<?> future = null;
private volatile boolean started;

@Inject
public SQLMetadataSegmentManager(
Expand All @@ -116,11 +119,11 @@ public SQLMetadataSegmentManager(
@LifecycleStart
public void start()
{
if (!lifecycleLock.canStart()) {
return;
}
synchronized (lock) {
if (started) {
return;
}

try {
exec = MoreExecutors.listeningDecorator(Execs.scheduledSingleThreaded("DatabaseSegmentManager-Exec--%d"));

final Duration delay = config.get().getPollDuration().toStandardDuration();
Expand All @@ -143,21 +146,19 @@ public void run()
delay.getMillis(),
TimeUnit.MILLISECONDS
);
lifecycleLock.started();
}
finally {
lifecycleLock.exitStart();
started = true;
}
}

@Override
@LifecycleStop
public void stop()
{
if (!lifecycleLock.canStop()) {
return;
}
try {
synchronized (lock) {
if (!started) {
return;
}

final ConcurrentHashMap<String, DruidDataSource> emptyMap = new ConcurrentHashMap<>();
ConcurrentHashMap<String, DruidDataSource> current;
do {
Expand All @@ -168,9 +169,7 @@ public void stop()
future = null;
exec.shutdownNow();
exec = null;
}
finally {
lifecycleLock.exitStop();
started = false;
}
}

Expand Down Expand Up @@ -366,7 +365,7 @@ public boolean removeSegment(String ds, final String segmentID)
@Override
public boolean isStarted()
{
return lifecycleLock.isStarted();
return started;
}

@Override
Expand Down Expand Up @@ -420,7 +419,7 @@ public List<String> fold(
public void poll()
{
try {
if (!lifecycleLock.isStarted()) {
if (!started) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.inject.Inject;
import io.druid.java.util.emitter.EmittingLogger;
import io.druid.java.util.http.client.HttpClient;
import io.druid.java.util.http.client.Request;
import io.druid.java.util.http.client.response.ClientResponse;
import io.druid.java.util.http.client.response.HttpResponseHandler;
import io.druid.java.util.http.client.response.SequenceInputStreamResponseHandler;
import io.druid.audit.AuditInfo;
import io.druid.common.config.JacksonConfigManager;
import io.druid.concurrent.LifecycleLock;
Expand All @@ -54,6 +48,12 @@
import io.druid.java.util.common.StreamUtils;
import io.druid.java.util.common.StringUtils;
import io.druid.java.util.common.concurrent.Execs;
import io.druid.java.util.emitter.EmittingLogger;
import io.druid.java.util.http.client.HttpClient;
import io.druid.java.util.http.client.Request;
import io.druid.java.util.http.client.response.ClientResponse;
import io.druid.java.util.http.client.response.HttpResponseHandler;
import io.druid.java.util.http.client.response.SequenceInputStreamResponseHandler;
import io.druid.query.lookup.LookupsState;
import io.druid.server.http.HostAndPortWithScheme;
import io.druid.server.listener.resource.ListenerResource;
Expand Down Expand Up @@ -124,8 +124,7 @@ public class LookupCoordinatorManager
// Updated by config watching service
private AtomicReference<Map<String, Map<String, LookupExtractorFactoryMapContainer>>> lookupMapConfigRef;

@VisibleForTesting
final LifecycleLock lifecycleLock = new LifecycleLock();
private final LifecycleLock lifecycleLock = new LifecycleLock();

private ListeningScheduledExecutorService executorService;
private ListenableScheduledFuture<?> backgroundManagerFuture;
Expand Down Expand Up @@ -333,6 +332,17 @@ public LookupExtractorFactoryMapContainer getLookup(final String tier, final Str
return tierLookups.get(lookupName);
}

public boolean isStarted()
{
return lifecycleLock.isStarted();
}

@VisibleForTesting
boolean awaitStarted(long waitTimeMs)
{
return lifecycleLock.awaitStarted(waitTimeMs, TimeUnit.MILLISECONDS);
}

// start() and stop() are synchronized so that they never run in parallel in case of ZK acting funny or druid bug and
// coordinator becomes leader and drops leadership in quick succession.
public void start()
Expand Down Expand Up @@ -439,8 +449,7 @@ public void stop()
}
finally {
//so that subsequent start() would happen, even if stop() failed with exception
lifecycleLock.exitStop();
lifecycleLock.reset();
lifecycleLock.exitStopAndReset();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ public void setUp()
);
}

@Test
public void testMultipleStopAndStart()
{
// Simulate successive losing and getting the coordinator leadership
ruleManager.start();
ruleManager.stop();
ruleManager.start();
ruleManager.stop();
}

@Test
public void testRuleInsert()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import java.io.IOException;


public class MetadataSegmentManagerTest
public class SQLMetadataSegmentManagerTest
{
@Rule
public final TestDerbyConnector.DerbyConnectorRule derbyConnectorRule = new TestDerbyConnector.DerbyConnectorRule();
Expand Down Expand Up @@ -230,4 +230,14 @@ public void testRemoveDataSegment() throws IOException
Assert.assertNull(manager.getInventoryValue(newDataSource));
Assert.assertTrue(manager.removeSegment(newDataSource, newSegment.getIdentifier()));
}

@Test
public void testStopAndStart()
{
// Simulate successive losing and getting the coordinator leadership
manager.start();
manager.stop();
manager.start();
manager.stop();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
Expand Down Expand Up @@ -1256,15 +1255,15 @@ public void testStartStop() throws Exception
lookupCoordinatorManagerConfig
);

Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.isStarted());

manager.start();
Assert.assertTrue(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertTrue(manager.awaitStarted(1));
Assert.assertTrue(manager.backgroundManagerIsRunning());
Assert.assertFalse(manager.waitForBackgroundTermination(10));

manager.stop();
Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.awaitStarted(1));
Assert.assertTrue(manager.waitForBackgroundTermination(10));
Assert.assertFalse(manager.backgroundManagerIsRunning());

Expand Down Expand Up @@ -1293,35 +1292,35 @@ public void testMultipleStartStop() throws Exception
lookupCoordinatorManagerConfig
);

Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.awaitStarted(1));

manager.start();
Assert.assertTrue(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertTrue(manager.awaitStarted(1));
Assert.assertTrue(manager.backgroundManagerIsRunning());
Assert.assertFalse(manager.waitForBackgroundTermination(10));

manager.stop();
Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.awaitStarted(1));
Assert.assertTrue(manager.waitForBackgroundTermination(10));
Assert.assertFalse(manager.backgroundManagerIsRunning());

manager.start();
Assert.assertTrue(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertTrue(manager.awaitStarted(1));
Assert.assertTrue(manager.backgroundManagerIsRunning());
Assert.assertFalse(manager.waitForBackgroundTermination(10));

manager.stop();
Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.awaitStarted(1));
Assert.assertTrue(manager.waitForBackgroundTermination(10));
Assert.assertFalse(manager.backgroundManagerIsRunning());

manager.start();
Assert.assertTrue(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertTrue(manager.awaitStarted(1));
Assert.assertTrue(manager.backgroundManagerIsRunning());
Assert.assertFalse(manager.waitForBackgroundTermination(10));

manager.stop();
Assert.assertFalse(manager.lifecycleLock.awaitStarted(1, TimeUnit.MILLISECONDS));
Assert.assertFalse(manager.awaitStarted(1));
Assert.assertTrue(manager.waitForBackgroundTermination(10));
Assert.assertFalse(manager.backgroundManagerIsRunning());

Expand Down