mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-04 03:13:49 +00:00
fixing bug 723 by queuing Operations until initial load has completed
This commit is contained in:
1 parent
348c66e5cf
commit
f4928da853
5 files changed
+113
-16
No files matched your search
+16
-8
@@ -52,7 +52,7 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
*/
|
*/
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
Pair<ReplicationService, ReplicationMasterDescriptor> result = basicSetUp(true, /* master=null means create a new one */ null,
|
Pair<ReplicationServiceTestImpl, ReplicationMasterDescriptor> result = basicSetUp(true, /* master=null means create a new one */ null,
|
||||||
/* replica=null means create a new one */ null);
|
/* replica=null means create a new one */ null);
|
||||||
result.getA().startToReplicateFrom(result.getB());
|
result.getA().startToReplicateFrom(result.getB());
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
* if not <code>null</code>, the value will be used for {@link #replica}; otherwise, a new racing event
|
* if not <code>null</code>, the value will be used for {@link #replica}; otherwise, a new racing event
|
||||||
* service will be created as replica
|
* service will be created as replica
|
||||||
*/
|
*/
|
||||||
protected Pair<ReplicationService, ReplicationMasterDescriptor> basicSetUp(
|
protected Pair<ReplicationServiceTestImpl, ReplicationMasterDescriptor> basicSetUp(
|
||||||
boolean dropDB, RacingEventServiceImpl master, RacingEventServiceImpl replica) throws FileNotFoundException, Exception,
|
boolean dropDB, RacingEventServiceImpl master, RacingEventServiceImpl replica) throws FileNotFoundException, Exception,
|
||||||
JMSException, UnknownHostException {
|
JMSException, UnknownHostException {
|
||||||
final MongoDBService mongoDBService = MongoDBService.INSTANCE;
|
final MongoDBService mongoDBService = MongoDBService.INSTANCE;
|
||||||
@@ -132,9 +132,9 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ReplicationService replicaReplicator = new ReplicationServiceTestImpl(resolveAgainst, rim, brokerMgr,
|
ReplicationServiceTestImpl replicaReplicator = new ReplicationServiceTestImpl(resolveAgainst, rim, brokerMgr,
|
||||||
replicaDescriptor, this.replica, this.master, masterReplicator);
|
replicaDescriptor, this.replica, this.master, masterReplicator);
|
||||||
Pair<ReplicationService, ReplicationMasterDescriptor> result = new Pair<>(replicaReplicator, masterDescriptor);
|
Pair<ReplicationServiceTestImpl, ReplicationMasterDescriptor> result = new Pair<>(replicaReplicator, masterDescriptor);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
Activator.removeTemporaryTestBrokerPersistenceDirectory(brokerPersistenceDir);
|
Activator.removeTemporaryTestBrokerPersistenceDirectory(brokerPersistenceDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReplicationServiceTestImpl extends ReplicationServiceImpl {
|
static class ReplicationServiceTestImpl extends ReplicationServiceImpl {
|
||||||
private final DomainFactory resolveAgainst;
|
private final DomainFactory resolveAgainst;
|
||||||
private final RacingEventService master;
|
private final RacingEventService master;
|
||||||
private final ReplicaDescriptor replicaDescriptor;
|
private final ReplicaDescriptor replicaDescriptor;
|
||||||
@@ -169,11 +169,19 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
@Override
|
@Override
|
||||||
public void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException,
|
public void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException,
|
||||||
ClassNotFoundException, JMSException {
|
ClassNotFoundException, JMSException {
|
||||||
|
Replicator replicator = startToReplicateFromButDontYetFetchInitialLoad(master, /* startReplicatorSuspended */ true);
|
||||||
|
initialLoad();
|
||||||
|
replicator.setSuspended(false); // resume after initial load
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Replicator startToReplicateFromButDontYetFetchInitialLoad(ReplicationMasterDescriptor master, boolean startReplicatorSuspended)
|
||||||
|
throws JMSException, UnknownHostException {
|
||||||
masterReplicationService.registerReplica(replicaDescriptor);
|
masterReplicationService.registerReplica(replicaDescriptor);
|
||||||
registerReplicaUuidForMaster(replicaDescriptor.getUuid().toString(), master);
|
registerReplicaUuidForMaster(replicaDescriptor.getUuid().toString(), master);
|
||||||
TopicSubscriber replicationSubscription = master.getTopicSubscriber(replicaDescriptor.getUuid().toString());
|
TopicSubscriber replicationSubscription = master.getTopicSubscriber(replicaDescriptor.getUuid().toString());
|
||||||
replicationSubscription.setMessageListener(new Replicator(master, this));
|
final Replicator replicator = new Replicator(master, this, startReplicatorSuspended);
|
||||||
initialLoad();
|
replicationSubscription.setMessageListener(replicator);
|
||||||
|
return replicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,7 +189,7 @@ public abstract class AbstractServerReplicationTest {
|
|||||||
* {@link RacingEventServiceImpl#serializeForInitialReplication(ObjectOutputStream)} and
|
* {@link RacingEventServiceImpl#serializeForInitialReplication(ObjectOutputStream)} and
|
||||||
* {@link RacingEventServiceImpl#initiallyFillFrom(ObjectInputStream)} through a piped input/output stream.
|
* {@link RacingEventServiceImpl#initiallyFillFrom(ObjectInputStream)} through a piped input/output stream.
|
||||||
*/
|
*/
|
||||||
private void initialLoad() throws IOException, ClassNotFoundException {
|
protected void initialLoad() throws IOException, ClassNotFoundException {
|
||||||
PipedOutputStream pos = new PipedOutputStream();
|
PipedOutputStream pos = new PipedOutputStream();
|
||||||
PipedInputStream pis = new PipedInputStream(pos);
|
PipedInputStream pis = new PipedInputStream(pos);
|
||||||
final ObjectOutputStream oos = new ObjectOutputStream(pos);
|
final ObjectOutputStream oos = new ObjectOutputStream(pos);
|
||||||
|
|||||||
+1
-1
@@ -99,7 +99,7 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer
|
|||||||
masterLeaderboardReloaded.getRaceColumnByName(Q2), MillisecondsTimePoint.now()));
|
masterLeaderboardReloaded.getRaceColumnByName(Q2), MillisecondsTimePoint.now()));
|
||||||
|
|
||||||
// replicate the re-loaded environment
|
// replicate the re-loaded environment
|
||||||
Pair<ReplicationService, ReplicationMasterDescriptor> descriptors = basicSetUp(/* dropDB */ false, master, replica);
|
Pair<ReplicationServiceTestImpl, ReplicationMasterDescriptor> descriptors = basicSetUp(/* dropDB */ false, master, replica);
|
||||||
replicaReplicator = descriptors.getA();
|
replicaReplicator = descriptors.getA();
|
||||||
masterDescriptor = descriptors.getB();
|
masterDescriptor = descriptors.getB();
|
||||||
replicaReplicator.startToReplicateFrom(masterDescriptor);
|
replicaReplicator.startToReplicateFrom(masterDescriptor);
|
||||||
|
|||||||
+3
-2
@@ -179,11 +179,12 @@ public class ReplicationServiceImpl implements ReplicationService, OperationExec
|
|||||||
String uuid = registerReplicaWithMaster(master);
|
String uuid = registerReplicaWithMaster(master);
|
||||||
TopicSubscriber replicationSubscription = master.getTopicSubscriber(uuid);
|
TopicSubscriber replicationSubscription = master.getTopicSubscriber(uuid);
|
||||||
URL initialLoadURL = master.getInitialLoadURL();
|
URL initialLoadURL = master.getInitialLoadURL();
|
||||||
// TODO bug 723 hold back Replicator from performing operations received until initial load has completed
|
final Replicator replicator = new Replicator(master, this, /* startSuspended */ true);
|
||||||
replicationSubscription.setMessageListener(new Replicator(master, this));
|
replicationSubscription.setMessageListener(replicator);
|
||||||
InputStream is = initialLoadURL.openStream();
|
InputStream is = initialLoadURL.openStream();
|
||||||
ObjectInputStream ois = new ObjectInputStream(is);
|
ObjectInputStream ois = new ObjectInputStream(is);
|
||||||
getRacingEventService().initiallyFillFrom(ois);
|
getRacingEventService().initiallyFillFrom(ois);
|
||||||
|
replicator.setSuspended(false); // apply queued operations
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+83
-4
@@ -3,6 +3,9 @@ package com.sap.sailing.server.replication.impl;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.jms.BytesMessage;
|
import javax.jms.BytesMessage;
|
||||||
import javax.jms.JMSException;
|
import javax.jms.JMSException;
|
||||||
@@ -17,7 +20,12 @@ import com.sap.sailing.server.replication.ReplicationMasterDescriptor;
|
|||||||
/**
|
/**
|
||||||
* Receives {@link RacingEventServiceOperation}s through JMS and
|
* Receives {@link RacingEventServiceOperation}s through JMS and
|
||||||
* {@link RacingEventService#apply(RacingEventServiceOperation) applies} them to the {@link RacingEventService} passed
|
* {@link RacingEventService#apply(RacingEventServiceOperation) applies} them to the {@link RacingEventService} passed
|
||||||
* to this replicator at construction.
|
* to this replicator at construction. When started in suspended mode, messages received will be turned into
|
||||||
|
* {@link RacingEventServiceOperation}s and then queued until {@link #setSuspended(boolean) setSuspended(false)} is invoked
|
||||||
|
* which applies all queued operations before applying the ones received later.<p>
|
||||||
|
*
|
||||||
|
* The receiver takes care of synchronizing receiving, suspending/resuming and queuing. Waiters are notified
|
||||||
|
* whenever the result of {@link #isQueueEmpty} changes.
|
||||||
*
|
*
|
||||||
* @author Axel Uhl (d043530)
|
* @author Axel Uhl (d043530)
|
||||||
*
|
*
|
||||||
@@ -25,10 +33,34 @@ import com.sap.sailing.server.replication.ReplicationMasterDescriptor;
|
|||||||
public class Replicator implements MessageListener {
|
public class Replicator implements MessageListener {
|
||||||
private final ReplicationMasterDescriptor master;
|
private final ReplicationMasterDescriptor master;
|
||||||
private final HasRacingEventService racingEventServiceTracker;
|
private final HasRacingEventService racingEventServiceTracker;
|
||||||
|
private final List<RacingEventServiceOperation<?>> queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the replicator is suspended, messages received are queued.
|
||||||
|
*/
|
||||||
|
private boolean suspended;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the replicator immediately, not holding back messages received but forwarding them directly.
|
||||||
|
*
|
||||||
|
* @param master
|
||||||
|
* descriptor of the master server from which this replicator receives messages
|
||||||
|
* @param racingEventServiceTracker
|
||||||
|
* OSGi service tracker for the replica to which to apply the messages received
|
||||||
|
*/
|
||||||
public Replicator(ReplicationMasterDescriptor master, HasRacingEventService racingEventServiceTracker) {
|
public Replicator(ReplicationMasterDescriptor master, HasRacingEventService racingEventServiceTracker) {
|
||||||
|
this(master, racingEventServiceTracker, /* startSuspended */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Replicator(ReplicationMasterDescriptor master, HasRacingEventService racingEventServiceTracker, boolean startSuspended) {
|
||||||
|
this.queue = new ArrayList<RacingEventServiceOperation<?>>();
|
||||||
this.master = master;
|
this.master = master;
|
||||||
this.racingEventServiceTracker = racingEventServiceTracker;
|
this.racingEventServiceTracker = racingEventServiceTracker;
|
||||||
|
this.suspended = startSuspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isQueueEmpty() {
|
||||||
|
return queue.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +69,7 @@ public class Replicator implements MessageListener {
|
|||||||
* time.
|
* time.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Message m) {
|
public synchronized void onMessage(Message m) {
|
||||||
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
|
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
|
||||||
try {
|
try {
|
||||||
byte[] bytesFromMessage = getBytes((BytesMessage) m);
|
byte[] bytesFromMessage = getBytes((BytesMessage) m);
|
||||||
@@ -47,14 +79,61 @@ public class Replicator implements MessageListener {
|
|||||||
ObjectInputStream ois = DomainFactory.INSTANCE.createObjectInputStreamResolvingAgainstThisFactory(
|
ObjectInputStream ois = DomainFactory.INSTANCE.createObjectInputStreamResolvingAgainstThisFactory(
|
||||||
new ByteArrayInputStream(bytesFromMessage));
|
new ByteArrayInputStream(bytesFromMessage));
|
||||||
RacingEventServiceOperation<?> operation = (RacingEventServiceOperation<?>) ois.readObject();
|
RacingEventServiceOperation<?> operation = (RacingEventServiceOperation<?>) ois.readObject();
|
||||||
racingEventServiceTracker.getRacingEventService().apply(operation);
|
applyOrQueue(operation);
|
||||||
} catch (IOException | ClassNotFoundException | JMSException e) {
|
} catch (IOException | ClassNotFoundException | JMSException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
Thread.currentThread().setContextClassLoader(oldClassLoader);
|
Thread.currentThread().setContextClassLoader(oldClassLoader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the replicator is currently {@link #suspended}, the <code>operation</code> is queued, otherwise immediately applied to
|
||||||
|
* the receiving replica.
|
||||||
|
*/
|
||||||
|
private synchronized void applyOrQueue(RacingEventServiceOperation<?> operation) {
|
||||||
|
if (suspended) {
|
||||||
|
queue(operation);
|
||||||
|
} else {
|
||||||
|
apply(operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void apply(RacingEventServiceOperation<?> operation) {
|
||||||
|
racingEventServiceTracker.getRacingEventService().apply(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void queue(RacingEventServiceOperation<?> operation) {
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
queue.add(operation);
|
||||||
|
assert !queue.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void setSuspended(final boolean suspended) {
|
||||||
|
if (this.suspended != suspended) {
|
||||||
|
this.suspended = suspended;
|
||||||
|
if (!this.suspended) {
|
||||||
|
applyQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void applyQueue() {
|
||||||
|
for (Iterator<RacingEventServiceOperation<?>> i=queue.iterator(); i.hasNext(); ) {
|
||||||
|
RacingEventServiceOperation<?> operation = i.next();
|
||||||
|
i.remove();
|
||||||
|
apply(operation);
|
||||||
|
}
|
||||||
|
assert queue.isEmpty();
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isSuspended() {
|
||||||
|
return suspended;
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] getBytes(BytesMessage m) throws JMSException {
|
private byte[] getBytes(BytesMessage m) throws JMSException {
|
||||||
byte[] buf = new byte[(int) m.getBodyLength()];
|
byte[] buf = new byte[(int) m.getBodyLength()];
|
||||||
m.readBytes(buf);
|
m.readBytes(buf);
|
||||||
@@ -63,7 +142,7 @@ public class Replicator implements MessageListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Replicator for master "+master;
|
return "Replicator for master "+master+", queue size: "+queue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+10
-1
@@ -1,10 +1,13 @@
|
|||||||
package com.sap.sailing.server.operationaltransformation;
|
package com.sap.sailing.server.operationaltransformation;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||||
import com.sap.sailing.server.RacingEventService;
|
import com.sap.sailing.server.RacingEventService;
|
||||||
import com.sap.sailing.server.RacingEventServiceOperation;
|
import com.sap.sailing.server.RacingEventServiceOperation;
|
||||||
|
|
||||||
public class CreateFlexibleLeaderboard extends AbstractLeaderboardOperation<FlexibleLeaderboard> {
|
public class CreateFlexibleLeaderboard extends AbstractLeaderboardOperation<FlexibleLeaderboard> {
|
||||||
|
private static final Logger logger = Logger.getLogger(CreateFlexibleLeaderboard.class.getName());
|
||||||
private static final long serialVersionUID = 891352705068098580L;
|
private static final long serialVersionUID = 891352705068098580L;
|
||||||
private final int[] discardThresholds;
|
private final int[] discardThresholds;
|
||||||
|
|
||||||
@@ -15,7 +18,13 @@ public class CreateFlexibleLeaderboard extends AbstractLeaderboardOperation<Flex
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FlexibleLeaderboard internalApplyTo(RacingEventService toState) {
|
public FlexibleLeaderboard internalApplyTo(RacingEventService toState) {
|
||||||
return toState.addFlexibleLeaderboard(getLeaderboardName(), discardThresholds);
|
FlexibleLeaderboard result = null;
|
||||||
|
if (toState.getLeaderboardByName(getLeaderboardName()) == null) {
|
||||||
|
result = toState.addFlexibleLeaderboard(getLeaderboardName(), discardThresholds);
|
||||||
|
} else {
|
||||||
|
logger.warning("Cannot replicate creation of flexible leaderboard "+getLeaderboardName()+" because it already exists in the replica");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in new issue
Block a user