Redis sse namespace entries won't be deleted if the ServerEventClient crashed

We have the problem that the Redis sse namespace entries won’t be deleted if the ServerEventClient crashed?

ServiceStack version 5.0.2

Redis key sse:id:Fq2AaTUvWdRWYpQ3yqoH:

    {
  "createdAt": "2018-06-05T16:36:33.9219863Z",
  "channels": [
    "Fq2AaTUvWdRWYpQ3yqoH"
  ],
  "userId": "-3",
  "displayName": "user3",
  "sessionId": "onUdxMICnW65o3BV83Ac",
  "subscriptionId": "Fq2AaTUvWdRWYpQ3yqoH",
  "userAddress": "************",
  "isAuthenticated": false,
  "meta": {
    "userId": "-3",
    "isAuthenticated": "false",
    "displayName": "user3",
    "channels": "Fq2AaTUvWdRWYpQ3yqoH",
    "createdAt": "1528216593921",
    "profileUrl": "https://raw.githubusercontent.com/ServiceStack/Assets/master/img/apps/no-profile64.png"
  },
  "connectArgs": {
    "userId": "-3",
    "isAuthenticated": "false",
    "displayName": "user3",
    "channels": "*",
    "createdAt": "1528216593921",
    "profileUrl": "https://raw.githubusercontent.com/ServiceStack/Assets/master/img/apps/no-profile64.png",
    "id": "Fq2AaTUvWdRWYpQ3yqoH",
    "unRegisterUrl": "https://*****.cloud.***.com/em/event-unregister?id=Fq2AaTUvWdRWYpQ3yqoH",
    "heartbeatUrl": "https://*****.cloud.***.com/em/event-heartbeat?id=Fq2AaTUvWdRWYpQ3yqoH",
    "updateSubscriberUrl": "https://*****.cloud.***.com/em/event-subscribers/Fq2AaTUvWdRWYpQ3yqoH",
    "heartbeatIntervalMs": "10000",
    "idleTimeoutMs": "30000"
  },
  "serverArgs": {
    
  }
}

The SSE client will stop sending heartbeats which eventually expire the subscription on the server who removes it from Redis. But the Redis metadata wont be removed if the SSE Server crashes.

Please see this answer/thread on using the IServerEvents.Reset() API to clear out Redis metadata entries on AppHost Start.

Also you may want to upgrade to v5.1.1 on MyGet as it fixes a potential locking and memory leak issue.

Ok thank your sorry for my late reply …we have now updated all SS Assemblies from 5.0.2 to 5.1.1 on our production system at the moment it looks fine with some little problems with the new ServiceStack.Text version which seems ot be
more strict as the previous version during the deserialization of incoming generic JSON values which we have declared as type JsonObject in the depending REST endpoint but so we could detect that some users of our REST API sometimes sends wrong data with e.g. 2 dots for double values in the JSON request so that we get exceptions like

    Message:[Failed to set property 'speed' with '24.687999725341797.0']
StackTrace:[   at ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn, Dictionary`2 typeAccessorMap)
   at ServiceStack.Text.Common.DeserializeType`1.<>c__DisplayClass2_0.<GetParseStringSegmentMethod>b__1(StringSegment value)
   at ServiceStack.Text.Json.JsonReader`1.Parse(StringSegment value)
   at ServiceStack.Text.Json.JsonReader`1.Parse(String value)
   at ServiceStack.Text.JsonSerializer.DeserializeFromString[T](String value)
   at EventQueueStatusScript.ProcessState(TourBO tourBO, String decryptedCustomerSCEMID, EventContent stateEvent, SubscriptionContent[] subscriptions)
   at EventQueueStatusScript.ProcessMessage(BrokeredMessage message)]
ExceptionType:[System.FormatException]
Message:[Input string was not in a correct format.]
StackTrace:[   at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
   at ServiceStack.Text.Common.DeserializeBuiltin`1.<>c.<GetParseStringSegmentFn>b__7_27(StringSegment value)
   at ServiceStack.Text.Common.JsReader`1.<>c__DisplayClass4_0`1.<GetCoreParseStringSegmentFn>b__4(StringSegment value)
   at ServiceStack.Text.Common.DeserializeTypeRefJson.StringToType(TypeConfig typeConfig, StringSegment strType, EmptyCtorDelegate ctorFn, Dictionary`2 typeAccessorMap)]

That’s potentially a bad error because it’s could be treated like an illegal syntax error and break serialization.

But this prints the value without thrown an Exception?

var jsonObj = JsonObject.Parse("{\"d\":24.687999725341797.0}");
var d = jsonObj.Get("d");
d.Print(); //= 24.687999725341797.0

Do you have StrictMode enabled anywhere?

no StrictMode isn’t enabled…

We have the following endpoint:

  public class CreateEventService : Service
   {
      ...
      ...
         public object Post(CreateEvent request)
        {
           ...
           ...


 [Route("/event", Summary = "Create a event", Notes = "Create a event for....", Verbs = "POST")]
    public class CreateEvent 
    {
        public CreateEvent()
        {
        }
        [ApiMember(Description = "The event (json)", IsRequired = true, ParameterType = "body", DataType = 
               "EventContent")]
        public EventContent Event { get; set; }
    }

   public class EventContent 
  {
      public JsonObject EventPayLoad { get; set; }
      ...
   }

and then we cast the EventPayLoad of type JsonObject to an internal EventPayLoadPosition class:

                EventPayLoadPosition clonedEventPayLoadPosition = JsonSerializer.DeserializeFromString<EventPayLoadPosition>(PositionEvent.EventPayLoad.SerializeToString());


public class EventPayLoadPosition 
{
    public double? Speed { get; set; }
     ....
     ....
}

ok the casting line looks a little bit cumbersome so we should probably better replace this line with:

EventPayLoadPosition clonedEventPayLoadPositiond = PositionEvent.EventPayLoad.ConvertTo<EventPayLoadPosition>();

??

If you provide a stand-alone repro (like the one I sent) I can run it, but not a partial fragment dump like this.

Also if you change your arbitrary data to an object you’ll get the more appropriate object deserialized based on the data structure that was sent, e.g. a double if it was sent a JSON double or a Dictionary<string,object> if it was sent {...}.

the stand-alone repo:

using ServiceStack;
using ServiceStack.Text;

namespace StandAlone.Repo
{
    public class CreateEventService : Service
    {
        public object Post(CreateEvent request)
        {
            EventPayLoadPosition clonedEventPayLoadPosition = JsonSerializer.DeserializeFromString<EventPayLoadPosition>(request.Event.EventPayLoad.SerializeToString());
            //or more improved
            //EventPayLoadPosition clonedEventPayLoadPositiond = request.Event.EventPayLoad.ConvertTo<EventPayLoadPosition>();

            return clonedEventPayLoadPosition;
        }
    }

    [Route("/event", Summary = "Create a event", Notes = "Create a event for....", Verbs = "POST")]
    public class CreateEvent
    {
        public CreateEvent() { }

        [ApiMember(Description = "The event (json)", IsRequired = true, ParameterType = "body", DataType = "EventContent")]
        public EventContent Event { get; set; }

    }

    public class EventContent
    {
        public JsonObject EventPayLoad { get; set; }
    }

    public class EventPayLoadPosition
    {
        public double? Heading { get; set; }

        public double? Accuracy { get; set; }

        public double? Speed { get; set; }
    }
}

Ok so it tries to convert it to a double but fails because it’s an invalid number which is what I would expect to happen. If you want to handle invalid input than access the fields as a string EventPayLoad.Get("Heading") and do your own validation/parsing.

Also having an object Type is more appropriate as you’re better able to detect what the actual payload is and handle it accordingly, e.g:

public class CreateEvent
{
    public EventContent Event { get; set; }
}

public class EventContent
{
    public object EventPayLoad { get; set; }
}

...

if (request.Event.EventPayload is Dictionary<string, object> payload)
{
    var payloadPosition = payload.FromObjectDictionary<EventPayLoadPosition>();
}

At the same time I’d recommend against having unknown types in Service Contracts which has poor interoperability and fails to work in metadata services, different serializers and clients/languages.

Yeah exactly the solution (with EventPayLoad.Get(“Heading”)) which we have already implemented yesterday …telepathy…but thanks anyway…sorry but by the way what I always wanted to ask you is are you really Demis Bellot ok I know your profile picture because it’s very memorable but how is it possible to answer so much questions so fast and not only in place of your ServiceStack forum but also in other Forums in the www I’ve seen many answered questions of you …so what I would like to say is that’s e very awesome performance of you :thumbsup: :thumbsup:

Yep that’s me! and basically it’s possible because this is all I do :slight_smile:

cool …and developing such an amazing web service framework like ServiceStack…wow :smiley:

1 Like

sorry one more question is your recommended solution changing from JsonObject to object type backward compatible for the users of our API so that we have no breaking API changes?

JsonObject is just a server side object that wraps a Dictionary<string,string>. It’s not coupled to the client. Using object is more robust as you’re able to detect the type of Object users send and handle accordingly. If existing clients were always sending a JSON object e.g. {....} then object would always be a Dictionary<string,object>.

As JsonObject is a string dictionary all values are stored as a string and you need to tell it what to convert to, where as object preserves the type that was sent so if was sent {"Heading":1.1} the EventPayLoad property will be populated with a double, i.e:

new Dictionary<string,object> {
    ["Heading"] = 1.1
}

Thanks…exactly the answer that i wanted to hear from you :thumbsup: