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

Implement Consumers Pause #432

Merged
merged 5 commits into from Mar 11, 2024

Conversation

MauriceVanVeen
Copy link
Contributor

@MauriceVanVeen MauriceVanVeen commented Mar 6, 2024

Resolves #416

Adds support for pausing and resuming consumers.

  • The NatsJSContext.Consumers now has methods to PauseConsumerAsync(stream, consumer, pauseUntil), as well as ResumeConsumerAsync(stream, consumer).
  • ConsumerConfiguration now has support for setting PauseUntil as well. Allowing to set it during consumer creation.

The implementation is similar to and based on the changes I made for the Java client: nats-io/nats.java#1093

Am not too familiar with .NET, and this is the first time contributing to this client. Please let me know if I'm missing any things specific to .NET or this client, that can be improved further.

Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
@@ -335,3 +337,30 @@ public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, Jso
public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) =>
writer.WriteNumberValue((long)(value.TotalMilliseconds * 1_000_000L));
}

internal class NatsJSJsonNullableNanosecondsConverter : JsonConverter<TimeSpan?>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have created this to support a nullable TimeSpan? when used for the PauseRemaining in the ConsumerInfo.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, added a test for PauseRemaining.

Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
@MauriceVanVeen MauriceVanVeen marked this pull request as ready for review March 6, 2024 20:44
Copy link

@oising oising left a comment

Choose a reason for hiding this comment

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

Some suggestions around immutability and return types implementation.

{
[System.Text.Json.Serialization.JsonPropertyName("paused")]
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Never)]
public bool Paused { get; set; }
Copy link

Choose a reason for hiding this comment

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

As this is a server response, I'd imagine this should be exposed as an immutable type ( see https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/immutability ) -- in short, the properties should be { get; init; } to allow the JSON serializer to function correctly while preventing the caller from changing the properties (or more to the point, believing that changing them may have some kind of effect.) With this mind, the Paused property should be named IsPaused to align better with convention

Copy link

Choose a reason for hiding this comment

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

(The same should apply for other immutable return values here)

Copy link
Collaborator

Choose a reason for hiding this comment

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

should we make this change in others? (not in this PR of course)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have renamed to IsPaused, as well as using the { get; init; } in the ConsumerPauseResponse.

Think it makes sense to have other server responses (as well as ConsumerInfo for example) use the same { get; init; }.
But indeed like @mtmk says, would agree that would be good for another PR 🙂

Copy link
Collaborator

@mtmk mtmk Mar 7, 2024

Choose a reason for hiding this comment

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

@oising, it looks like init isn't supported in generated serializers in net6:

System.InvalidOperationException : Deserialization of init-only properties is currently not supported in source generation mode.

we either ifdef or wait until net6 eol

Copy link

@oising oising Mar 7, 2024

Choose a reason for hiding this comment

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

I'm curious about something -- following the thread here ( dotnet/runtime#58770 ), that leads to the net7 PR here ( dotnet/runtime#68064 ) -- that seems to say that switching to a positional syntax for the record, i.e. public record Foo (bool Foo, string Bar); also emits init only properties, but the source generated code for a net6 target will work fine (because it generates a call to a constructor and doesn't try to manipulate the setters directly), as long as you compile with an SDK greater than 6 (i.e. sdk 7 or 8 .) It seems that full support for init/required properties arrived in dotnet/runtime#79828 and this only works for net8 targets. So, switching to position syntax for the record may work for net6 targets, if we compile with SDK8

(edit: I'll test this hypothesis and post back here)

Copy link

Choose a reason for hiding this comment

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

Ah, I remember now that "for reasons" you guys cannot down-target net6 using the net8 SDK because of how it pulls in dependencies and they will clash with other things. It actually works fine for net6 targeting with SDK8, using both syntaxes (init-only or positional), and works only with positional with SDK7. For SDK6, it's broken as you state.

Copy link

Choose a reason for hiding this comment

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

I tested with the SDK8 before I remembered the limitation:

// See https://aka.ms/new-console-template for more information

// Define the record type Foo

using System.Text.Json;
using System.Text.Json.Serialization;

// Create an instance of Foo
FooPos foo1 = new FooPos(true, "Test");

FooProp foo2 = new FooProp()
{
    BoolValue = true,
    StringValue = "Test"
};

// Create an instance of the context
var context = new MyJsonContext();

// Serialize the Foo instance to JSON using the context
string json = JsonSerializer.Serialize(foo1, context.FooPos);

Console.WriteLine($"Serialized FooPos: {json}");

var foo3 = JsonSerializer.Deserialize<FooPos>(json, context.FooPos);

Console.WriteLine($"Deserialized FooPos: {foo3}");


json = JsonSerializer.Serialize(foo2, context.FooProp);

Console.WriteLine($"Serialized FooProp: {json}");

var foo4 = JsonSerializer.Deserialize<FooProp>(json, context.FooProp);

Console.WriteLine($"Deserialized FooProp: {foo4}");
public record FooPos(bool BoolValue, string StringValue);

public record FooProp
{
    public bool BoolValue { get; init; }
    public string? StringValue { get; init; }
}

// Define the context class for JSON serialization
[JsonSourceGenerationOptions(
    WriteIndented = true,
    GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(FooPos))]
[JsonSerializable(typeof(FooProp))]
public partial class MyJsonContext : JsonSerializerContext
{
}

Works fine under SDK8 :/

image

Copy link

Choose a reason for hiding this comment

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

So I guess just either ifdef, or defer the work until later - but it would be nice to start on the True Path now ;)

Copy link
Collaborator

Choose a reason for hiding this comment

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

nice work 💯 🚀 ifdef'ed it ;-) We should be good. Created an issue so we don't forget #435

Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Copy link
Collaborator

@mtmk mtmk left a comment

Choose a reason for hiding this comment

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

LGTM thanks @MauriceVanVeen 💯 🥇

@mtmk mtmk added this to the 2.1.3 milestone Mar 7, 2024
Copy link

@oising oising left a comment

Choose a reason for hiding this comment

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

LGTM

mtmk added a commit that referenced this pull request Mar 11, 2024
@mtmk mtmk mentioned this pull request Mar 11, 2024
@mtmk mtmk merged commit 1c11db3 into nats-io:main Mar 11, 2024
10 checks passed
mtmk added a commit that referenced this pull request Mar 12, 2024
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.

Implement Consumer Pause
3 participants