Exemple #1
0
        /// <summary>
        /// Subscribe to specific shard.
        /// </summary>
        /// <param name="db">The database.</param>
        /// <param name="plan">The consumer plan.</param>
        /// <param name="func">The function.</param>
        /// <param name="options">The options.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        private async Task SubsribeShardAsync(
            IDatabaseAsync db,
            IConsumerPlan plan,
            Func <Announcement, IAck, ValueTask> func,
            IEventSourceConsumerOptions options,
            CancellationToken cancellationToken)
        {
            string key = $"{plan.Partition}:{plan.Shard}";
            bool   isFirstBatchOrFailure = true;

            CommandFlags flags = CommandFlags.None;

            #region await db.CreateConsumerGroupIfNotExistsAsync(...)

            await db.CreateConsumerGroupIfNotExistsAsync(
                key,
                plan.ConsumerGroup,
                plan.Logger ?? _logger);

            #endregion // await db.CreateConsumerGroupIfNotExistsAsync(...)

            cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(
                cancellationToken, plan.Cancellation).Token;
            TimeSpan delay           = TimeSpan.Zero;
            int      emptyBatchCount = 0;
            while (!cancellationToken.IsCancellationRequested)
            {
                await HandleBatchAsync();
            }

            #region HandleBatchAsync

            // Handle single batch
            async ValueTask HandleBatchAsync()
            {
                StreamEntry[] results = await ReadBatchAsync();

                emptyBatchCount = results.Length == 0 ? emptyBatchCount + 1 : 0;
                results         = await ClaimStaleMessages(emptyBatchCount, results);

                await DelayIfEmpty(results.Length);

                if (results.Length == 0)
                {
                    return;
                }

                try
                {
                    var batchCancellation = new CancellationTokenSource();
                    for (int i = 0; i < results.Length && !batchCancellation.IsCancellationRequested; i++)
                    {
                        StreamEntry result = results[i];

                        #region var announcement = new Announcement(...)

                        var    entries         = result.Values.ToDictionary(m => m.Name, m => m.Value);
                        string id              = entries[nameof(MetadataExtensions.Empty.MessageId)];
                        string segmentsKey     = $"Segments~{id}";
                        string interceptorsKey = $"Interceptors~{id}";

                        string         operation      = entries[nameof(MetadataExtensions.Empty.Operation)];
                        long           producedAtUnix = (long)entries[nameof(MetadataExtensions.Empty.ProducedAt)];
                        DateTimeOffset producedAt     = DateTimeOffset.FromUnixTimeSeconds(producedAtUnix);
                        var            meta           = new Metadata
                        {
                            MessageId  = id,
                            Partition  = plan.Partition,
                            Shard      = plan.Shard,
                            Operation  = operation,
                            ProducedAt = producedAt
                        };

                        var segmentsEntities = await db.HashGetAllAsync(segmentsKey, CommandFlags.DemandMaster); // DemandMaster avoid racing

                        var segmentsPairs         = segmentsEntities.Select(m => ((string)m.Name, (byte[])m.Value));
                        var interceptionsEntities = await db.HashGetAllAsync(interceptorsKey, CommandFlags.DemandMaster); // DemandMaster avoid racing

                        var interceptionsPairs = interceptionsEntities.Select(m => ((string)m.Name, (byte[])m.Value));

                        var segmets       = Bucket.Empty.AddRange(segmentsPairs);
                        var interceptions = Bucket.Empty.AddRange(interceptionsPairs);

                        var announcement = new Announcement
                        {
                            Metadata         = meta,
                            Segments         = segmets,
                            InterceptorsData = interceptions
                        };

                        #endregion // var announcement = new Announcement(...)

                        int local          = i;
                        var cancellableIds = results[local..].Select(m => m.Id);
                        var ack            = new AckOnce(
                            () => AckAsync(result.Id),
                            plan.Options.AckBehavior, _logger,
                            async() =>
                        {
                            batchCancellation.CancelSafe();                 // cancel forward
                            await CancelAsync(cancellableIds);
                        });
                        await func(announcement, ack);
                    }