use of com.twitter.util.FutureEventListener in project distributedlog by twitter.
the class ZKDistributedLock method asyncTryLock.
void asyncTryLock(SessionLock lock, final Promise<ZKDistributedLock> acquirePromise, final long lockTimeout) {
if (null != tryLockFuture) {
tryLockFuture.cancel();
}
tryLockFuture = lock.asyncTryLock(lockTimeout, TimeUnit.MILLISECONDS);
tryLockFuture.addEventListener(OrderedFutureEventListener.of(new FutureEventListener<LockWaiter>() {
@Override
public void onSuccess(LockWaiter waiter) {
synchronized (ZKDistributedLock.this) {
if (closed) {
LOG.info("Skipping acquiring lock {} since it is already closed", lockPath);
waiter.getAcquireFuture().raise(new LockingException(lockPath, "lock is already closed."));
FutureUtils.setException(acquirePromise, newLockClosedException());
return;
}
}
tryLockFuture = null;
lockWaiter = waiter;
waitForAcquire(waiter, acquirePromise);
}
@Override
public void onFailure(Throwable cause) {
FutureUtils.setException(acquirePromise, cause);
}
}, lockStateExecutor, lockPath));
}
use of com.twitter.util.FutureEventListener in project distributedlog by twitter.
the class BKLogWriteHandler method doCompleteAndCloseLogSegment.
protected void doCompleteAndCloseLogSegment(final String inprogressZnodeName, long logSegmentSeqNo, long ledgerId, long firstTxId, long lastTxId, int recordCount, long lastEntryId, long lastSlotId, final Promise<LogSegmentMetadata> promise) {
try {
lock.checkOwnershipAndReacquire();
} catch (IOException ioe) {
FutureUtils.setException(promise, ioe);
return;
}
LOG.debug("Completing and Closing Log Segment {} {}", firstTxId, lastTxId);
final String inprogressZnodePath = inprogressZNode(inprogressZnodeName);
LogSegmentMetadata inprogressLogSegment = readLogSegmentFromCache(inprogressZnodeName);
// validate log segment
if (inprogressLogSegment.getLedgerId() != ledgerId) {
FutureUtils.setException(promise, new IOException("Active ledger has different ID to inprogress. " + inprogressLogSegment.getLedgerId() + " found, " + ledgerId + " expected"));
return;
}
// validate the transaction id
if (inprogressLogSegment.getFirstTxId() != firstTxId) {
FutureUtils.setException(promise, new IOException("Transaction id not as expected, " + inprogressLogSegment.getFirstTxId() + " found, " + firstTxId + " expected"));
return;
}
// validate the log sequence number
if (validateLogSegmentSequenceNumber) {
synchronized (inprogressLSSNs) {
if (inprogressLSSNs.isEmpty()) {
FutureUtils.setException(promise, new UnexpectedException("Didn't find matched inprogress log segments when completing inprogress " + inprogressLogSegment));
return;
}
long leastInprogressLSSN = inprogressLSSNs.getFirst();
// it should also be same as the least inprogress log segment sequence number tracked in {@link inprogressLSSNs}
if ((inprogressLogSegment.getLogSegmentSequenceNumber() != logSegmentSeqNo) || (leastInprogressLSSN != logSegmentSeqNo)) {
FutureUtils.setException(promise, new UnexpectedException("Didn't find matched inprogress log segments when completing inprogress " + inprogressLogSegment));
return;
}
}
}
// store max sequence number.
long maxSeqNo = Math.max(logSegmentSeqNo, maxLogSegmentSequenceNo.getSequenceNumber());
if (maxLogSegmentSequenceNo.getSequenceNumber() == logSegmentSeqNo || (maxLogSegmentSequenceNo.getSequenceNumber() == logSegmentSeqNo + 1)) {
// ignore the case that a new inprogress log segment is pre-allocated
// before completing current inprogress one
LOG.info("Try storing max sequence number {} in completing {}.", new Object[] { logSegmentSeqNo, inprogressZnodePath });
} else {
LOG.warn("Unexpected max ledger sequence number {} found while completing log segment {} for {}", new Object[] { maxLogSegmentSequenceNo.getSequenceNumber(), logSegmentSeqNo, getFullyQualifiedName() });
if (validateLogSegmentSequenceNumber) {
FutureUtils.setException(promise, new DLIllegalStateException("Unexpected max log segment sequence number " + maxLogSegmentSequenceNo.getSequenceNumber() + " for " + getFullyQualifiedName() + ", expected " + (logSegmentSeqNo - 1)));
return;
}
}
// Prepare the completion
final String nameForCompletedLedger = completedLedgerZNodeName(firstTxId, lastTxId, logSegmentSeqNo);
final String pathForCompletedLedger = completedLedgerZNode(firstTxId, lastTxId, logSegmentSeqNo);
long startSequenceId;
try {
startSequenceId = computeStartSequenceId(inprogressLogSegment);
} catch (IOException ioe) {
FutureUtils.setException(promise, ioe);
return;
}
// write completed ledger znode
final LogSegmentMetadata completedLogSegment = inprogressLogSegment.completeLogSegment(pathForCompletedLedger, lastTxId, recordCount, lastEntryId, lastSlotId, startSequenceId);
setLastLedgerRollingTimeMillis(completedLogSegment.getCompletionTime());
// prepare the transaction
ZKTransaction txn = new ZKTransaction(zooKeeperClient);
// create completed log segment
writeLogSegment(txn, zooKeeperClient.getDefaultACL(), nameForCompletedLedger, completedLogSegment, pathForCompletedLedger);
// delete inprogress log segment
deleteLogSegment(txn, inprogressZnodeName, inprogressZnodePath);
// store max sequence number
storeMaxSequenceNumber(txn, maxLogSegmentSequenceNo, maxSeqNo, false);
// update max txn id.
LOG.debug("Trying storing LastTxId in Finalize Path {} LastTxId {}", pathForCompletedLedger, lastTxId);
storeMaxTxId(txn, maxTxId, lastTxId);
txn.execute().addEventListener(FutureEventListenerRunnable.of(new FutureEventListener<Void>() {
@Override
public void onSuccess(Void value) {
LOG.info("Completed {} to {} for {} : {}", new Object[] { inprogressZnodeName, nameForCompletedLedger, getFullyQualifiedName(), completedLogSegment });
FutureUtils.setValue(promise, completedLogSegment);
}
@Override
public void onFailure(Throwable cause) {
FutureUtils.setException(promise, cause);
}
}, scheduler));
}
use of com.twitter.util.FutureEventListener in project distributedlog by twitter.
the class ReadUtils method asyncReadRecord.
private static Future<LogRecordWithDLSN> asyncReadRecord(final String streamName, final LogSegmentMetadata l, final boolean fence, final boolean includeControl, final boolean includeEndOfStream, final int scanStartBatchSize, final int scanMaxBatchSize, final AtomicInteger numRecordsScanned, final ExecutorService executorService, final LedgerHandleCache handleCache, final LogRecordSelector selector, final boolean backward, final long startEntryId) {
final Promise<LogRecordWithDLSN> promise = new Promise<LogRecordWithDLSN>();
FutureEventListener<LedgerDescriptor> openLedgerListener = new FutureEventListener<LedgerDescriptor>() {
@Override
public void onSuccess(final LedgerDescriptor ledgerDescriptor) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} Opened logsegment {} for reading record", streamName, l);
}
promise.ensure(new AbstractFunction0<BoxedUnit>() {
@Override
public BoxedUnit apply() {
handleCache.asyncCloseLedger(ledgerDescriptor);
return BoxedUnit.UNIT;
}
});
if (LOG.isDebugEnabled()) {
LOG.debug("{} {} scanning {}.", new Object[] { (backward ? "backward" : "forward"), streamName, l });
}
asyncReadRecordFromLogSegment(streamName, ledgerDescriptor, handleCache, l, executorService, scanStartBatchSize, scanMaxBatchSize, includeControl, includeEndOfStream, promise, numRecordsScanned, selector, backward, startEntryId);
}
@Override
public void onFailure(final Throwable cause) {
String errMsg = "Error opening log segment [" + l + "] for reading record of " + streamName;
promise.setException(new IOException(errMsg, BKException.create(FutureUtils.bkResultCode(cause))));
}
};
handleCache.asyncOpenLedger(l, fence).addEventListener(FutureEventListenerRunnable.of(openLedgerListener, executorService));
return promise;
}
use of com.twitter.util.FutureEventListener in project distributedlog by twitter.
the class ReadUtils method getLogRecordNotLessThanTxIdFromEntries.
/**
* Find the log record whose transaction id is not less than provided <code>transactionId</code> from
* entries between <code>startEntryId</code> and <code>endEntryId</code>.
*
* @param logName
* name of the log
* @param segment
* log segment
* @param transactionId
* provided transaction id to search
* @param executorService
* executor service
* @param handleCache
* handle cache
* @param entriesToSearch
* list of entries to search
* @param nWays
* how many entries to search in parallel
* @param prevFoundRecord
* the log record found in previous search
* @param promise
* promise to satisfy the result
*/
private static void getLogRecordNotLessThanTxIdFromEntries(final String logName, final LedgerDescriptor ld, final LogSegmentMetadata segment, final long transactionId, final ExecutorService executorService, final LedgerHandleCache handleCache, final List<Long> entriesToSearch, final int nWays, final Optional<LogRecordWithDLSN> prevFoundRecord, final Promise<Optional<LogRecordWithDLSN>> promise) {
final List<Future<LogRecordWithDLSN>> searchResults = Lists.newArrayListWithExpectedSize(entriesToSearch.size());
for (Long entryId : entriesToSearch) {
LogRecordSelector selector = new FirstTxIdNotLessThanSelector(transactionId);
Future<LogRecordWithDLSN> searchResult = asyncReadRecordFromEntries(logName, ld, handleCache, segment, executorService, new SingleEntryScanContext(entryId), selector);
searchResults.add(searchResult);
}
FutureEventListener<List<LogRecordWithDLSN>> processSearchResultsListener = new FutureEventListener<List<LogRecordWithDLSN>>() {
@Override
public void onSuccess(List<LogRecordWithDLSN> resultList) {
processSearchResults(logName, ld, segment, transactionId, executorService, handleCache, resultList, nWays, prevFoundRecord, promise);
}
@Override
public void onFailure(Throwable cause) {
promise.setException(cause);
}
};
Future.collect(searchResults).addEventListener(FutureEventListenerRunnable.of(processSearchResultsListener, executorService));
}
use of com.twitter.util.FutureEventListener in project distributedlog by twitter.
the class ReadUtils method getLogRecordNotLessThanTxId.
//
// Search Functions
//
/**
* Get the log record whose transaction id is not less than provided <code>transactionId</code>.
*
* <p>
* It uses a binary-search like algorithm to find the log record whose transaction id is not less than
* provided <code>transactionId</code> within a log <code>segment</code>. You could think of a log segment
* in terms of a sequence of records whose transaction ids are non-decreasing.
*
* - The sequence of records within a log segment is divided into N pieces.
* - Find the piece of records that contains a record whose transaction id is not less than provided
* <code>transactionId</code>.
*
* N could be chosen based on trading off concurrency and latency.
* </p>
*
* @param logName
* name of the log
* @param segment
* metadata of the log segment
* @param transactionId
* transaction id
* @param executorService
* executor service used for processing entries
* @param handleCache
* ledger handle cache
* @param nWays
* how many number of entries to search in parallel
* @return found log record. none if all transaction ids are less than provided <code>transactionId</code>.
*/
public static Future<Optional<LogRecordWithDLSN>> getLogRecordNotLessThanTxId(final String logName, final LogSegmentMetadata segment, final long transactionId, final ExecutorService executorService, final LedgerHandleCache handleCache, final int nWays) {
if (!segment.isInProgress()) {
if (segment.getLastTxId() < transactionId) {
// all log records whose transaction id is less than provided transactionId
// then return none
Optional<LogRecordWithDLSN> noneRecord = Optional.absent();
return Future.value(noneRecord);
}
}
final Promise<Optional<LogRecordWithDLSN>> promise = new Promise<Optional<LogRecordWithDLSN>>();
final FutureEventListener<LedgerDescriptor> openLedgerListener = new FutureEventListener<LedgerDescriptor>() {
@Override
public void onSuccess(final LedgerDescriptor ld) {
promise.ensure(new AbstractFunction0<BoxedUnit>() {
@Override
public BoxedUnit apply() {
handleCache.asyncCloseLedger(ld);
return BoxedUnit.UNIT;
}
});
long lastEntryId;
try {
lastEntryId = handleCache.getLastAddConfirmed(ld);
} catch (BKException e) {
promise.setException(e);
return;
}
if (lastEntryId < 0) {
// it means that the log segment is created but not written yet or an empty log segment.
// it is equivalent to 'all log records whose transaction id is less than provided transactionId'
Optional<LogRecordWithDLSN> nonRecord = Optional.absent();
promise.setValue(nonRecord);
return;
}
// all log records whose transaction id is not less than provided transactionId
if (segment.getFirstTxId() >= transactionId) {
final FirstTxIdNotLessThanSelector selector = new FirstTxIdNotLessThanSelector(transactionId);
asyncReadRecordFromEntries(logName, ld, handleCache, segment, executorService, new SingleEntryScanContext(0L), selector).addEventListener(new FutureEventListener<LogRecordWithDLSN>() {
@Override
public void onSuccess(LogRecordWithDLSN value) {
promise.setValue(Optional.of(selector.result()));
}
@Override
public void onFailure(Throwable cause) {
promise.setException(cause);
}
});
return;
}
getLogRecordNotLessThanTxIdFromEntries(logName, ld, segment, transactionId, executorService, handleCache, Lists.newArrayList(0L, lastEntryId), nWays, Optional.<LogRecordWithDLSN>absent(), promise);
}
@Override
public void onFailure(final Throwable cause) {
String errMsg = "Error opening log segment [" + segment + "] for find record from " + logName;
promise.setException(new IOException(errMsg, BKException.create(FutureUtils.bkResultCode(cause))));
}
};
handleCache.asyncOpenLedger(segment, false).addEventListener(FutureEventListenerRunnable.of(openLedgerListener, executorService));
return promise;
}
Aggregations