Skip to content

Conversation

@dagnir
Copy link
Contributor

@dagnir dagnir commented Jan 6, 2026

Motivation and Context

Previously, signalig onNext() to the subscriber was done via recursion, pulling elements from an iterator over the current page returned by the service. However, this can quickly lead to a stackoverflow error since the stack will grow linearly with the size of the page.

  • Replace sendNextElement recursion with a loop
  • Ensure that handleRequests does not recurse into itself

Modifications

Testing

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added a changelog entry. Adding a new entry must be accomplished by running the scripts/new-change script and following the instructions. Commit the new file created by the script in .changes/next-release with your changes.
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

@dagnir dagnir force-pushed the dongie/gh-6411 branch 3 times, most recently from 4125690 to 09a3a0c Compare January 6, 2026 22:36
@dagnir dagnir marked this pull request as ready for review January 6, 2026 22:37
@dagnir dagnir requested a review from a team as a code owner January 6, 2026 22:37
@dagnir dagnir force-pushed the dongie/gh-6411 branch 2 times, most recently from 124360b to 35c79e4 Compare January 6, 2026 22:38
Previously, signalig onNext() to the subscriber was done via recursion,
pulling elements from an iterator over the current page returned by the
service. However, this can quickly lead to a stackoverflow error since
the stack will grow linearly with the size of the page.

 - Replace sendNextElement recursion with a loop
 - Ensure that handleRequests does not recurse into itself

 fixes #6411
public final class ItemsSubscription<ResponseT, ItemT> extends PaginationSubscription<ResponseT> {
private final Function<ResponseT, Iterator<ItemT>> getIteratorFunction;
private volatile Iterator<ItemT> singlePageItemsIterator;
private final AtomicBoolean handlingRequests = new AtomicBoolean();
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume we're effectively using this as a "try-lock" (ie, return quickly if another thread is already in this method)?

Copy link
Contributor Author

@dagnir dagnir Jan 6, 2026

Choose a reason for hiding this comment

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

Yep basically

edit: not just another thread, but also prevent handleRequests from reentering, which could happen for example if the subscriber does something like

void onNext(String item) {
    subscription.request(1);
}

private final Function<ResponseT, Iterator<ItemT>> getIteratorFunction;
private volatile Iterator<ItemT> singlePageItemsIterator;
private final AtomicBoolean handlingRequests = new AtomicBoolean();
private volatile boolean awaitingNewPage = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we using an AtomicBoolean for handlingRequests but a volatile for awaitingNewPage? I'm trying to work through different cases.... when handleRequests sets awaitingNewPage to true (line 78), we create a next page future and then the whenComplete on that future which may run in a different thread and so the awaitingNewPage getting set to false may only apply in that thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only reason awaitingNewPage is volatile is I didn't need to use a CAS for it

so the awaitingNewPage getting set to false may only apply in that thread?

Hmm I might not completely understand the question here, but it's volatile so reads on awaitingNewPage will never be stale. e.g. thread T1 setting awaitingNewPage = true (atomic), and T2 reading it after the write will always return true (never false).

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 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.

2 participants