Пример #1
0
        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>();
        }
Пример #3
0
        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));
            }
        }