[CONTROLLER-524] Introduce example which shows read/write/retry cycle with OptimisticLockFailedException Created: 30/May/14  Updated: 03/Jul/14  Due: 30/Jun/14  Resolved: 03/Jul/14

Status: Resolved
Project: controller
Component/s: mdsal
Affects Version/s: Helium
Fix Version/s: None

Type: Improvement
Reporter: Tony Tkacik Assignee: Tony Tkacik
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Operating System: Windows
Platform: PC


Issue Links:
Blocks
is blocked by CONTROLLER-522 Promote DataPreconditionFailedExcepti... Resolved

 Description   

Introduce an example which will showcase proper handling
of failure of Data Broker commit, which failed because of OptimisticLockFailedException.



 Comments   
Comment by Tony Tkacik [ 30/Jun/14 ]

https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Migration:Data_Broker#New_API_-_Write.2FCommit.2FRetry_Example

Comment by Rob Adams [ 30/Jun/14 ]

The example isn't quite right. The logic needs to exhibit a read/modify/write loop and (in a synchronous pseudocode to make it easy to follow) it would look roughly like:

while (true) {
try

{ Transaction t = newTransaction(); ChunkOData d = t.read(chunkodatapath); ChunkOData newChunk = modify(d, relevantEvent); t.write(chunkodatapath, newChunk); break; }

catch (ConcurrentModificationException)

{ // nothing to do here }

}

The read/modify part here is important for correctness, since if you get this exception if you just keep retrying your write you'll lose the data that was written by the other process or thread.

Comment by Tony Tkacik [ 01/Jul/14 ]

Updated to:

private void readWriteRetry() {
ReadWriteTransaction writeTx = dataBroker.newReadWriteTransaction();

Optional<DataObject> readed = writeTx.read(LogicalDatastoreType.OPERATIONAL,PATH).get();
DataObject modified = modifyData(readed);
writeTx.put(LogicalDatastoreType.OPERATIONAL, PATH, data);
ListenableFuture<RpcResult<TransactionStatus>> future = writeTx.commit();

Futures.addCallback(future, new FutureCallback<RpcResult<TransactionStatus>>() {

@Override
public void onSuccess(final RpcResult<TransactionStatus> result)

{ // Commited successfully // Nothing to do }

@Override
public void onFailure(final Throwable t) {
// Transaction failed

if(t instanceof OptimisticLockFailedException)

{ LOG.error("Concurrent modification of data",e); readWriteRetry(); }

}
});
}

This example is bit harder to read than your pseudocode, but illustrates read/write/retry using ListenableFutures
to get transaction status.

Comment by Rob Adams [ 01/Jul/14 ]

Looks much better, though this documentation really shouldn't be on this hidden page. I might change to:

private void readWriteRetry(RelevantEvent event)

{ // ... DataObject modified = modifyData(readed, event); // ... }

but this is not critical.

Comment by Rob Adams [ 01/Jul/14 ]

By the way, do we recommend calling get() on the read operation future? Or are you supposed to write the double chain of async events?

Is each module supposed to allocate its own executor or is there a global shared one to use?

Comment by Tony Tkacik [ 02/Jul/14 ]

Futures.addCallback(Future,FutureCallback) for callback uses thread in which future completed (e.g. MD-SAL thread in this use cases). Futures.addCallback(Future,FutureCallback,Executor) uses executor of your choice.

We do not have global shared executor.

Generated at Wed Feb 07 19:53:14 UTC 2024 using Jira 8.20.10#820010-sha1:ace47f9899e9ee25d7157d59aa17ab06aee30d3d.