@Override public E next() { try { return next; } finally { next = poll(); } } };
public void add(E item) { new LinkedNode<>(item).appendTo(tail); }
public static <T> LinkedNode<T> anchor() { return new LinkedNode<>(null); }
@Override public void publishAsync(Record record, PublishCallback callback) { queue.add(new AsyncRecord(record, callback)); }
public ProducerPipe(ProducerPipeConfig config, Producer<K, V> producer, String threadName, ExceptionHandler exceptionHandler) { this.producer = producer; this.exceptionHandler = exceptionHandler; if (config.isAsync()) { queue = new NodeQueue<>(); queueConsumer = queue.consumer(); thread = WorkerThread.builder() .withOptions(new WorkerOptions().daemon().withName(threadName)) .onCycle(this::cycle) .buildAndStart(); } else { queue = null; queueConsumer = null; thread = null; } }
@Override public void attach(MessageHandler handler) { if (handler.getGroupId() != null && ! groups.add(handler.getGroupId())) return; final WorkerThread thread = WorkerThread.builder() .withOptions(new WorkerOptions().daemon().withName(MultiNodeQueueLedger.class, handler.getGroupId())) .onCycle(new NodeWorker(handler, handler.getGroupId(), queue.consumer())) .buildAndStart(); threads.add(thread); }
public QueueConsumer<E> consumer() { return new QueueConsumer<>(tail.get()); } }
public void appendTo(AtomicReference<LinkedNode<E>> tail) { final LinkedNode<E> t1 = tail.getAndSet(this); t1.lazySet(this); } }
@Override public Iterator<E> iterator() { return new Iterator<E>() { private E next = poll(); @Override public boolean hasNext() { return next != null; } @Override public E next() { try { return next; } finally { next = poll(); } } }; } }
@Override public void append(Message message, AppendCallback callback) { queue.add(message); callback.onAppend(message.getMessageId(), null); }
public int drain(List<E> sink) { int drained = 0; for (E item; (item = poll()) != null; sink.add(item), drained++); return drained; }
/** * Pushes a record and an optional send callback through the pipeline. * * @param record The record. * @param callback The callback (may be {@code null}). */ public void send(ProducerRecord<K, V> record, Callback callback) { if (thread != null) { queue.add(new AsyncRecord<>(record, callback)); } else { sendNow(record, callback); } }
private void cycle(WorkerThread t) throws InterruptedException { final AsyncRecord<K, V> rec = queueConsumer.poll(); if (rec != null) { sendNow(rec.record, rec.callback); yields = 0; } else if (yields++ < MAX_YIELDS) { Thread.yield(); } else { Thread.sleep(QUEUE_BACKOFF_MILLIS); } }
@Override public void append(Message message, AppendCallback callback) { if (debugMessageCounts != 0) { final long appends = this.appends.getAndIncrement(); if (appends % debugMessageCounts == 0) { logLine.accept(String.format("appends=%,d", appends)); } } queue.add(message); callback.onAppend(message.getMessageId(), null); }
private void cycle(WorkerThread thread) throws InterruptedException { final Message m = consumer.poll(); if (m != null) { for (MessageHandler handler : handlers) { handler.onMessage(context, m); } } else if (yields++ < maxYields) { Thread.yield(); } else { // resetting yields here appears counterintuitive (it makes more sense to reset it on a hit than a miss), // however, this technique avoids writing to an instance field from a hotspot, markedly improving performance // at the expense of (1) prematurely sleeping on the next miss and (2) yielding after a sleep yields = 0; Thread.sleep(POLL_BACKOFF_MILLIS); } }
private void decideBallot(PendingBallot ballot) { zlg.t("Decided ballot for %s: resolution: %s", z -> z.arg(ballot::getProposal).arg(ballot::getResolution)); final Proposal proposal = ballot.getProposal(); final String xid = proposal.getXid(); final Object metadata = metadataEnabled ? new OutcomeMetadata(proposal.getTimestamp()) : null; final Outcome outcome = new Outcome(xid, ballot.getResolution(), ballot.getAbortReason(), ballot.getResponses(), metadata) .inResponseTo(proposal).withSource(groupId); pending.remove(xid); if (trackingEnabled) { additions.add(outcome); } action.appendOutcome(outcome, (id, x) -> { if (x == null) { ballot.getConfirmation().confirm(); } else { zlg.w("Error appending to ledger [message: %s]", z -> z.arg(outcome).threw(x)); } }); }
private void publisherCycle(WorkerThread t) throws InterruptedException { List<AsyncRecord> recs = null; for (;;) { final AsyncRecord rec = queueConsumer.poll(); if (rec != null) { if (recs == null) { recs = new ArrayList<>(); yields = 0; } recs.add(rec); if (recs.size() == MAX_BATCH_SIZE) { sendNow(recs); return; } } else { if (recs != null) { sendNow(recs); } else if (yields++ < PUBLISH_MAX_YIELDS) { Thread.yield(); } else { Thread.sleep(PUBLISH_BACKOFF_MILLIS); } return; } } }
@Override public void cycle(WorkerThread thread) throws InterruptedException { final Message m = consumer.poll(); if (m != null) { if (debugMessageCounts != 0) { final long consumed = this.consumed.getAndIncrement(); if (consumed % debugMessageCounts == 0) { logLine.accept(String.format("groupId=%s, consumed=%,d", groupId, consumed)); } } handler.onMessage(context, m); } else if (yields++ < maxYields) { Thread.yield(); } else { // resetting yields here appears counterintuitive (it makes more sense to reset it on a hit than a miss); // however, this technique avoids writing to an instance field from a hotspot, markedly improving performance // at the expense of (1) prematurely sleeping on the next miss and (2) yielding after a sleep yields = 0; Thread.sleep(POLL_BACKOFF_MILLIS); } } }