CollectionBulkInsertOperation cancel 시 cancel 원인을 API 사용자가 알 수 없는 이슈
// ArcusClient
private <T> Future<Map<String, CollectionOperationStatus>> asyncCollectionInsertBulk2(
List<CollectionBulkInsert<T>> insertList) {
final ConcurrentLinkedQueue<Operation> ops = new ConcurrentLinkedQueue<Operation>();
final Map<String, CollectionOperationStatus> failedResult =
new ConcurrentHashMap<String, CollectionOperationStatus>();
final CountDownLatch latch = new CountDownLatch(insertList.size());
for (final CollectionBulkInsert<T> insert : insertList) {
Operation op = opFact.collectionBulkInsert(
insert, new CollectionBulkInsertOperation.Callback() {
public void receivedStatus(OperationStatus status) {
// Nothing to do here because the user MUST search the result Map instance.
}
public void complete() {
latch.countDown();
}
public void gotStatus(String key, OperationStatus status) {
if (!status.isSuccess()) {
if (status instanceof CollectionOperationStatus) {
failedResult.put(key,
(CollectionOperationStatus) status);
} else {
failedResult.put(key,
new CollectionOperationStatus(status));
}
}
}
});
ops.add(op);
addOp(insert.getMemcachedNode(), op);
}
...
}
CollectionBulkInsertOperation.Callback.receivedStatus() 메소드는 비어 있다. 이는 Multi Key Operation이며, receivedStatus() 메소드 단에서는 어느 Key가 실패했는지 알 수 없기 때문이다. 해당 정보는 gotStatus() 메소드를 통해 전달해주어야 한다.
CollectionBulkInsertOperation.cancel() 호출 시 wasCancelled() 메소드를 통해 callback.receivedStatus() 메소드를 호출한다. cancel 동작 또한 연산 실패이므로 CollectionBulkInsertOperation의 failedResult에 포함되어야 하는데, receivedStatus() Callback으로는 Key 정보가 주어지지 않아 failedResult에 포함할 수 없다.
// BaseOperationImpl
public final void cancel(String cause) {
cancelled = true;
if (handlingNode != null) {
cancelCause = "Cancelled (" + cause + " : (" + handlingNode.getNodeName() + ")" + ")";
} else {
cancelCause = "Cancelled (" + cause + ")";
}
wasCancelled();
callback.complete();
}
// CollectionInsertOperationImpl
@Override
protected void wasCancelled() {
getCallback().receivedStatus(STORE_CANCELED);
}
// CollectionBulkInsertOperation.Callback
public void receivedStatus(OperationStatus status) {
// Nothing to do here because the user MUST search the result Map instance.
}
CollectionInsertOperationImpl.wasCancelled() 구현에 한해 callback.gotStatus() 메소드를 호출하도록 하여 failedResult에 cancel 원인이 담기도록 한다.
@uhm0311 위의 처리가 필요한 다른 연산이 존재하는 지 확인하고 알려주세요.
receivedStatus() 메소드에서 key를 알 수 없어서 OperationStatus를 정할 수 없는 경우는 CollectionBulkInsertOperation이 유일합니다.
Cacnel은 All or Nothing입니다. Operation.cancel()이 호출되었다면 Future.get() 메소드 호출 시 Future의 값을 얻어오지 못하고 Exception이 발생합니다. 즉, 어떤 Key에 한해 Cancel 되었는지를 확인할 방법이 없습니다. 따라서 이 이슈는 Close 합니다.
@uhm0311 본 이슈는 아래 경우에 의미가 있습니다.
- multi op 연산에서 일부는 완료되고 나머지 일부만 cancel 처리하고, Future.get()에서 실패한 정보만 전달하는 경우
- 만약, 모든 연산이 cancel 되면, exception 처리
생각하는 시간을 가지도록 하시죠.
현재 Multi op를 다루는 Future들은 모두 Operation 중 하나라도 Cancel 처리되었다면 Exception을 발생하는 것으로 되어 있습니다.
- SMGetFuture
- BroadcastFuture
- BulkOperationFuture
- CollectionGetBulkFuture
- PipedCollectionFuture
일부의 결과라도 반환하도록 변경하면 하위 호환성이 깨질 수 있기 때문에 일부 결과를 반환하고 싶다면 getSome()과 같은 새로운 인터페이스를 제공하는 것이 나을 것 같습니다.