Skip to content

Serializing with a reused JsonSerializerOptions instance with SerializationFilter.ForElements produces incorrect results in high concurrency #3214

@almostchristian

Description

@almostchristian

Describe the bug
The Firely documentation recommends to "reuse the instances of JsonSerializerOptions" for all deserialization calls for performance. We also reuse it for serialization calls. We have observed that during load testing, some of our server responses did not include the Bundle.entry. We are removing the caching of the JsonSerializerOptions to fix the issue. In our repro code, we also notice that this can also happen when using the SerializationFilter.ForText and SerializationFilter.ForCount.

Minimal reproduction code

using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using System.Collections.Concurrent;
using System.Text.Json;

var options = new JsonSerializerOptions()
    .ForFhir(new FhirJsonPocoSerializerSettings { SummaryFilter = SerializationFilter.ForElements(["id", "active"]) })
    .Pretty();

var patient = new Patient
{
    Id = "123",
    Active = true,
    Name = [new() { Family = "Doe", Given = ["John"] }],
    MultipleBirth = new FhirBoolean(false),
};
var bundle = new Bundle
{
    Type = Bundle.BundleType.Collection,
    Entry = [ new() { Resource = patient } ]
};

ConcurrentBag<string> serialized = [];
var result = Parallel.For(0, 100, i =>
{
    serialized.Add(JsonSerializer.Serialize(bundle, options));
});

for (int i = 0; i < serialized.Count; i++)
{
    Console.WriteLine($"{i + 1:000}: {serialized.ElementAt(i)}");
}

Result:
The resulting json does not have the element filter properly applied. Multiplebirth and name are present when they shouldn't be. Also sometimes, the entries are missing altogether.

099: {
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "123",
        "active": true,
        "name": [
          {
            "family": "Doe",
            "given": [
              "John"
            ]
          }
        ],
        "multipleBirthBoolean": false
      }
    }
  ]
}
100: {
  "resourceType": "Bundle",
  "type": "collection"
}

Expected behavior
If I change the test code to not use Parallel.For, the result is the desired output:

099: {
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "123",
        "active": true
      }
    }
  ]
}
100: {
  "resourceType": "Bundle",
  "type": "collection",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "123",
        "active": true
      }
    }
  ]
}

Version used:

  • FHIR Version: R4/R5
  • Version: 5.11.3, 5.12.0, 6.0.0-beta1

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions