arcus-java-client icon indicating copy to clipboard operation
arcus-java-client copied to clipboard

CollectionBulkInsertOperation cancel 시 cancel 원인을 API 사용자가 알 수 없는 이슈

Open uhm0311 opened this issue 3 years ago • 5 comments

// 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 avatar Aug 17 '22 09:08 uhm0311

@uhm0311 위의 처리가 필요한 다른 연산이 존재하는 지 확인하고 알려주세요.

jhpark816 avatar Jun 27 '23 02:06 jhpark816

receivedStatus() 메소드에서 key를 알 수 없어서 OperationStatus를 정할 수 없는 경우는 CollectionBulkInsertOperation이 유일합니다.

uhm0311 avatar Jun 27 '23 02:06 uhm0311

Cacnel은 All or Nothing입니다. Operation.cancel()이 호출되었다면 Future.get() 메소드 호출 시 Future의 값을 얻어오지 못하고 Exception이 발생합니다. 즉, 어떤 Key에 한해 Cancel 되었는지를 확인할 방법이 없습니다. 따라서 이 이슈는 Close 합니다.

uhm0311 avatar Jun 29 '23 06:06 uhm0311

@uhm0311 본 이슈는 아래 경우에 의미가 있습니다.

  • multi op 연산에서 일부는 완료되고 나머지 일부만 cancel 처리하고, Future.get()에서 실패한 정보만 전달하는 경우
  • 만약, 모든 연산이 cancel 되면, exception 처리

생각하는 시간을 가지도록 하시죠.

jhpark816 avatar Jun 29 '23 11:06 jhpark816

현재 Multi op를 다루는 Future들은 모두 Operation 중 하나라도 Cancel 처리되었다면 Exception을 발생하는 것으로 되어 있습니다.

  • SMGetFuture
  • BroadcastFuture
  • BulkOperationFuture
  • CollectionGetBulkFuture
  • PipedCollectionFuture

일부의 결과라도 반환하도록 변경하면 하위 호환성이 깨질 수 있기 때문에 일부 결과를 반환하고 싶다면 getSome()과 같은 새로운 인터페이스를 제공하는 것이 나을 것 같습니다.

uhm0311 avatar Jan 26 '24 02:01 uhm0311