public void TestConcurrent() { var serializerRepresentation = GetSerializerRepresentation(); var tags = new List <NamedValue <string> > { new NamedValue <string>("ChangeSet", Guid.NewGuid().ToString().ToUpperInvariant()), }; var timestampUtc = DateTime.UtcNow; var payloadType = typeof(byte[]).ToRepresentation(); var metadata = new StreamRecordMetadata( Guid.NewGuid().ToString().ToUpperInvariant(), serializerRepresentation, typeof(string).ToRepresentation().ToWithAndWithoutVersion(), payloadType.ToWithAndWithoutVersion(), tags, timestampUtc, timestampUtc); var payload = new BinaryDescribedSerialization(payloadType, serializerRepresentation, new byte[3000000]); var putOp = new StandardPutRecordOp(metadata, payload); var commandTimeout = TimeSpan.FromSeconds(1000); var listOfStreams = Enumerable.Range(1, 10) .Select( _ => this.GetCreatedSqlStream( commandTimeout, RecordTagAssociationManagementStrategy.ExternallyManaged)) .ToList(); var times = new ConcurrentBag <TimeSpan>(); Parallel.ForEach(listOfStreams, _ => { var stopwatch = new Stopwatch(); for (var idx = 0; idx < 100; idx++) { stopwatch.Reset(); stopwatch.Start(); _.Execute(putOp); stopwatch.Stop(); times.Add(stopwatch.Elapsed); } }); var averageSeconds = times.Average(_ => _.TotalSeconds); var minSeconds = times.Min(_ => _.TotalSeconds); var maxSeconds = times.Max(_ => _.TotalSeconds); this.testOutputHelper.WriteLine(Invariant($"{nameof(averageSeconds)}: {averageSeconds}, {nameof(minSeconds)}: {minSeconds}, {nameof(maxSeconds)}: {maxSeconds}, ")); foreach (var time in times) { this.testOutputHelper.WriteLine(time.TotalSeconds.ToString(CultureInfo.InvariantCulture)); } }
public DatabaseDummyFactory() { // ------------------------------- EVENTS ------------------------------------- AutoFixtureBackedDummyFactory.AddDummyCreator( () => new HandlingForStreamDisabledEvent( A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new HandlingForStreamEnabledEvent( A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new HandlingForRecordDisabledEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var record = A.Dummy <StreamRecord>(); return(new RecordHandlingAvailableEvent( record.InternalRecordId, A.Dummy <string>(), record, A.Dummy <UtcDateTime>(), A.Dummy <string>())); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingCanceledEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingCompletedEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingFailedEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingFailureResetEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingRunningEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new RecordHandlingSelfCanceledEvent( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new PruneOperationExecutedEvent( A.Dummy <IPruneOp>(), A.Dummy <PruneSummary>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new PruneOperationRequestedEvent( A.Dummy <IPruneOp>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new PruneRequestCanceledEvent( A.Dummy <string>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new UniqueLongIssuedEvent( A.Dummy <long>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var result = new IdDeprecatedEvent( A.Dummy <UtcDateTime>(), A.Dummy <string>()); return(result); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new IdDeprecatedEvent <Version, Version>( A.Dummy <Version>(), A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new IdDeprecatedEvent <Version>( A.Dummy <UtcDateTime>(), A.Dummy <string>())); // ------------------------------- MODELS ------------------------------------- AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StreamRecordHandlingEntry( A.Dummy <long>(), A.Dummy <long>(), A.Dummy <string>(), A.Dummy <HandlingStatus>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), A.Dummy <string>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var scenario = ThreadSafeRandom.Next(1, 4); switch (scenario) { case 1: return(new CreateStreamResult(false, true)); case 2: return(new CreateStreamResult(true, false)); case 3: return(new CreateStreamResult(true, true)); default: throw new NotSupportedException( FormattableString.Invariant($"Invalid scenario {scenario} for creating a dummy {nameof(CreateStreamResult)}.")); } }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StreamRecordMetadata( A.Dummy <string>(), A.Dummy <SerializerRepresentation>(), A.Dummy <TypeRepresentationWithAndWithoutVersion>(), A.Dummy <TypeRepresentationWithAndWithoutVersion>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), A.Dummy <UtcDateTime>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StreamRecordMetadata <Version>( A.Dummy <Version>(), A.Dummy <SerializerRepresentation>(), A.Dummy <TypeRepresentationWithAndWithoutVersion>(), A.Dummy <TypeRepresentationWithAndWithoutVersion>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), A.Dummy <UtcDateTime>(), A.Dummy <UtcDateTime>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new GetStreamFromRepresentationOp <FileStreamRepresentation, MemoryStandardStream>( A.Dummy <FileStreamRepresentation>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var scenario = ThreadSafeRandom.Next(1, 5); switch (scenario) { case 1: return(new PutRecordResult(A.Dummy <long>(), null)); case 2: return(new PutRecordResult(null, Some.ReadOnlyDummies <long>().ToList())); case 3: return(new PutRecordResult(null, Some.ReadOnlyDummies <long>().ToList(), Some.ReadOnlyDummies <long>().ToList())); case 4: return(new PutRecordResult(A.Dummy <long>(), Some.ReadOnlyDummies <long>().ToList(), Some.ReadOnlyDummies <long>().ToList())); default: throw new NotSupportedException(FormattableString.Invariant($"Invalid scenario {scenario} for creating a dummy {nameof(PutRecordResult)}.")); } }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var scenario = ThreadSafeRandom.Next(1, 4); switch (scenario) { case 1: return(new TryHandleRecordResult(null, false)); case 2: return(new TryHandleRecordResult(A.Dummy <StreamRecord>(), false)); case 3: return(new TryHandleRecordResult(null, true)); default: throw new NotSupportedException(FormattableString.Invariant($"Invalid scenario {scenario} for creating a dummy {nameof(TryHandleRecordResult)}.")); } }); // ------------------------------- ENUMS -------------------------------------- AutoFixtureBackedDummyFactory.ConstrainDummyToBeOneOf(VersionMatchStrategy.Any, VersionMatchStrategy.SpecifiedVersion); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(CompositeHandlingStatus.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(ExistingDatabaseStrategy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(ExistingRecordStrategy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(ExistingStreamStrategy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(HandlingStatus.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(OrderRecordsBy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(RecordNotFoundStrategy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(StreamNotFoundStrategy.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(StreamRecordItemsToInclude.Unknown); AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(TagMatchStrategy.Unknown); // ------------------------------- MODEL INTERFACES -------------------------------------- AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IResourceLocator>(); AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IStreamRepresentation>(); // ------------------------------- OPERATIONS ------------------------------------- AutoFixtureBackedDummyFactory.AddDummyCreator( () => new PruneBeforeInternalRecordDateOp( A.Dummy <UtcDateTime>(), A.Dummy <string>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StandardPruneStreamOp( A.Dummy <long>(), A.Dummy <UtcDateTime>(), A.Dummy <string>(), A.Dummy <IResourceLocator>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StandardUpdateHandlingStatusForRecordOp( A.Dummy <long>(), A.Dummy <string>(), A.Dummy <HandlingStatus>().ThatIsNot(HandlingStatus.DisabledForStream), Some.ReadOnlyDummies <HandlingStatus>().ToList(), A.Dummy <string>(), Some.ReadOnlyDummies <NamedValue <string> >().ToList(), A.Dummy <bool>(), A.Dummy <IResourceLocator>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => new StandardUpdateHandlingStatusForStreamOp( A.Dummy <HandlingStatus>().ThatIsIn(new[] { HandlingStatus.DisabledForStream, HandlingStatus.AvailableByDefault }), A.Dummy <string>(), Some.ReadOnlyDummies <NamedValue <string> >().ToList(), A.Dummy <IResourceLocator>())); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var existingRecordStrategy = A.Dummy <ExistingRecordStrategy>(); var result = new PutAndReturnInternalRecordIdOp <Version>( A.Dummy <Version>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), existingRecordStrategy, existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundById || existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundByIdAndType ? (int?)A.Dummy <ZeroOrPositiveInteger>() : null, A.Dummy <VersionMatchStrategy>()); return(result); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var existingRecordStrategy = A.Dummy <ExistingRecordStrategy>(); var result = new PutOp <Version>( A.Dummy <Version>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), existingRecordStrategy, existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundById || existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundByIdAndType ? (int?)A.Dummy <ZeroOrPositiveInteger>() : null, A.Dummy <VersionMatchStrategy>()); return(result); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var existingRecordStrategy = A.Dummy <ExistingRecordStrategy>(); var result = new PutWithIdAndReturnInternalRecordIdOp <Version, Version>( A.Dummy <Version>(), A.Dummy <Version>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), existingRecordStrategy, existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundById || existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundByIdAndType ? (int?)A.Dummy <ZeroOrPositiveInteger>() : null, A.Dummy <VersionMatchStrategy>()); return(result); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var existingRecordStrategy = A.Dummy <ExistingRecordStrategy>(); var result = new PutWithIdOp <Version, Version>( A.Dummy <Version>(), A.Dummy <Version>(), A.Dummy <IReadOnlyCollection <NamedValue <string> > >(), existingRecordStrategy, existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundById || existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundByIdAndType ? (int?)A.Dummy <ZeroOrPositiveInteger>() : null, A.Dummy <VersionMatchStrategy>()); return(result); }); AutoFixtureBackedDummyFactory.AddDummyCreator( () => { var existingRecordStrategy = A.Dummy <ExistingRecordStrategy>(); var result = new StandardPutRecordOp( A.Dummy <StreamRecordMetadata>(), A.Dummy <DescribedSerializationBase>(), existingRecordStrategy, existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundById || existingRecordStrategy == ExistingRecordStrategy.PruneIfFoundByIdAndType ? (int?)A.Dummy <ZeroOrPositiveInteger>() : null, A.Dummy <VersionMatchStrategy>(), A.Dummy <long?>(), A.Dummy <IResourceLocator>()); return(result); }); // ------------------------------- OPERATION INTERFACES ------------------------------------- AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IPruneOp>(); }
public override PutRecordResult Execute( StandardPutRecordOp operation) { operation.MustForArg(nameof(operation)).NotBeNull(); var sqlServerLocator = this.TryGetLocator(operation); var payloadSerializationFormat = operation.Payload.GetSerializationFormat(); var serializerRepresentation = this.GetIdAddIfNecessarySerializerRepresentation(sqlServerLocator, operation.Payload.SerializerRepresentation, payloadSerializationFormat); var identifierTypeIds = this.GetIdsAddIfNecessaryType(sqlServerLocator, operation.Metadata.TypeRepresentationOfId); var objectTypeIds = this.GetIdsAddIfNecessaryType(sqlServerLocator, operation.Metadata.TypeRepresentationOfObject); var tagIdsCsv = operation.Metadata.Tags == null ? null : this.GetIdsAddIfNecessaryTag(sqlServerLocator, operation.Metadata.Tags).Select(_ => _.ToStringInvariantPreferred()).ToCsv(); var storedProcOp = StreamSchema.Sprocs.PutRecord.BuildExecuteStoredProcedureOp( this.Name, serializerRepresentation, identifierTypeIds, objectTypeIds, operation.InternalRecordId, operation.Metadata.StringSerializedId, payloadSerializationFormat == SerializationFormat.String ? operation.Payload.GetSerializedPayloadAsEncodedString() : null, payloadSerializationFormat == SerializationFormat.Binary ? operation.Payload.GetSerializedPayloadAsEncodedBytes() : null, operation.Metadata.ObjectTimestampUtc, tagIdsCsv, operation.ExistingRecordStrategy, operation.RecordRetentionCount, operation.VersionMatchStrategy); var sqlProtocol = this.BuildSqlOperationsProtocol(sqlServerLocator); var sprocResult = sqlProtocol.Execute(storedProcOp); var newRecordId = sprocResult.OutputParameters[nameof(StreamSchema.Sprocs.PutRecord.OutputParamName.Id)].GetValueOfType <long?>(); var existingRecordIdsCsv = sprocResult.OutputParameters[nameof(StreamSchema.Sprocs.PutRecord.OutputParamName.ExistingRecordIdsCsv)].GetValueOfType <string>(); var prunedRecordIdsCsv = sprocResult.OutputParameters[nameof(StreamSchema.Sprocs.PutRecord.OutputParamName.PrunedRecordIdsCsv)].GetValueOfType <string>(); var existingRecordIds = existingRecordIdsCsv.FromCsv()?.Select(_ => long.Parse(_, CultureInfo.InvariantCulture)).ToList() ?? new List <long>(); var prunedRecordIds = prunedRecordIdsCsv.FromCsv()?.Select(_ => long.Parse(_, CultureInfo.InvariantCulture)).ToList() ?? new List <long>(); if (prunedRecordIds.Any() && !existingRecordIds.Any()) { throw new InvalidOperationException(Invariant($"There were '{nameof(prunedRecordIds)}' but no '{nameof(existingRecordIds)}', this scenario should not occur, it is expected that the records pruned were in the existing set; pruned: '{prunedRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'.")); } if (existingRecordIds.Any()) { switch (operation.ExistingRecordStrategy) { case ExistingRecordStrategy.None: throw new InvalidOperationException(Invariant($"Operation {nameof(ExistingRecordStrategy)} was {operation.ExistingRecordStrategy}; did not expect any '{nameof(existingRecordIds)}' value but got '{existingRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'.")); case ExistingRecordStrategy.ThrowIfFoundById: throw new InvalidOperationException( Invariant( $"Operation {nameof(ExistingRecordStrategy)} was {operation.ExistingRecordStrategy}; expected to not find a record by identifier '{operation.Metadata.StringSerializedId}' yet found '{nameof(existingRecordIds)}' '{existingRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'.")); case ExistingRecordStrategy.ThrowIfFoundByIdAndType: throw new InvalidOperationException( Invariant( $"Operation {nameof(ExistingRecordStrategy)} was {operation.ExistingRecordStrategy}; expected to not find a record by identifier '{operation.Metadata.StringSerializedId}' and identifier type '{operation.Metadata.TypeRepresentationOfId.GetTypeRepresentationByStrategy(operation.VersionMatchStrategy)}' and object type '{operation.Metadata.TypeRepresentationOfObject.GetTypeRepresentationByStrategy(operation.VersionMatchStrategy)}' yet found '{nameof(existingRecordIds)}': '{existingRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'.")); case ExistingRecordStrategy.ThrowIfFoundByIdAndTypeAndContent: throw new InvalidOperationException( Invariant( $"Operation {nameof(ExistingRecordStrategy)} was {operation.ExistingRecordStrategy}; expected to not find a record by identifier '{operation.Metadata.StringSerializedId}' and identifier type '{operation.Metadata.TypeRepresentationOfId.GetTypeRepresentationByStrategy(operation.VersionMatchStrategy)}' and object type '{operation.Metadata.TypeRepresentationOfObject.GetTypeRepresentationByStrategy(operation.VersionMatchStrategy)}' and contents '{operation.Payload}' yet found '{nameof(existingRecordIds)}': '{existingRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'.")); case ExistingRecordStrategy.DoNotWriteIfFoundById: case ExistingRecordStrategy.DoNotWriteIfFoundByIdAndType: case ExistingRecordStrategy.DoNotWriteIfFoundByIdAndTypeAndContent: if (newRecordId != null) { throw new InvalidOperationException(Invariant($"Expect {nameof(ExistingRecordStrategy)} of {operation.ExistingRecordStrategy} to not write when there are existing ids found: '{existingRecordIds.Select(_ => _.ToString(CultureInfo.InvariantCulture)).ToCsv()}'; new record id: '{newRecordId}'.")); } return(new PutRecordResult(null, existingRecordIds, prunedRecordIds)); case ExistingRecordStrategy.PruneIfFoundById: case ExistingRecordStrategy.PruneIfFoundByIdAndType: return(new PutRecordResult(newRecordId, existingRecordIds, prunedRecordIds)); default: throw new NotSupportedException( Invariant($"{nameof(ExistingRecordStrategy)} {operation.ExistingRecordStrategy} is not supported.")); } } else { return(new PutRecordResult(newRecordId)); } }