feat: wire consumer pause/resume API endpoint (Gap 7.5)
Enhances HandlePause to parse pause_until (RFC3339) for time-bounded pauses and returns current pause state in the response body. Adds Paused/PauseUntil properties and PauseResponse factory to JetStreamApiResponse. Covers 10 new parity tests.
This commit is contained in:
@@ -84,10 +84,23 @@ public static class ConsumerApiHandlers
|
||||
return JetStreamApiResponse.NotFound(subject);
|
||||
|
||||
var (stream, durableName) = parsed.Value;
|
||||
var paused = ParsePause(payload);
|
||||
return consumerManager.Pause(stream, durableName, paused)
|
||||
? JetStreamApiResponse.SuccessResponse()
|
||||
: JetStreamApiResponse.NotFound(subject);
|
||||
|
||||
var (paused, pauseUntil) = ParsePauseRequest(payload);
|
||||
|
||||
bool success;
|
||||
if (pauseUntil.HasValue)
|
||||
success = consumerManager.Pause(stream, durableName, pauseUntil.Value);
|
||||
else if (paused)
|
||||
success = consumerManager.Pause(stream, durableName, true);
|
||||
else
|
||||
success = consumerManager.Resume(stream, durableName);
|
||||
|
||||
if (!success)
|
||||
return JetStreamApiResponse.NotFound(subject);
|
||||
|
||||
return JetStreamApiResponse.PauseResponse(
|
||||
consumerManager.IsPaused(stream, durableName),
|
||||
consumerManager.GetPauseUntil(stream, durableName));
|
||||
}
|
||||
|
||||
public static JetStreamApiResponse HandleReset(string subject, ConsumerManager consumerManager)
|
||||
@@ -359,22 +372,44 @@ public static class ConsumerApiHandlers
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static bool ParsePause(ReadOnlySpan<byte> payload)
|
||||
/// <summary>
|
||||
/// Parses pause request payload supporting both a boolean <c>pause</c> field and an RFC3339
|
||||
/// <c>pause_until</c> deadline. When <c>pause_until</c> is present it implies <c>pause=true</c>.
|
||||
/// Go reference: server/consumer.go jsConsumerPauseRequest.
|
||||
/// </summary>
|
||||
private static (bool Paused, DateTime? PauseUntil) ParsePauseRequest(ReadOnlySpan<byte> payload)
|
||||
{
|
||||
if (payload.IsEmpty)
|
||||
return false;
|
||||
return (false, null);
|
||||
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(payload.ToArray());
|
||||
if (doc.RootElement.TryGetProperty("pause", out var pauseEl))
|
||||
return pauseEl.ValueKind == JsonValueKind.True;
|
||||
var root = doc.RootElement;
|
||||
|
||||
DateTime? pauseUntil = null;
|
||||
if (root.TryGetProperty("pause_until", out var untilEl)
|
||||
&& untilEl.ValueKind == JsonValueKind.String
|
||||
&& DateTime.TryParse(untilEl.GetString(), null, System.Globalization.DateTimeStyles.RoundtripKind, out var dt))
|
||||
{
|
||||
pauseUntil = dt.ToUniversalTime();
|
||||
}
|
||||
|
||||
bool paused = false;
|
||||
if (root.TryGetProperty("pause", out var pauseEl))
|
||||
paused = pauseEl.ValueKind == JsonValueKind.True;
|
||||
|
||||
// pause_until implies pause=true (Go reference: consumer.go pauseConsumer).
|
||||
if (pauseUntil.HasValue)
|
||||
paused = true;
|
||||
|
||||
return (paused, pauseUntil);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
}
|
||||
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
private static string? ParseStreamSubject(string subject, string prefix)
|
||||
|
||||
@@ -17,6 +17,18 @@ public sealed class JetStreamApiResponse
|
||||
public bool Success { get; init; }
|
||||
public ulong Purged { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the consumer is currently paused. Populated by pause/resume API responses.
|
||||
/// Go reference: server/consumer.go jsConsumerPauseResponse.paused field.
|
||||
/// </summary>
|
||||
public bool? Paused { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// UTC deadline until which the consumer is paused. Null when no deadline is set.
|
||||
/// Go reference: server/consumer.go jsConsumerPauseResponse.pause_until field.
|
||||
/// </summary>
|
||||
public DateTime? PauseUntil { get; init; }
|
||||
|
||||
public static JetStreamApiResponse NotFound(string subject) => new()
|
||||
{
|
||||
Error = new JetStreamApiError
|
||||
@@ -66,6 +78,17 @@ public sealed class JetStreamApiResponse
|
||||
Success = true,
|
||||
Purged = purged,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a pause/resume success response with current pause state.
|
||||
/// Go reference: server/consumer.go jsConsumerPauseResponse — returned after pause/resume API call.
|
||||
/// </summary>
|
||||
public static JetStreamApiResponse PauseResponse(bool paused, DateTime? pauseUntil) => new()
|
||||
{
|
||||
Success = true,
|
||||
Paused = paused,
|
||||
PauseUntil = pauseUntil,
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class JetStreamStreamInfo
|
||||
@@ -102,6 +125,15 @@ public sealed class JetStreamDirectMessage
|
||||
public sealed class JetStreamSnapshot
|
||||
{
|
||||
public string Payload { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>Stream name this snapshot was taken from.</summary>
|
||||
public string? StreamName { get; init; }
|
||||
|
||||
/// <summary>Number of chunks the snapshot was split into (1 for non-chunked snapshots).</summary>
|
||||
public int NumChunks { get; init; }
|
||||
|
||||
/// <summary>Block/chunk size in bytes.</summary>
|
||||
public int BlkSize { get; init; }
|
||||
}
|
||||
|
||||
public sealed class JetStreamPullBatch
|
||||
|
||||
Reference in New Issue
Block a user