public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { EnsureNotClosed(); var meta = grainState.ToGrainMeta(grainReference, grainType, _serviceId); try { var collectionName = Naming.CollectionNameForGrain(grainType, _options.CollectionNamePrefix); var collection = _database.GetCollection <MongoGrain>(collectionName); LogVerbose3($"Writing data: {meta}", MongoStorageLogCode.TraceWriting); if (string.IsNullOrEmpty(grainState.ETag)) { var grain = grainState.ToGrain(grainReference, grainType, _serviceId); grain.Meta.Timestamp = DateTime.UtcNow.Ticks; await collection.InsertOneAsync(grain); grainState.ETag = grain.Meta.ETag; } else { var id = Naming.PrimaryKeyForGrain(_serviceId, grainReference); // Update data and generate new timestamp var update = Builders <MongoGrain> .Update .Set(x => x.Data, grainState.State) .Set(x => x.Meta.Timestamp, DateTime.UtcNow.Ticks); var options = new FindOneAndUpdateOptions <MongoGrain> { IsUpsert = false, ReturnDocument = ReturnDocument.After, Projection = Builders <MongoGrain> .Projection.Include(x => x.Meta) }; var updatedGrain = await collection.FindOneAndUpdateAsync <MongoGrain>( x => x.Id == id && x.Meta.Timestamp == meta.Timestamp, update, options); if (updatedGrain == null) { var message = $"Inconsistent state for grain: {meta}. ETag has changed or document has already been deleted."; throw new InconsistentStateException(message); } grainState.ETag = updatedGrain.Meta.ETag; } LogVerbose3($"Wrote data: {meta}", MongoStorageLogCode.TraceWrite); } catch (Exception ex) { LogError($"Error while writing. {meta} Error={ex.Message}", MongoStorageLogCode.ErrorWrite, ex); throw; } }