Skip to content

StartupQueue plugin loses enrichment closure on replay #132

@richerm

Description

@richerm

Describe the bug

We are trying to use the enrichment closure for Track events. The incoming event is null IF the StartupQueue hasn't completed yet (meaning it'll replay it later). If I thread sleep for say a few seconds, then track the event, the enrichment closure is fire.

I believe this is because the startup plugin does not have the enrichment closure with it so when it plays it back it loses the original closure.

To Reproduce
Steps to reproduce the behavior:

  1. Start the Analytics with a config and immediately call Track with an enrichment closure

Expected behavior
The enrichment closure RawEvent is not null.

Platform (please complete the following information):

  • Library Version in use: 2.5.1
  • Platform being tested: Windows, Linux
  • Integrations in use: N/A

Additional context

Track Call

var analyticsClient = scope.ServiceProvider.GetService<Analytics>();
// System.Threading.Thread.Sleep(5000);   // This gives time for it to startup (no replay needed)
analyticsClient.Track(
    "TestEvent",
    properties,
    (RawEvent evt) =>
    {
        evt.UserId = "12345"; // explodes if StartupQueue plugin stores an event for replay.
    }
);

Scoped Configuration:

builder.Services.AddScoped<Analytics>(
    (provider) =>
    {
        var config = new Configuration(
            writeKey: builder.Configuration.GetSection("Segment")["WriteKey"],
            storageProvider: new InMemoryStorageProvider(),
            flushAt: 1,
            eventPipelineProvider: new SyncEventPipelineProvider()
        );

        var analytics = new Analytics(config);

        return analytics;
    }
);

Stock Plugins Loaded:

Before - StartupQueue
Before - ContextPlugin
Destination - SegmentDestination

Activity

wenxi-zeng

wenxi-zeng commented on Apr 16, 2025

@wenxi-zeng
Contributor

hi @richerm, thanks for reporting this issue. this is in our roadmap. will get back to you once we have a release for it.

richerm

richerm commented on Apr 16, 2025

@richerm
Author

@wenxi-zeng - thanks - In the meantime is there a way I can check the 'state' or system status of the Analytics class? Basically I can block until it is 'ready'? This would save an arbitrary wait in code.

if (analyticsClient.state != SystemState.Running)
   // wait
wenxi-zeng

wenxi-zeng commented on Apr 16, 2025

@wenxi-zeng
Contributor

@richerm unfortunately no. the state is marked as internal. another workaround would be custom plugin. see this comment for example. not sure if that'd work for your use case though. I'll also try to escalate this issue.

richerm

richerm commented on Apr 16, 2025

@richerm
Author

@wenxi-zeng Yes I saw that comment when searching earlier. Unfortunately, the enrichment I'm doing (in addition to user id) has contextual information and the serialize it to a property and to deserialize in a plugin and move to context is a pretty heavy workaround. Hopefully this gets addressed soon. In the meantime, I'm going to switch to a singleton model vs scoped with the caveats noted in the docs.

wenxi-zeng

wenxi-zeng commented on Apr 16, 2025

@wenxi-zeng
Contributor

@richerm sounds good! I have prioritized a fix for this issue. should have a release out next monday.

wenxi-zeng

wenxi-zeng commented on Apr 23, 2025

@wenxi-zeng
Contributor

hi @richerm, this issue should be fixed in the latest release 2.5.2. please have a try. thanks!

richerm

richerm commented on Apr 28, 2025

@richerm
Author

Awesome! Thank-you for the quick turn-around!

richerm

richerm commented on Apr 30, 2025

@richerm
Author

@wenxi-zeng

I received this error on writing to storage because the Enrichment property is marked as serializable. Is that expected behaviour?

The unsupported member type is located on type 'System.Func`2[Segment.Analytics.RawEvent,Segment.Analytics.RawEvent]'. Path: $.Enrichment.

[11:59:43 ERR] Segment.io: Error writing events to storage.
System.NotSupportedException: Serialization and deserialization of 'System.Func`2[[Segment.Analytics.RawEvent, Segment.Analytics.CSharp, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null],[Segment.Analytics.RawEvent, Segment.Analytics.CSharp, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null]]' instances is not supported. The unsupported member type is located on type 'System.Func`2[Segment.Analytics.RawEvent,Segment.Analytics.RawEvent]'. Path: $.Enrichment.
 ---> System.NotSupportedException: Serialization and deserialization of 'System.Func`2[[Segment.Analytics.RawEvent, Segment.Analytics.CSharp, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null],[Segment.Analytics.RawEvent, Segment.Analytics.CSharp, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null]]' instances is not supported.
   at System.Text.Json.Serialization.Converters.UnsupportedTypeConverter`1.Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   --- End of inner exception stack trace ---
   at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& state, Exception innerException)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsObject(Utf8JsonWriter writer, Object rootValue)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed)
   at System.Text.Json.JsonSerializer.WriteString[TValue](TValue& value, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
   at Segment.Serialization.JsonUtility.ToJson(Object value, Boolean pretty)
   at Segment.Analytics.Utilities.SyncEventPipeline.<Write>b__22_0()
wenxi-zeng

wenxi-zeng commented on Apr 30, 2025

@wenxi-zeng
Contributor

hi @richerm, no, this is not expected. I just released a fix. please give 2.5.3 a try. you shouldn't see this error anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @richerm@wenxi-zeng

      Issue actions

        StartupQueue plugin loses enrichment closure on replay · Issue #132 · segmentio/Analytics-CSharp