Skip to content

Conversation

@onobc
Copy link
Contributor

@onobc onobc commented Dec 2, 2025

This change introduces the Support XDELEX, XACKEX and extend XADD and XTRIM options feature...

See original pull request for more details.

Closes: #3232
Original Pull Request: #3247

See #3232

Signed-off-by: viktoriya.kutsarova <viktoriya.kutsarova@redis.com>
Closes #3232

Signed-off-by: viktoriya.kutsarova <viktoriya.kutsarova@redis.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 2, 2025
@onobc onobc changed the title WIP: Issue/3232 [upstream] Add support for XDELEX and XACKDEL, and expand options for XADD and XTRIM Dec 2, 2025
Copy link
Contributor Author

@onobc onobc left a comment

Choose a reason for hiding this comment

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

Note

Thank you @viktoriya-kutsarova for the excellent deprecated detailed descriptions tells users what replacement to use w/o them having to do software archeology to figure it out. :)

Once Jenkins and PR is green and we resolve the rename concern (#3272 (comment)) this will be ready to merge.

Also note that I will do some final massaging of the commits (couple of squashes and be sure they proper links are in the commits etc..).

Thanks

if (options.hasTrimOptions()) {
TrimOptions trim = options.getTrimOptions();
var strategy = trim.getTrimStrategy();
TrimStrategy strategy = trim.getTrimStrategy();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm an advocate for var and I may bad-influenced you by using var in my previous commit or in some pseudo code when we were discussing things. If so, my bad. In general you will not find it in the main source of many Spring projects (sometimes in the tests).

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure, let's keep using the proper type.

private final TrimStrategy trimStrategy;
private final TrimOperator trimOperator;
private final @Nullable Long limit;
private final @Nullable StreamDeletionPolicy pendingReferences;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do have a small concern about possible confusion caused from tbedeletionPolicy/getPendingReferences rename. I believe now that the benefit of a better high-level api name pendingRefereneces does not outweigh the inconsistency between the two names throughout. I am leaning towards consistency here and just leave it named deletionPolicy for now. I would like to get yours MP input on this topic.

Copy link
Member

Choose a reason for hiding this comment

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

I suspect that I came up with the name of pendingReferences and StreamDeletionPolicy (not really sure about whether it was me or someone else). In any case, both terms describe concepts. Redis command documentation says:

Specifies how to handle consumer group references when trimming. If there are no consumer groups, these arguments have no effect. Available since Redis 8.2.
When trimming, removes entries … preserves existing references to these entries in all consumer groups' PEL (Pending Entries List).

Redis sometimes isn't great at making some concepts explicit, instead the underlying concept leaks through documentation. I think StreamDeletionPolicy is the shortest possible name to express a deletion policy for stream entry references from the pending element list. Agreed that the combination is not super-self explanatory, however it is something that when I encounter, I would want to read up more what this is. PendingElementListDeletionPolicy could be an alternative, yet its wordiness is much more annoying than the state we are currently at.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like I introduced some inconsistency with the rename, so I want to make sure I understand: are we discussing the naming of the class (StreamDeletionPolicy), the field (currently pendingReferences), or both?

Personally, I would stick with streamDeletionPolicy for both. Based on the Redis documentation, all three options (KEEPREF | DELREF | ACKED) represent different policies for how entries are removed from the stream. KEEPREF and DELREF delete entries strictly according to the trimming strategy (differing only in how PEL references are handled), while ACKED deletes only entries that were read and acknowledged by all consumer groups. In that sense, streamDeletionPolicy feels like the most concise name that still reflects the underlying concept.

As you noted, consistency across the API is important, so aligning on a single name and using it consistently seems like the best approach.

onobc and others added 5 commits December 1, 2025 22:05
Signed-off-by: Chris Bono <chris.bono@broadcom.com>
See #3232

Signed-off-by: Chris Bono <chris.bono@broadcom.com>
…options

See #3232

Signed-off-by: Chris Bono <chris.bono@broadcom.com>
See #3232

Signed-off-by: viktoriya.kutsarova <viktoriya.kutsarova@redis.com>
This commit makes the following "polish" changes:

- remove manual not null check in `TrimOptions.<init>` as it's already `@NullMarked`
- annnotate `RedisStreamCommands.XDelOptions` with `@NullMarked`
- minor tweak to javadoc
- remove usage of `var` in main code
- use since `4.1` instead of `4.0`

See #3232

Signed-off-by: Chris Bono <chris.bono@broadcom.com>
@onobc
Copy link
Contributor Author

onobc commented Dec 2, 2025

cc: @viktoriya-kutsarova @mp911de

Copy link
Member

@mp911de mp911de left a comment

Choose a reason for hiding this comment

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

I've left a few comments. There are several public API elements (methods, classes), that are missing Javadoc along with a mix of im proper or missing @since tags. I strongly urge to refrain from Deprecated(forRemoval=true) as the current API isn't breaking anything nor imposes its usage any danger, we've just broadened feature support.

Test code is overly verbose in its current form as each test scenratio is unfolded in a separate method rather than utilizing parametrized tests. In some areas we follow a test approach that doesn't consider refactoring of the underlying code inside components that we're testing. These require cleanup along with proper formatting according to our code style.

assertThat(params).extracting("expiration", "expirationValue").containsExactly(expirationType, expirationValue);
}
}

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Formatting

RecordId recordId = RecordId.of("1234567890-0");
XAddOptions options = XAddOptions.none();

XAddParams params = StreamConverters.toXAddParams(recordId, options);
Copy link
Member

Choose a reason for hiding this comment

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

A testing approach of testing that a component is not doing something quickly leads to a lot of complexity as the subsequent lines unfold into several assertions. Instead, I suggest testing for the expectation that the component is doing something we expect, namely, not adding any command arguments.

XAddParams.addParams(CommandArguments) would be a neat way to utilize public API for testing which command arguments are added.

}

@Nested // GH-3232
class ToStreamDeletionPolicyShould {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of having each invariant represented by a test method, can we turn this into parametrized tests if you want to cover each code path in tests? The current arrangement is pretty verbose requiring a lot of mental energy to parse those tests. Using a parametrized test condenses the setup into an assertion and a set of input/expected arguments.

}

@Test // GH-3232
void xaddShouldHonorMinId() {
Copy link
Member

Choose a reason for hiding this comment

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

Testing individual arguments in the xadd command series deviates from the underlying code.

Clearly, previously, the conversion has happened in the xadd method itself. With this pull request, all command conversion has been refactored to a converter, so it would be sufficient to keep a single xadd test with options and remove all other test variants to align with the code under test.

RecordId messageId = streamOps.add(key, Collections.singletonMap(hashKey, value));

streamOps.createGroup(key, ReadOffset.from("0-0"), "my-group");

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Formatting

private final TrimStrategy trimStrategy;
private final TrimOperator trimOperator;
private final @Nullable Long limit;
private final @Nullable StreamDeletionPolicy pendingReferences;
Copy link
Member

Choose a reason for hiding this comment

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

I suspect that I came up with the name of pendingReferences and StreamDeletionPolicy (not really sure about whether it was me or someone else). In any case, both terms describe concepts. Redis command documentation says:

Specifies how to handle consumer group references when trimming. If there are no consumer groups, these arguments have no effect. Available since Redis 8.2.
When trimming, removes entries … preserves existing references to these entries in all consumer groups' PEL (Pending Entries List).

Redis sometimes isn't great at making some concepts explicit, instead the underlying concept leaks through documentation. I think StreamDeletionPolicy is the shortest possible name to express a deletion policy for stream entry references from the pending element list. Agreed that the combination is not super-self explanatory, however it is something that when I encounter, I would want to read up more what this is. PendingElementListDeletionPolicy could be an alternative, yet its wordiness is much more annoying than the state we are currently at.

* Prefer {@code XAddOptions.trim(TrimOptions.maxLen(n).approximate())} or
* {@code XAddOptions.trim(TrimOptions.minId(id).exact())}.
*/
@Deprecated(since = "4.0", forRemoval = false)
Copy link
Member

Choose a reason for hiding this comment

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

I suggest avoiding forRemoval = true. It's a pretty harsh signal. Just the deprecation is a lighter form of signalling improper API usage, and that there is a better variant.

public XAddOptions nomkstream(boolean nomkstream) {
return new XAddOptions(nomkstream, trimOptions);
}

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Formatting

* The entry ID does not exist in the stream.
*/
NOT_FOUND(-1L),
/**
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Formatting (missing blank line)

* @since 4.1
*/
enum StreamDeletionPolicy {
/**
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Formatting (missing blank lines)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support XDELEX, XACKEX and extend XADD and XTRIM options from Redis 8.2

5 participants