Skip to content
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

Add flexibility in subject matching for nats_jetstream #1084

Merged

Conversation

evankanderson
Copy link
Contributor

I was attempting to migrate some existing code which uses longer topic names like ".." to NATS Jetstream. Unfortunately, the current code does not work for this stype of topic/subject, because:

  1. Stream names cannot contain ..
  2. The .* format matches a single .-delimited segment.

Added some logic to support both the .> multi-segment matcher, and using an empty string along with Consumer-side filtering.

@evankanderson evankanderson requested a review from a team as a code owner August 2, 2024 22:30
@evankanderson evankanderson force-pushed the jetstream-more-subscriptions branch 2 times, most recently from fe6e134 to c58b933 Compare August 2, 2024 23:28
@embano1
Copy link
Member

embano1 commented Aug 3, 2024

Is this a fundamental limitation in NATS or just with the JetStream version we're using? If the latter, @stephen-totty-hpe is working on updating the package to the latest NATS JS SDK.

@evankanderson
Copy link
Contributor Author

I'm not sure what you mean -- the NATS semantics are that foo.* matches foo.123 but not foo.clients.123, while foo.> matches both.

The stream naming limitations are part of core NATS: https://docs.nats.io/running-a-nats-service/nats_admin/jetstream_admin/naming

The subject matching rules are here: https://docs.nats.io/nats-concepts/subjects

@evankanderson
Copy link
Contributor Author

(our subject names look like internal.minder.subsystem.event in some cases. We're likely to change that naming at some point, but probably to something like internal.subsystem.type.<project-id>, so this patch would still be useful then.)

@embano1
Copy link
Member

embano1 commented Aug 4, 2024

The stream naming limitations are part of core NATS

That's what I wanted to understand, thx

Comment on lines 94 to 102
if !strings.HasPrefix(subject, stream) {
// Use an empty subject parameter in conjunction with
// nats.ConsumerFilterSubjects
subjectMatch = ""
} else if strings.Count(subject[len(stream):], ".") > 1 {
// More than one "." in the remainder of subject, use ".>" to match
subjectMatch = stream + ".>"
}
Copy link
Member

Choose a reason for hiding this comment

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

Question (minor): is there a case where both conditions could be true? If not, this code could be simplified by removing the else and just having two if statements to increase readability without if/else indentation?

Copy link
Member

Choose a reason for hiding this comment

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

Also, once #1083 lands (depending on the implementation by @stephen-totty-hpe), we need to make sure to also apply this logic to the new JetStream implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code could also be written as:

if strings.HasPrefix(subject, stream) {
  if strings.Count(strings.TrimPrefix(subject, stream), ".") > 1 {
    // Change .* to .>
  }
} else {
  // Change to ""
}

I think if stream is foo and "subject" is bar.baz.12345, then the reformulated code would hit the second condition.

We could rewrite this as:

if strings.HasPrefix(subject, stream) && strings.Count(strings.TrimPrefix(subject, stream), ".") > 1 {
  // Change .* to .>
}
if !strings.HasPrefix(subject, stream) {
  subjectMatch = ""
}

But, we need the `HasPrefix` check on the `strings.Count`.  I could switch to using `TrimPrefix` (or some other tricks, like getting the prefix-trimmed string, and comparing its length to the original to determine `HasPrefix`), but none of the above seemed better (in particular, `TrimPrefix` in the non-prefixed case returns the original string, which seemed wrong.

Thinking further, I suppose I could remove the `HasPrefix` from the first clause, as the second will completely overwrite if `HasPrefix` is false.

@evankanderson
Copy link
Contributor Author

Note - if we didn't have the current implementation that someone may already be relying on, I'd have simply changed the .* to a .>`, but I didn't want to break any existing users.

@embano1
Copy link
Member

embano1 commented Aug 4, 2024

Nice and clean! Thx!

Yup, keeping existing by default is something I would have called out anyways 😀

Wanna squash the commits bc we don't have a fancy bot doing this for us ,)

Signed-off-by: Evan Anderson <evan@stacklok.com>
@evankanderson
Copy link
Contributor Author

Squashed -- it wasn't clear if you were using the "squash merge" option here.

@embano1
Copy link
Member

embano1 commented Aug 5, 2024

Thx, we don't because Github doesn't support proper commit messages when using squash merge. IIRC, it should be doc-ed in our CONTRIBUTING guide. But all good :)

@embano1 embano1 enabled auto-merge August 5, 2024 05:16
Copy link
Member

@embano1 embano1 left a comment

Choose a reason for hiding this comment

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

Thank you!

@embano1 embano1 merged commit 1aecb20 into cloudevents:main Aug 5, 2024
9 checks passed
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