private void validateAppend(Append append, Session session) { if (session == null || !session.id.equals(append.getWriterId())) { throw new InvalidMessageException("Sending appends without setting up the append."); } if (append.getEventNumber() <= session.lastEventNumber) { throw new InvalidMessageException("Events written out of order. Received: " + append.getEventNumber() + " following: " + session.lastEventNumber); } if (append.isConditional()) { throw new IllegalArgumentException("Conditional appends should be written via a ConditionalAppend object."); } Preconditions.checkState(bytesLeftInBlock == 0 || bytesLeftInBlock > TYPE_PLUS_LENGTH_SIZE, "Bug in CommandEncoder.encode, block is too small."); }
@Override public void send(Append append) throws ConnectionFailedException { recentMessage.set(true); batchSizeTracker.recordAppend(append.getEventNumber(), append.getData().readableBytes()); Futures.getAndHandleExceptions(getChannel().writeAndFlush(append), ConnectionFailedException::new); }
/** * Append data to the store. * Because ordering dictates that there only be one outstanding append from a given connection, this is implemented * by adding the append to a queue. */ @Override public void append(Append append) { log.trace("Processing append received from client {}", append); UUID id = append.getWriterId(); synchronized (lock) { Long lastEventNumber = latestEventNumbers.get(Pair.of(append.getSegment(), id)); Preconditions.checkState(lastEventNumber != null, "Data from unexpected connection: %s.", id); Preconditions.checkState(append.getEventNumber() >= lastEventNumber, "Event was already appended."); waitingAppends.put(id, append); } pauseOrResumeReading(); performNextWrite(); }
long eventNumber = last.getEventNumber(); outstandingAppend = new Append(segment, writer, eventNumber, eventCount, data, null);
private CompletableFuture<Void> storeAppend(Append append) { long lastEventNumber; synchronized (lock) { lastEventNumber = latestEventNumbers.get(Pair.of(append.getSegment(), append.getWriterId())); } List<AttributeUpdate> attributes = Arrays.asList( new AttributeUpdate(append.getWriterId(), AttributeUpdateType.ReplaceIfEquals, append.getEventNumber(), lastEventNumber), new AttributeUpdate(EVENT_COUNT, AttributeUpdateType.Accumulate, append.getEventCount())); ByteBuf buf = append.getData().asReadOnly(); byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); if (append.isConditional()) { return store.append(append.getSegment(), append.getExpectedLength(), bytes, attributes, TIMEOUT); } else { return store.append(append.getSegment(), bytes, attributes, TIMEOUT); } }
if (conditionalFailed) { log.debug("Conditional append failed due to incorrect offset: {}, {}", append, exception.getMessage()); connection.send(new ConditionalCheckFailed(append.getWriterId(), append.getEventNumber())); } else { handleException(append.getWriterId(), append.getEventNumber(), append.getSegment(), "appending data", exception); statsRecorder.record(append.getSegment(), append.getDataLength(), append.getEventCount()); final DataAppended dataAppendedAck = new DataAppended(append.getWriterId(), append.getEventNumber(), previousEventNumber); log.trace("Sending DataAppended : {}", dataAppendedAck); outstandingAppend = null; if (exception == null) { latestEventNumbers.put(Pair.of(append.getSegment(), append.getWriterId()), append.getEventNumber()); } else { if (!conditionalFailed) { performNextWrite(); } catch (Throwable e) { handleException(append.getWriterId(), append.getEventNumber(), append.getSegment(), "handling append result", e);
@Override public void sendAsync(List<Append> appends, CompletedCallback callback) { recentMessage.set(true); Channel ch; try { ch = getChannel(); } catch (ConnectionFailedException e) { callback.complete(new ConnectionFailedException("Connection to " + connectionName + " is not established.")); return; } PromiseCombiner combiner = new PromiseCombiner(); for (Append append : appends) { batchSizeTracker.recordAppend(append.getEventNumber(), append.getData().readableBytes()); combiner.add(ch.write(append)); } ch.flush(); ChannelPromise promise = ch.newPromise(); promise.addListener(new GenericFutureListener<Future<? super Void>>() { @Override public void operationComplete(Future<? super Void> future) throws Exception { Throwable cause = future.cause(); callback.complete(cause == null ? null : new ConnectionFailedException(cause)); } }); combiner.finish(promise); }
session.lastEventNumber = append.getEventNumber(); session.eventCount++; ByteBuf data = append.getData().slice();
private void verify(List<Object> results, int numValues, int sizeOfEachValue) { int currentValue = -1; int currentCount = sizeOfEachValue; for (Object r : results) { Append append = (Append) r; assertEquals("Append split mid event", sizeOfEachValue, currentCount); while (append.getData().isReadable()) { if (currentCount == sizeOfEachValue) { assertEquals(EVENT.getCode(), append.getData().readInt()); assertEquals(sizeOfEachValue, append.getData().readInt()); currentCount = 0; currentValue++; } byte readByte = append.getData().readByte(); assertEquals((byte) currentValue, readByte); currentCount++; } assertEquals(currentValue, append.getEventNumber()); } assertEquals(numValues - 1, currentValue); assertEquals(currentCount, sizeOfEachValue); }