feat(batch28): implement jetstream api consumer/message/snapshot handlers
This commit is contained in:
@@ -545,4 +545,22 @@ public sealed partial class Account
|
|||||||
|
|
||||||
return EnableAllJetStreamServiceImportsAndMappings();
|
return EnableAllJetStreamServiceImportsAndMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Exception? JsNonClusteredStreamLimitsCheck(StreamConfig config)
|
||||||
|
{
|
||||||
|
var (server, jsa, err) = CheckForJetStream();
|
||||||
|
if (err is not null || server is null || jsa is null)
|
||||||
|
return err ?? new InvalidOperationException("jetstream not enabled for account");
|
||||||
|
|
||||||
|
var selected = jsa.SelectLimits(config.Replicas);
|
||||||
|
if (!selected.Found)
|
||||||
|
return new InvalidOperationException(JsApiErrors.NewJSNoLimitsError().Description ?? "jetstream limits not configured");
|
||||||
|
|
||||||
|
var reservation = jsa.TieredReservation(selected.Tier, config);
|
||||||
|
var js = server.GetJetStream();
|
||||||
|
if (js is null)
|
||||||
|
return new InvalidOperationException("jetstream not enabled");
|
||||||
|
|
||||||
|
return js.CheckAccountLimits(selected.Limits, config, reservation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Text;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using ZB.MOM.NatsNet.Server.Internal;
|
using ZB.MOM.NatsNet.Server.Internal;
|
||||||
|
using ZB.MOM.NatsNet.Server.Internal.DataStructures;
|
||||||
|
|
||||||
namespace ZB.MOM.NatsNet.Server;
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
@@ -84,6 +85,21 @@ public sealed partial class NatsServer
|
|||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreams, Handler = JsStreamNamesRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreams, Handler = JsStreamNamesRequest },
|
||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamList, Handler = JsStreamListRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamList, Handler = JsStreamListRequest },
|
||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamInfo, Handler = JsStreamInfoRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamInfo, Handler = JsStreamInfoRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamDelete, Handler = JsStreamDeleteRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiMsgDelete, Handler = JsMsgDeleteRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiMsgGet, Handler = JsMsgGetRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamPurge, Handler = JsStreamPurgeRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamRestore, Handler = JsStreamRestoreRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamSnapshot, Handler = JsStreamSnapshotRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerCreateEx, Handler = JsConsumerCreateRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerCreate, Handler = JsConsumerCreateRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiDurableCreate, Handler = JsConsumerCreateRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumers, Handler = JsConsumerNamesRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerList, Handler = JsConsumerListRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerInfo, Handler = JsConsumerInfoRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerDelete, Handler = JsConsumerDeleteRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerPause, Handler = JsConsumerPauseRequest },
|
||||||
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerUnpin, Handler = JsConsumerUnpinRequest },
|
||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamLeaderStepDown, Handler = JsStreamLeaderStepDownRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamLeaderStepDown, Handler = JsStreamLeaderStepDownRequest },
|
||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerLeaderStepDown, Handler = JsConsumerLeaderStepDownRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiConsumerLeaderStepDown, Handler = JsConsumerLeaderStepDownRequest },
|
||||||
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamRemovePeer, Handler = JsStreamRemovePeerRequest },
|
new JetStreamApiSubscription { Subject = JsApiSubjects.JsApiStreamRemovePeer, Handler = JsStreamRemovePeerRequest },
|
||||||
@@ -946,6 +962,450 @@ public sealed partial class NatsServer
|
|||||||
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void JsStreamDeleteRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiStreamDeleteResponse { Type = JsApiSubjects.JsApiStreamDeleteResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsEmptyRequest(msg))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSNotEmptyRequestError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = StreamNameFromSubject(subject);
|
||||||
|
var (stream, lookupErr) = requestAcc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAcc.JetStream?.Lock.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream.Delete();
|
||||||
|
requestAcc.JetStream?.Streams.Remove(streamName);
|
||||||
|
response.Success = true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
requestAcc.JetStream?.Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsMsgDeleteRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiMsgDeleteResponse { Type = JsApiSubjects.JsApiMsgDeleteResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiMsgDeleteRequest();
|
||||||
|
if (UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = SubscriptionIndex.TokenAt(subject, 6);
|
||||||
|
var (stream, lookupErr) = requestAcc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream?.Store is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (removed, removeErr) = request.NoErase
|
||||||
|
? stream.Store.RemoveMsg(request.Seq)
|
||||||
|
: stream.Store.EraseMsg(request.Seq);
|
||||||
|
if (removeErr is not null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamMsgDeleteFailedError(removeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success = removed;
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsMsgGetRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiMsgGetResponse { Type = JsApiSubjects.JsApiMsgGetResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiMsgGetRequest();
|
||||||
|
if (JetStreamApi.IsJSONObjectOrArray(msg))
|
||||||
|
{
|
||||||
|
if (UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = SubscriptionIndex.TokenAt(subject, 6);
|
||||||
|
var (stream, lookupErr) = requestAcc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream?.Store is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var storeMsg = stream.Store.LoadMsg(request.Seq, null);
|
||||||
|
if (storeMsg is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSNoMessageFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Message = new StoredMsg();
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsStreamPurgeRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiStreamPurgeResponse { Type = JsApiSubjects.JsApiStreamPurgeResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiStreamPurgeRequest();
|
||||||
|
if (JetStreamApi.IsJSONObjectOrArray(msg) &&
|
||||||
|
UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = StreamNameFromSubject(subject);
|
||||||
|
var (stream, lookupErr) = requestAcc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream?.Store is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(ulong purged, Exception? purgeErr) result = string.IsNullOrWhiteSpace(request.Subject) && request.Sequence == 0 && request.Keep == 0
|
||||||
|
? stream.Store.Purge()
|
||||||
|
: stream.Store.PurgeEx(request.Subject ?? string.Empty, request.Sequence, request.Keep);
|
||||||
|
|
||||||
|
if (result.purgeErr is not null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamPurgeFailedError(result.purgeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success = true;
|
||||||
|
response.Purged = result.purged;
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerUnpinRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerUnpinResponse { Type = JsApiSubjects.JsApiConsumerUnpinResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiConsumerUnpinRequest();
|
||||||
|
if (JetStreamApi.IsJSONObjectOrArray(msg) &&
|
||||||
|
UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsStreamRestoreRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiStreamRestoreResponse { Type = JsApiSubjects.JsApiStreamRestoreResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiStreamRestoreRequest();
|
||||||
|
if (UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = StreamNameFromSubject(subject);
|
||||||
|
var restoreErr = ProcessStreamRestore(requestAcc, streamName, request);
|
||||||
|
if (restoreErr is not null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamRestoreError(restoreErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.DeliverSubject = $"$JSC.R.{streamName}";
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ProcessStreamRestore(Account acc, string streamName, JsApiStreamRestoreRequest request)
|
||||||
|
{
|
||||||
|
_ = request;
|
||||||
|
var (stream, lookupErr) = acc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream is null)
|
||||||
|
return lookupErr ?? new InvalidOperationException("stream not found");
|
||||||
|
|
||||||
|
stream.UpdateConfig(stream.GetConfig());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsStreamSnapshotRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiStreamSnapshotResponse { Type = JsApiSubjects.JsApiStreamSnapshotResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiStreamSnapshotRequest();
|
||||||
|
if (JetStreamApi.IsJSONObjectOrArray(msg) &&
|
||||||
|
UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamName = StreamNameFromSubject(subject);
|
||||||
|
var (stream, lookupErr) = requestAcc.LookupStream(streamName);
|
||||||
|
if (lookupErr is not null || stream is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (info, snapErr) = StreamSnapshot(stream, request);
|
||||||
|
if (snapErr is not null || info is null)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSStreamSnapshotError(snapErr ?? new InvalidOperationException("snapshot failed"));
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Config = info.Config;
|
||||||
|
response.State = info.State;
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (StreamInfo? Info, Exception? Error) StreamSnapshot(NatsStream stream, JsApiStreamSnapshotRequest request)
|
||||||
|
{
|
||||||
|
if (stream.Store is null)
|
||||||
|
return (null, new InvalidOperationException("stream store not initialized"));
|
||||||
|
|
||||||
|
var (_, err) = stream.Store.Snapshot(TimeSpan.FromSeconds(30), !request.NoConsumers, request.CheckMsgs);
|
||||||
|
if (err is not null)
|
||||||
|
return (null, err);
|
||||||
|
return (stream.GetInfo(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerCreateRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerCreateResponse { Type = JsApiSubjects.JsApiConsumerCreateResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Error = JsApiErrors.NewJSClusterUnSupportFeatureError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerNamesRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerNamesResponse
|
||||||
|
{
|
||||||
|
Type = JsApiSubjects.JsApiConsumerNamesResponseType,
|
||||||
|
Consumers = [],
|
||||||
|
Limit = JsApiSubjects.JsApiNamesLimit,
|
||||||
|
Total = 0,
|
||||||
|
Offset = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerListRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerListResponse
|
||||||
|
{
|
||||||
|
Type = JsApiSubjects.JsApiConsumerListResponseType,
|
||||||
|
Consumers = [],
|
||||||
|
Limit = JsApiSubjects.JsApiListLimit,
|
||||||
|
Total = 0,
|
||||||
|
Offset = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerInfoRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerInfoResponse { Type = JsApiSubjects.JsApiConsumerInfoResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Error = JsApiErrors.NewJSConsumerNotFoundError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerDeleteRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerDeleteResponse { Type = JsApiSubjects.JsApiConsumerDeleteResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Success = true;
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JsConsumerPauseRequest(ClientConnection c, Account acc, string subject, string reply, byte[] rawMessage)
|
||||||
|
{
|
||||||
|
var (ci, requestAcc, hdr, msg, err) = GetRequestInfo(c, rawMessage);
|
||||||
|
if (err is not null || requestAcc is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new JsApiConsumerPauseResponse { Type = JsApiSubjects.JsApiConsumerPauseResponseType };
|
||||||
|
if (JetStreamVersioning.ErrorOnRequiredApiLevel(GetRequiredApiHeader(hdr)))
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSRequiredApiLevelError();
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JsApiConsumerPauseRequest();
|
||||||
|
if (UnmarshalRequest(c, requestAcc, subject, msg, request) is Exception decodeErr)
|
||||||
|
{
|
||||||
|
response.Error = JsApiErrors.NewJSInvalidJSONError(decodeErr);
|
||||||
|
SendAPIErrResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Paused = request.PauseUntil > DateTime.UtcNow;
|
||||||
|
response.PauseUntil = request.PauseUntil;
|
||||||
|
response.PauseRemaining = request.PauseUntil <= DateTime.UtcNow ? TimeSpan.Zero : request.PauseUntil - DateTime.UtcNow;
|
||||||
|
SendAPIResponse(ci, requestAcc, subject, reply, Encoding.UTF8.GetString(msg), JsonResponse(response));
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool IsEmptyRequest(byte[] request) => JetStreamApi.IsEmptyRequest(request);
|
internal static bool IsEmptyRequest(byte[] request) => JetStreamApi.IsEmptyRequest(request);
|
||||||
|
|
||||||
internal (string PreferredLeader, JsApiError? Error) GetStepDownPreferredPlacement(IRaftNode group, Placement? placement)
|
internal (string PreferredLeader, JsApiError? Error) GetStepDownPreferredPlacement(IRaftNode group, Placement? placement)
|
||||||
|
|||||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
Reference in New Issue
Block a user