Skip to content

ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator - #6770

Open
amarkevich wants to merge 1 commit into
apache:mainfrom
amarkevich:ARTEMIS-6289
Open

amarkevich wants to merge 1 commit into
apache:mainfrom
amarkevich:ARTEMIS-6289

Conversation

@amarkevich

Copy link
Copy Markdown
Contributor

No description provided.

@jbertram

jbertram commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

This looks good to me, although org.apache.activemq.artemis.utils.SimpleIDGenerator doesn't get a lot of (if any) concurrent use. @clebertsuconic, @gemmellr, what do you think?

That said, I did notice that there's no unit test for org.apache.activemq.artemis.utils.SimpleIDGenerator at all. It might be nice to add one (e.g. to ensure IllegalStateException is thrown as expected).

return idSequence.getAndUpdate(id -> {
if (id == Long.MAX_VALUE) {
// Wrap - Very unlikely to happen
throw new IllegalStateException("Exhausted ids to use!");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would set it to 0 if this ever happens... similar to what the Journal backed generator does.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor does not enforce that the sequence starts at/above 0 so it could as equally have been Long.MIN_VALUE it started at, or any other value. Wrapping to 0 would seem to be making an assumption which doesnt necessarily fit with that constructor?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just meant to wrap like it used to be. it used to be MIN_VALUE I guess.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It didnt used to wrap at all, it threw which is why I was confused between this and the other comment (which I wonder if you meant to reply to here instead) that seemed they might conflict.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well ok...it wrapped the field, noticed, remembered it, then threw rather than return any value. Is that what you meant? Here it just noticed the value (tracing the current/previous id) hit MAX_VALUE and threw before the incrementing wrapped, so that it didnt need to remember anything.

public long generateID() {
return idSequence.getAndUpdate(id -> {
if (id == Long.MAX_VALUE) {
// Wrap - Very unlikely to happen

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep the previous semantics.

@gemmellr gemmellr Oct 7, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not clear which semantics you mean here, can you clarify? Especially as you have 2 comments from <1min apart overlapping 3 of the same line numbers of code context.

@tabish121

Copy link
Copy Markdown
Contributor

If making changes here it would make more sense to use AtomicLongFieldUpdater and a single volatile long value instead of creating a new AtomicLong for every instance of this type.

@jbertram

jbertram commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

...it would make more sense to use AtomicLongFieldUpdater and a single volatile long value...

I'm not sure there would ever be enough instances of SimpleIDGenerator on the broker to justify this.

@tabish121

Copy link
Copy Markdown
Contributor

...it would make more sense to use AtomicLongFieldUpdater and a single volatile long value...

I'm not sure there would ever be enough instances of SimpleIDGenerator on the broker to justify this.

I'm not sure why one needs to justify good coding practices...

@jbertram

jbertram commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

I'm not sure why one needs to justify good coding practices...

Perhaps I'm wrong about this, but my understanding is that the main use-case for any field updater is lowering memory use at scale. The main trade-offs being some clunky boilerplate code, the loss of compile-time type safety, and slightly higher per-call overhead. However, if the scale is too low to reap meaningful memory benefits then it seems reasonable to use the standard Atomic* version. I think the latter is true in this case.

@amarkevich amarkevich changed the title ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator; introduce SharedIDGenerator Oct 7, 2026
@gemmellr

gemmellr commented Oct 7, 2026

Copy link
Copy Markdown
Member

Perhaps I'm wrong about this, but my understanding is that the main use-case for any field updater is lowering memory use at scale. The main trade-offs being some clunky boilerplate code, the loss of compile-time type safety, and slightly higher per-call overhead. However, if the scale is too low to reap meaningful memory benefits then it seems reasonable to use the standard Atomic* version. I think the latter is true in this case.

There seem like enough connection and session specific uses of it that it might be worth it in this case? Having one would mean both use cases were covered, vs having both implementations as the PR now seems to?

/**
* {@link AtomicLongFieldUpdater} can only bind to a {@code volatile} <em>instance</em> field
* ({@code newUpdater} throws {@link IllegalArgumentException} for static fields), so the
* JVM-wide counter lives on this single static holder instance.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this generator now being made JVM-wide? It doesnt seem like it was originally in the place you used it, but rather per-session.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear why this was resolved...unresolving.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move logic completely to ActiveMQSessionContext to avoid generator instance creation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if you arent understanding my question or are just repeatedly ignoring it.

Why have you changed the semantics of the session-level consumer id, from being indexed per-session to being per-JVM? I see nothing here or the Jira about why that is being done.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got your point - reverted per-session counter

@amarkevich
amarkevich force-pushed the ARTEMIS-6289 branch 2 times, most recently from 1ed70f1 to 869ade2 Compare October 8, 2026 08:27
@amarkevich amarkevich changed the title ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator; introduce SharedIDGenerator ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator; AtomicLongFieldUpdater for ActiveMQSessionContext Oct 8, 2026
@amarkevich amarkevich changed the title ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator; AtomicLongFieldUpdater for ActiveMQSessionContext ARTEMIS-6289 Use AtomicLong instead of synchronized counter in SimpleIDGenerator Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants