Repository navigation
[JmsIO] Let restored checkpoint marks acknowledge live messages #40605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
94cf005
165d4f6
86d28a3
2b0b034
fd4f216
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,11 +26,15 @@ | |
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
| import org.apache.beam.sdk.io.UnboundedSource; | ||
| import org.apache.beam.sdk.io.jms.JmsIO.AcknowledgeMode; | ||
| import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; | ||
| import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.cache.Cache; | ||
| import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.cache.CacheBuilder; | ||
| import org.checkerframework.checker.nullness.qual.Nullable; | ||
| import org.joda.time.Instant; | ||
| import org.slf4j.Logger; | ||
|
|
@@ -42,8 +46,25 @@ | |
| */ | ||
| class JmsCheckpointMark implements UnboundedSource.CheckpointMark, Serializable { | ||
|
|
||
| // Pinned to the value computed for this class before the id field was added, so checkpoints | ||
| // written by those versions can still be restored. | ||
| private static final long serialVersionUID = -275230511397134772L; | ||
|
|
||
| private static final Logger LOG = LoggerFactory.getLogger(JmsCheckpointMark.class); | ||
|
|
||
| /** | ||
| * Live checkpoints of this JVM that still hold unacknowledged messages, by id. The messages, | ||
| * consumer and session of a checkpoint are transient, so a runner that serializes and restores | ||
| * the mark (or recreates the reader) would otherwise lose them and never acknowledge. A restored | ||
| * mark uses this registry to find the live checkpoint with the same id and finalize that one. To | ||
| * bound memory, at most 10,000 checkpoints are kept and those not finalized within an hour are | ||
| * dropped; the broker redelivers their messages. | ||
| */ | ||
| private static final Cache<String, JmsCheckpointMark> LIVE_CHECKPOINTS = | ||
| CacheBuilder.newBuilder().maximumSize(10_000).expireAfterWrite(1, TimeUnit.HOURS).build(); | ||
|
|
||
| // May be null when restored from a checkpoint written by a version without this field. | ||
| private final @Nullable String id = UUID.randomUUID().toString(); | ||
|
jbonofre marked this conversation as resolved.
Resolved
|
||
| private Instant oldestMessageTimestamp; | ||
| private transient @Nullable List<Message> messages; | ||
| private transient @Nullable MessageConsumer consumer; | ||
|
|
@@ -65,12 +86,27 @@ private JmsCheckpointMark( | |
|
|
||
| /** Acknowledge all outstanding message. */ | ||
| @Override | ||
| public void finalizeCheckpoint() { | ||
| @SuppressWarnings("ReferenceEquality") // live must be a different instance, not an equal one | ||
| public synchronized void finalizeCheckpoint() { | ||
| if (id != null) { | ||
| // Atomic, so only one copy of this checkpoint ever finalizes the live one. | ||
| JmsCheckpointMark live = LIVE_CHECKPOINTS.asMap().remove(id); | ||
| if (live != null && live != this) { | ||
| // This is a restored copy whose state lives in another instance. | ||
| live.finalizeCheckpoint(); | ||
| return; | ||
| } | ||
| } | ||
| if (messages == null && consumer == null && session == null && activeCheckpoints == null) { | ||
| // Nothing to acknowledge or release, e.g. a restored copy that was already finalized. | ||
| return; | ||
| } | ||
| try { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be return immediately here if
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A checkpoint without messages can still hold a consumer and session to close ( |
||
| if (messages != null) { | ||
| for (Message message : messages) { | ||
| message.acknowledge(); | ||
| } | ||
| messages = null; | ||
| } | ||
| } catch (JMSException e) { | ||
| // The effect of this is message not get acknowledged and thus will be redelivered. It is | ||
|
|
@@ -103,9 +139,19 @@ public void finalizeCheckpoint() { | |
|
|
||
| if (activeCheckpoints != null) { | ||
| activeCheckpoints.decrementAndGet(); | ||
| activeCheckpoints = null; | ||
| } | ||
| } | ||
|
|
||
| private void registerLive() { | ||
| LIVE_CHECKPOINTS.put(Objects.requireNonNull(id), this); | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| static long liveCheckpointCount() { | ||
| return LIVE_CHECKPOINTS.size(); | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| @Nullable List<Message> getMessages() { | ||
| return messages; | ||
|
|
@@ -139,6 +185,7 @@ public boolean equals(@Nullable Object o) { | |
| return false; | ||
| } | ||
| JmsCheckpointMark that = (JmsCheckpointMark) o; | ||
| // The id is random and only used to find the live checkpoint, so it is not part of equality. | ||
| return oldestMessageTimestamp.equals(that.oldestMessageTimestamp); | ||
| } | ||
|
|
||
|
|
@@ -251,6 +298,9 @@ JmsCheckpointMark newCheckpoint( | |
| activeCheckpoints); | ||
| messages.clear(); | ||
| oldestMessageTimestamp = Instant.now(); | ||
| if (messagesCopy != null) { | ||
| checkpointMark.registerLive(); | ||
| } | ||
| if (activeCheckpoints != null) { | ||
| activeCheckpoints.incrementAndGet(); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.