コード例 #1
0
        public async Task SerializeAsync(Stream stream, DataSegment segment, Random?random = null, CancellationToken token = default)
        {
            // length
            int numsize = s_encoding.GetBytes(segment.Length.ToString(), _buffer);
            await stream.WriteAsync(_buffer.AsMemory(0, numsize), token);

            stream.WriteByte((byte)',');
            // checksum
            numsize = s_encoding.GetBytes(segment.Checksum.ToString(), _buffer);
            await stream.WriteAsync(_buffer.AsMemory(0, numsize), token);

            stream.WriteByte((byte)',');
            // payload
            Memory <byte> source = segment.AsMemory();

            // write the entire segment outright if not given random instance
            if (random == null)
            {
                await stream.WriteAsync(source, token);

                return;
            }
            // randomize chunking otherwise
            while (source.Length > 0)
            {
                if (random.NextBoolean(probability: 0.05))
                {
                    stream.WriteByte(source.Span[0]);
                    source = source.Slice(1);
                }
                else
                {
                    // TODO consider non-uniform distribution for chunk sizes
                    int           chunkSize = random.Next(source.Length);
                    Memory <byte> chunk     = source.Slice(0, chunkSize);
                    source = source.Slice(chunkSize);

                    if (random.NextBoolean(probability: 0.9))
                    {
                        await stream.WriteAsync(chunk, token);
                    }
                    else
                    {
                        stream.Write(chunk.Span);
                    }
                }

                if (random.NextBoolean(probability: 0.3))
                {
                    await stream.FlushAsync(token);
                }

                // randomized delay
                if (random.NextBoolean(probability: 0.05))
                {
                    if (random.NextBoolean(probability: 0.7))
                    {
                        await Task.Delay(random.Next(60));
                    }
                    else
                    {
                        Thread.SpinWait(random.Next(1000));
                    }
                }
            }
        }
コード例 #2
0
        protected override async Task HandleConnection(int workerId, long jobId, SslStream stream, TcpClient client, Random random, TimeSpan duration, CancellationToken token)
        {
            // token used for signalling cooperative cancellation; do not pass this to SslStream methods
            using var connectionLifetimeToken = new CancellationTokenSource(duration);

            long     messagesInFlight = 0;
            DateTime lastWrite        = DateTime.Now;
            DateTime lastRead         = DateTime.Now;

            await StressTaskExtensions.WhenAllThrowOnFirstException(token, Sender, Receiver, Monitor);

            async Task Sender(CancellationToken token)
            {
                var serializer = new DataSegmentSerializer();

                while (!token.IsCancellationRequested && !connectionLifetimeToken.IsCancellationRequested)
                {
                    await ApplyBackpressure();

                    DataSegment chunk = DataSegment.CreateRandom(random, _config.MaxBufferLength);
                    try
                    {
                        await serializer.SerializeAsync(stream, chunk, random, token);

                        stream.WriteByte((byte)'\n');
                        await stream.FlushAsync(token);

                        Interlocked.Increment(ref messagesInFlight);
                        lastWrite = DateTime.Now;
                    }
                    finally
                    {
                        chunk.Return();
                    }
                }

                // write an empty line to signal completion to the server
                stream.WriteByte((byte)'\n');
                await stream.FlushAsync(token);

                /// Polls until number of in-flight messages falls below threshold
                async Task ApplyBackpressure()
                {
                    if (Volatile.Read(ref messagesInFlight) > 5000)
                    {
                        Stopwatch stopwatch = Stopwatch.StartNew();
                        bool      isLogged  = false;

                        while (!token.IsCancellationRequested && !connectionLifetimeToken.IsCancellationRequested && Volatile.Read(ref messagesInFlight) > 2000)
                        {
                            // only log if tx has been suspended for a while
                            if (!isLogged && stopwatch.ElapsedMilliseconds >= 1000)
                            {
                                isLogged = true;
                                lock (Console.Out)
                                {
                                    Console.ForegroundColor = ConsoleColor.Yellow;
                                    Console.WriteLine($"worker #{workerId}: applying backpressure");
                                    Console.WriteLine();
                                    Console.ResetColor();
                                }
                            }

                            await Task.Delay(20);
                        }

                        if (isLogged)
                        {
                            Console.WriteLine($"worker #{workerId}: resumed tx after {stopwatch.Elapsed}");
                        }
                    }
                }
            }

            async Task Receiver(CancellationToken token)
            {
                var serializer = new DataSegmentSerializer();

                using var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
                await stream.ReadLinesUsingPipesAsync(Callback, cts.Token, separator : '\n');

                Task Callback(ReadOnlySequence <byte> buffer)
                {
                    if (buffer.Length == 0 && connectionLifetimeToken.IsCancellationRequested)
                    {
                        // server echoed back empty buffer sent by client,
                        // signal cancellation and complete the connection.
                        cts.Cancel();
                        return(Task.CompletedTask);
                    }

                    // deserialize to validate the checksum, then discard
                    DataSegment chunk = serializer.Deserialize(buffer);

                    chunk.Return();
                    Interlocked.Decrement(ref messagesInFlight);
                    lastRead = DateTime.Now;
                    return(Task.CompletedTask);
                }
            }

            async Task Monitor(CancellationToken token)
            {
                do
                {
                    await Task.Delay(500);

                    if ((DateTime.Now - lastWrite) >= TimeSpan.FromSeconds(10))
                    {
                        throw new Exception($"worker #{workerId} job #{jobId} has stopped writing bytes to server");
                    }

                    if ((DateTime.Now - lastRead) >= TimeSpan.FromSeconds(10))
                    {
                        throw new Exception($"worker #{workerId} job #{jobId} has stopped receiving bytes from server");
                    }
                }while(!token.IsCancellationRequested && !connectionLifetimeToken.IsCancellationRequested);
            }
        }