Queue Worker: Ack After Processing

queue-worker
Mechanism lab

Queue Worker: Ack After Processing

A worker pulls a message off a queue and acks it before it finishes processing. When processing fails, the message is already gone and is lost forever. Fix the ack ordering so failures are redelivered (at-least-once).

You'll predict the failure first, then write the fix and watch your own code drive the behavior.

Understand it step by step (한국어로 이해 → 영어로 말하기)

큐에서 일감(메시지)을 하나 꺼내서 처리하는 일꾼이 있다. 그런데 일을 끝내기도 전에 "다 했다"고 큐에 말해버린다. 일이 실패하면 그 일감은 이미 사라져서 영영 못 찾는다.
  1. 1

    큐(대기줄)에 일감이 쌓여 있고, 일꾼이 맨 앞 일감을 하나 꺼낸다. 꺼낸 일감은 '처리 중' 칸으로 옮겨진다.

    🔧 도구:MessageQueueinflight set

    String m = queue.pollFirst();
    if (m != null) inflight.add(m); // moved to inflight
    return m;
    🗣 영어로 말해

    A worker pulls one message off the queue to process it.

    checking microphone…

  2. 2

    'ack'는 큐한테 '이거 다 끝냈으니 지워도 돼'라고 말하는 거다. ack하면 그 일감은 완전히 사라진다.

    🔧 도구:ack()

    void ack(String msg) {
        inflight.remove(msg); // done -> removed for good
    }
    🗣 영어로 말해

    Ack tells the queue the message is done, so it gets removed for good.

    checking microphone…

  3. 3

    지금 코드의 순서가 거꾸로다. 일을 시작하기 전에 먼저 ack부터 해버린다. 일감을 지우고 나서 일을 한다.

    ⚖️ Trade-off: 먼저 지워버리면, 일하다 실패해도 되돌릴 게 없다.

    🔧 도구:ack() before process()

    boolean process(String msg, boolean willFail) {
        if (willFail) return false; // do the work FIRST, do not ack yet
        done.add(msg);
        mq.ack(msg);
        return true;
    }
    🗣 영어로 말해

    The bug is the order: it acks first, and only then does the work.

    checking microphone…

  4. 4

    일이 중간에 실패한다고 해보자. 일감은 이미 지워졌다. 'redeliver(다시 넣기)'를 불러도 넣을 일감이 없다 → 그 일은 영영 사라진다.

    ⚖️ Trade-off: 한 번 삐끗하면 그 메시지는 조용히 증발한다. 아무도 모른다.

    🔧 도구:redeliver()

    void redeliver(String msg) {
        if (inflight.remove(msg)) queue.addFirst(msg); // only works if still inflight
    }
    🗣 영어로 말해

    If the work fails, the message is already gone, so it is silently lost.

    checking microphone…

  5. 5

    고치는 법: 순서를 바꾼다. 일을 먼저 다 끝내고, 성공했을 때만 마지막에 ack한다.

    ✅ Fix: 성공해야만 지운다. 실패하면 일감은 '처리 중' 칸에 그대로 남는다.

    🔧 도구:process() then ack()

    if (willFail) return false; // fail: skip ack
    done.add(msg);
    mq.ack(msg); // ack ONLY after success
    🗣 영어로 말해

    The fix is to ack only after the work succeeds.

    checking microphone…

  6. 6

    이제 실패하면? 일감이 아직 살아 있으니 redeliver가 그걸 큐 맨 앞에 다시 넣는다. 다음에 또 시도한다 → 최소 한 번은 처리된다.

    🔧 도구:redeliver()at-least-once delivery

    boolean ok = w.process(got, true);
    if (!ok) mq.redeliver(got); // still inflight -> back to head, retried
    🗣 영어로 말해

    On failure the message stays inflight, so redelivery puts it back to retry.

    checking microphone…

  7. 7

    단, 새 문제가 하나 생긴다. 일은 성공했는데 ack 직전에 일꾼이 죽으면? 큐는 성공을 못 봤으니 같은 일감을 또 보낸다 → 같은 일이 두 번 처리될 수 있다.

    ⚖️ Trade-off: 성공-직후-사망 시 같은 메시지가 한 번 중복으로 다시 온다.

    ✅ Fix: 받는 쪽을 '여러 번 와도 결과가 같게(멱등)' 만든다. 그럼 중복이 와도 안전하다. 잃어버리는 것보단 두 번이 낫다.

    🔧 도구:idempotency keydedup on consumer

    // worker may die after process() but before ack() -> one duplicate redelivery
    if (seen.add(key)) done.add(msg); // consumer dedups -> idempotent
    🗣 영어로 말해

    It can duplicate if a worker dies after the work but before the ack, so consumers must be idempotent.

    checking microphone…

7단계 영어를 다 말하면 → 이 메커니즘 전체를 영어로 설명할 수 있게 된다.

Explain it in English

Say it out loud in your own words — hit these terms — then check against the gold answer.

Say it in your own words — but hit these: queue · at least once · concurrency · idempotency

checking microphone…