// EXCERCISE This code assumes there is no reservation and tries to create one. If a reservation exist then the update will fail. This is a good strategy
// when it is expected there are usually no reservations. Could modify the code to scan first.
// The following mutation requires that the column tx:seq does not exist and will fail if it does.
ConditionalMutation update = new ConditionalMutation(row, new Condition("tx", "seq"));
update.put("tx", "seq", "0");
update.put("res", String.format("%04d", 0), who);
ReservationResult result = ReservationResult.RESERVED;
ConditionalWriter cwriter = conn.createConditionalWriter(rTable, new ConditionalWriterConfig());
try {
while (true) {
Status status = cwriter.write(update).getStatus();
switch (status) {
case ACCEPTED:
return result;
case REJECTED:
case UNKNOWN:
// read the row and decide what to do
break;
default:
throw new RuntimeException("Unexpected status " + status);
}
// EXCERCISE in the case of many threads trying to reserve a slot, this approach of immediately retrying is inefficient. Exponential back-off is good
// general solution to solve contention problems like this. However in this particular case, exponential back-off could penalize the earliest threads
// that attempted to make a reservation by putting them later in the list. A more complex solution could involve having independent sub-queues within
// the row that approximately maintain arrival order and use exponential back off to fairly merge the sub-queues into the main queue.
// it is important to use an isolated scanner so that only whole mutations are seen
Scanner scanner = new IsolatedScanner(conn.createScanner(rTable, Authorizations.EMPTY));
scanner.setRange(new Range(row));
int seq = -1;
int maxReservation = -1;
for (Entry<Key,Value> entry : scanner) {
String cf = entry.getKey().getColumnFamilyData().toString();
String cq = entry.getKey().getColumnQualifierData().toString();
String val = entry.getValue().toString();
if (cf.equals("tx") && cq.equals("seq")) {
seq = Integer.parseInt(val);
} else if (cf.equals("res")) {
// EXCERCISE scanning the entire list to find if reserver is already in the list is inefficient. One possible way to solve this would be to sort the
// data differently in Accumulo so that finding the reserver could be done quickly.
if (val.equals(who))
if (maxReservation == -1)
return ReservationResult.RESERVED; // already have the first reservation
else
return ReservationResult.WAIT_LISTED; // already on wait list
// EXCERCISE the way this code finds the max reservation is very inefficient.... it would be better if it did not have to scan the entire row.
// One possibility is to just use the sequence number. Could also consider sorting the data in another way and/or using an iterator.
maxReservation = Integer.parseInt(cq);
}
}
Condition condition = new Condition("tx", "seq");
if (seq >= 0)
condition.setValue(seq + ""); // only expect a seq # if one was seen
update = new ConditionalMutation(row, condition);
update.put("tx", "seq", (seq + 1) + "");
update.put("res", String.format("%04d", maxReservation + 1), who);
// EXCERCISE if set capacity is implemented, then result should take capacity into account
if (maxReservation == -1)
result = ReservationResult.RESERVED; // if successful, will be first reservation
else