org.apache.omid.tso.ReplyProcessorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of omid-tso-server Show documentation
Show all versions of omid-tso-server Show documentation
Omid Transaction Status Oracle server and Timestamp Oracle
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.omid.tso;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject;
import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.BusySpinWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import org.apache.commons.pool2.ObjectPool;
import org.apache.omid.metrics.Meter;
import org.apache.omid.metrics.MetricsRegistry;
import org.apache.omid.proto.TSOProto;
import org.jboss.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import static com.codahale.metrics.MetricRegistry.name;
class ReplyProcessorImpl implements EventHandler, ReplyProcessor {
private static final Logger LOG = LoggerFactory.getLogger(ReplyProcessorImpl.class);
private final ObjectPool batchPool;
private final RingBuffer replyRing;
private AtomicLong nextIDToHandle = new AtomicLong();
private PriorityQueue futureEvents;
// Metrics
private final Meter abortMeter;
private final Meter commitMeter;
private final Meter timestampMeter;
@Inject
ReplyProcessorImpl(MetricsRegistry metrics, Panicker panicker, ObjectPool batchPool) {
this.batchPool = batchPool;
this.nextIDToHandle.set(0);
this.replyRing = RingBuffer.createMultiProducer(ReplyBatchEvent.EVENT_FACTORY, 1 << 12, new BusySpinWaitStrategy());
SequenceBarrier replySequenceBarrier = replyRing.newBarrier();
BatchEventProcessor replyProcessor = new BatchEventProcessor<>(replyRing, replySequenceBarrier, this);
replyProcessor.setExceptionHandler(new FatalExceptionHandler(panicker));
replyRing.addGatingSequences(replyProcessor.getSequence());
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("reply-%d").build();
ExecutorService replyExec = Executors.newSingleThreadExecutor(threadFactory);
replyExec.submit(replyProcessor);
this.futureEvents = new PriorityQueue<>(10, new Comparator() {
public int compare(ReplyBatchEvent replyBatchEvent1, ReplyBatchEvent replyBatchEvent2) {
return Long.compare(replyBatchEvent1.getBatchSequence(), replyBatchEvent2.getBatchSequence());
}
});
this.abortMeter = metrics.meter(name("tso", "aborts"));
this.commitMeter = metrics.meter(name("tso", "commits"));
this.timestampMeter = metrics.meter(name("tso", "timestampAllocation"));
}
private void handleReplyBatchEvent(ReplyBatchEvent event) throws Exception {
String name;
Batch batch = event.getBatch();
for (int i = 0; batch != null && i < batch.getNumEvents(); ++i) {
PersistEvent localEvent = batch.get(i);
switch (localEvent.getType()) {
case COMMIT:
name = "commitReplyProcessor";
localEvent.getMonCtx().timerStart(name);
sendCommitResponse(localEvent.getStartTimestamp(), localEvent.getCommitTimestamp(), localEvent.getChannel());
localEvent.getMonCtx().timerStop(name);
break;
case ABORT:
name = "abortReplyProcessor";
localEvent.getMonCtx().timerStart(name);
sendAbortResponse(localEvent.getStartTimestamp(), localEvent.getChannel());
localEvent.getMonCtx().timerStop(name);
break;
case TIMESTAMP:
name = "timestampReplyProcessor";
localEvent.getMonCtx().timerStart(name);
sendTimestampResponse(localEvent.getStartTimestamp(), localEvent.getChannel());
localEvent.getMonCtx().timerStop(name);
break;
default:
LOG.error("Unknown event {}", localEvent.getType());
break;
}
localEvent.getMonCtx().publish();
}
if (batch != null) {
batchPool.returnObject(batch);
}
}
private void processWaitingEvents() throws Exception {
while (!futureEvents.isEmpty() && futureEvents.peek().getBatchSequence() == nextIDToHandle.get()) {
ReplyBatchEvent e = futureEvents.poll();
handleReplyBatchEvent(e);
nextIDToHandle.incrementAndGet();
}
}
public void onEvent(ReplyBatchEvent event, long sequence, boolean endOfBatch) throws Exception {
// Order of event's reply need to be guaranteed in order to preserve snapshot isolation.
// This is done in order to present a scenario where a start id of N is returned
// while commit smaller than still does not appear in the commit table.
// If previous events were not processed yet (events contain smaller id)
if (event.getBatchSequence() > nextIDToHandle.get()) {
futureEvents.add(event);
return;
}
handleReplyBatchEvent(event);
nextIDToHandle.incrementAndGet();
// Process events that arrived before and kept in futureEvents.
processWaitingEvents();
}
@Override
public void manageResponsesBatch(long batchSequence, Batch batch) {
long seq = replyRing.next();
ReplyBatchEvent e = replyRing.get(seq);
ReplyBatchEvent.makeReplyBatch(e, batch, batchSequence);
replyRing.publish(seq);
}
@Override
public void sendCommitResponse(long startTimestamp, long commitTimestamp, Channel c) {
TSOProto.Response.Builder builder = TSOProto.Response.newBuilder();
TSOProto.CommitResponse.Builder commitBuilder = TSOProto.CommitResponse.newBuilder();
commitBuilder.setAborted(false)
.setStartTimestamp(startTimestamp)
.setCommitTimestamp(commitTimestamp);
builder.setCommitResponse(commitBuilder.build());
c.write(builder.build());
commitMeter.mark();
}
@Override
public void sendAbortResponse(long startTimestamp, Channel c) {
TSOProto.Response.Builder builder = TSOProto.Response.newBuilder();
TSOProto.CommitResponse.Builder commitBuilder = TSOProto.CommitResponse.newBuilder();
commitBuilder.setAborted(true);
commitBuilder.setStartTimestamp(startTimestamp);
builder.setCommitResponse(commitBuilder.build());
c.write(builder.build());
abortMeter.mark();
}
@Override
public void sendTimestampResponse(long startTimestamp, Channel c) {
TSOProto.Response.Builder builder = TSOProto.Response.newBuilder();
TSOProto.TimestampResponse.Builder respBuilder = TSOProto.TimestampResponse.newBuilder();
respBuilder.setStartTimestamp(startTimestamp);
builder.setTimestampResponse(respBuilder.build());
c.write(builder.build());
timestampMeter.mark();
}
final static class ReplyBatchEvent {
private Batch batch;
private long batchSequence;
static void makeReplyBatch(ReplyBatchEvent e, Batch batch, long batchSequence) {
e.batch = batch;
e.batchSequence = batchSequence;
}
Batch getBatch() {
return batch;
}
long getBatchSequence() {
return batchSequence;
}
final static EventFactory EVENT_FACTORY = new EventFactory() {
public ReplyBatchEvent newInstance() {
return new ReplyBatchEvent();
}
};
}
}