Ejemplo n.º 1
0
    public static string GetTopic(ILogger logger, int numberOfPartitions = 1)
    {
        string result = null;

        WithAdminClient(logger, adminClient =>
        {
            var topics = adminClient
                         .GetMetadata(TimeSpan.FromSeconds(10))
                         .Topics.Select(topic => topic.Topic)
                         .Select(topic => topic.Split('-'))
                         .Where(parts => parts.Length == 2 && int.TryParse(parts[1], out _))
                         .Select(parts => int.Parse(parts[1]))
                         .ToList();

            var number = topics.Any() ? topics.Max() : 0;

            var topicName = $"testtopic-{number + 1}";

            logger.Information("Using topic named {topic}", topicName);

            result = topicName;

            var topicSpecification = new TopicSpecification {
                Name = topicName, NumPartitions = numberOfPartitions, ReplicationFactor = 1
            };

            adminClient
            .CreateTopicsAsync(new[] { topicSpecification })
            .Wait();
        });

        return(result);
    }
Ejemplo n.º 2
0
    protected virtual async Task CreateTopicAsync()
    {
        using (var adminClient = new AdminClientBuilder(Options.Connections.GetOrDefault(ConnectionName)).Build())
        {
            var topic = new TopicSpecification
            {
                Name              = TopicName,
                NumPartitions     = 1,
                ReplicationFactor = 1
            };

            Options.ConfigureTopic?.Invoke(topic);

            try
            {
                await adminClient.CreateTopicsAsync(new[] { topic });
            }
            catch (CreateTopicsException e)
            {
                if (e.Results.Any(x => x.Error.Code != ErrorCode.TopicAlreadyExists))
                {
                    throw;
                }
            }
        }
    }
        /// <summary>
        /// Awaitable task to create a single topic by name, if it does not exist yet.
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="numPartitions"></param>
        /// <param name="replicationFactor"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static async Task CreateTopicIfNotExists(string topic, int numPartitions, short replicationFactor, ClientConfig configuration)
        {
            using (var adminClient = new AdminClientBuilder(configuration).Build())
            {
                try
                {
                    var topicSpecification = new TopicSpecification
                    {
                        Name              = topic,
                        NumPartitions     = numPartitions,
                        ReplicationFactor = replicationFactor
                    };

                    // CreateTopicAsync can take a list, but for here only a single topic is passed as input.
                    await adminClient.CreateTopicsAsync(new List <TopicSpecification> {
                        topicSpecification
                    });

                    Console.WriteLine($"Topic {topic} created.");
                }
                catch (CreateTopicsException ex)
                {
                    if (ex.Results[0].Error.Code != ErrorCode.TopicAlreadyExists)
                    {
                        Console.WriteLine($"An error occured creating topic {topic}: {ex.Results[0].Error.Reason}");
                    }
                    else
                    {
                        Console.WriteLine($"Topic {topic} already exists.");
                    }
                }
            }
        }
Ejemplo n.º 4
0
        public IActionResult OnGet(string link)
        {
            if (link == null)
            {
                return(NotFound());
            }

            var resultLink = _repository.Single(LinkSpecification.ByUrl(link));

            if (resultLink == null)
            {
                return(NotFound());
            }

            var resultTopic = _repository.SingleInclude(BaseSpecification <Topic> .ById(resultLink.TopicId), new List <ISpecification <Topic> > {
                TopicSpecification.IncludeQuestions()
            });

            if (resultTopic == null)
            {
                return(NotFound());
            }

            Questions = resultTopic.Questions;
            Link      = link;
            Title     = resultTopic.Title;

            return(Page());
        }
Ejemplo n.º 5
0
        protected virtual async Task CreateTopicAsync()
        {
            using (var adminClient = new AdminClientBuilder(Options.Connections.GetOrDefault(ConnectionName)).Build())
            {
                var topic = new TopicSpecification
                {
                    Name              = TopicName,
                    NumPartitions     = 1,
                    ReplicationFactor = 1
                };

                Options.ConfigureTopic?.Invoke(topic);

                try
                {
                    await adminClient.CreateTopicsAsync(new[] { topic });
                }
                catch (CreateTopicsException e)
                {
                    if (!e.Error.Reason.Contains($"Topic '{TopicName}' already exists"))
                    {
                        throw;
                    }
                }
            }
        }
Ejemplo n.º 6
0
        public bool CreateTopic(TopicSpecification topicSpecification)
        {
            if (topicSpecification == null)
            {
                throw new ArgumentNullException(nameof(topicSpecification));
            }

            var metadata = _adminClient.GetMetadata(TimeSpan.FromSeconds(30));

            if (!metadata.Topics.Exists(x => x.Topic == topicSpecification.Name))
            {
                try
                {
                    _adminClient.CreateTopicsAsync(new[]
                    {
                        topicSpecification
                    }).GetAwaiter().GetResult();
                }
                catch (Exception e)
                {
                    //just in case we have a race condition
                    if (!e.Message.Contains("already exists"))
                    {
                        throw;
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 7
0
        public async Task Create(ITopicConfiguration topicConfig)
        {
            if (!_adminClient.TopicExists(topicConfig.Name, out _))
            {
                TopicSpecification topicSpecification = new TopicSpecification
                {
                    Name              = topicConfig.Name,
                    NumPartitions     = topicConfig.Partitions,
                    ReplicationFactor = topicConfig.ReplicationFactor,
                    Configs           = new Dictionary <string, string>
                    {
                        { "retention.ms", topicConfig.RetentionMs.ToString() }
                    }
                };

                try
                {
                    await _adminClient.CreateTopicsAsync(new[] { topicSpecification });
                }
                catch (CreateTopicsException ex)
                {
                    Console.WriteLine(ex);
                }

                _adminClient.EnsureTopicCreation(topicConfig.Name);
            }
        }
Ejemplo n.º 8
0
        public static async Task AssureTopic(string host, string topic)
        {
            var adminClientConfig = new AdminClientConfig();

            adminClientConfig.BootstrapServers = host;

            using (var adminClient = new AdminClientBuilder(adminClientConfig).Build())
            {
                try
                {
                    var metadata = adminClient.GetMetadata(topic, TimeSpan.FromSeconds(10));
                    if (!metadata.Topics.Any(x => x.Topic == topic))
                    {
                        var topicSpecification = new TopicSpecification()
                        {
                            Name = topic,
                            ReplicationFactor = 1,
                            NumPartitions     = 1
                        };
                        await adminClient.CreateTopicsAsync(new TopicSpecification[] { topicSpecification });
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception($"{nameof(KafkaCommon)} failed to create topic {topic}", ex);
                }
            }
        }
Ejemplo n.º 9
0
        public async Task Create(ITopicConfiguration topicConfig)
        {
            if (!_adminClient.Value.TopicExists(topicConfig.Name, out _))
            {
                TopicSpecification topicSpecification = new TopicSpecification
                {
                    Name              = topicConfig.Name,
                    NumPartitions     = topicConfig.Partitions,
                    ReplicationFactor = topicConfig.ReplicationFactor,
                    Configs           = new Dictionary <string, string>
                    {
                        { "retention.ms", topicConfig.RetentionMs.ToString() }
                    }
                };

                try
                {
                    await _adminClient.Value.CreateTopicsAsync(new[] { topicSpecification });

                    _log.Information($"{topicConfig.Name} created.");
                }
                catch (CreateTopicsException ex)
                {
                    _log.Error(ex, "An unexpected exception occurred when creating topics.");
                }

                _adminClient.Value.EnsureTopicCreation(topicConfig.Name);
            }
        }
    public async Task CreateTopicAsync(string topic, int?numPartitions, short?replicationFactor,
                                       Dictionary <string, string> config, TimeSpan timeout)
    {
        var topicSpecification = new TopicSpecification
        {
            Name              = topic,
            NumPartitions     = numPartitions ?? -1,
            ReplicationFactor = replicationFactor ?? -1,
            Configs           = config
        };

        var options = new CreateTopicsOptions
        {
            RequestTimeout = timeout
        };

        try
        {
            await _adminClient.CreateTopicsAsync(new[] { topicSpecification }, options);
        }
        catch (KafkaException e)
        {
            throw new AdminClientException("Unable to create topic.", e);
        }
    }
Ejemplo n.º 11
0
        /// <summary>
        ///     Create all topics used by this example, if they don't already exist.
        /// </summary>
        static async Task CreateTopicsMaybe(string brokerList, string clientId)
        {
            var config = new AdminClientConfig
            {
                BootstrapServers = brokerList,
                ClientId         = clientId
            };

            using (var adminClent = new AdminClientBuilder(config).Build())
            {
                var countsTopicSpec = new TopicSpecification
                {
                    Name = Topic_Counts,
                    // note: in production, you should generally use a replication factor of 3.
                    ReplicationFactor = 1,
                    NumPartitions     = NumPartitions,
                    // this topic backs a kv (word -> count) state store, so it can be compacted.
                    Configs = new Dictionary <string, string> {
                        { "cleanup.policy", "compact" }
                    }
                };

                var wordsTopicSpec = new TopicSpecification
                {
                    Name = Topic_Words,
                    ReplicationFactor = 1,
                    NumPartitions     = NumPartitions
                };

                var inputLinesTopicSpec = new TopicSpecification
                {
                    Name = Topic_InputLines,
                    ReplicationFactor = 1,
                    NumPartitions     = NumPartitions
                };

                try
                {
                    await adminClent.CreateTopicsAsync(new List <TopicSpecification> {
                        countsTopicSpec, wordsTopicSpec, inputLinesTopicSpec
                    });
                }
                catch (CreateTopicsException ex)
                {
                    // propagate the exception unless the error was that one or more of the topics already exists.
                    if (ex.Results.Select(r => r.Error.Code).Where(el => el != ErrorCode.TopicAlreadyExists && el != ErrorCode.NoError).Count() > 0)
                    {
                        throw new Exception("Unable to create topics", ex);
                    }
                }
            }
        }
Ejemplo n.º 12
0
        public static void AddKafkaProducer <TKey, TValue>(this IServiceCollection services, IConfigurationSection configurationSection)
        {
            var connectionString             = configurationSection.GetValue <string>("ConnectionString");
            var producerConfigurationSection = configurationSection.GetSection("Producers");

            var producerOptions = new List <ProducerOptions>();

            producerConfigurationSection.Bind(producerOptions);

            var producer = producerOptions.FirstOrDefault(x =>
                                                          string.Equals(x.MessageType, typeof(TValue).Name, StringComparison.InvariantCultureIgnoreCase));

            if (producer == null)
            {
                throw new ArgumentNullException($"No producer configuration for the message type found");
            }

            var producerConfig = new ProducerConfig(producer.Configurations)
            {
                BootstrapServers = connectionString
            };

            if (producer.EnableTopicCreation)
            {
                var topicSpecification = new TopicSpecification
                {
                    Name          = typeof(TValue).Name,
                    NumPartitions = producer.NumPartitions
                };
                new KafkaTopicFactory(producerConfig).CreateTopic(topicSpecification);
            }

            services.AddSingleton(provider =>
            {
                var logger        = provider.GetRequiredService <ILogger <IProducer <TKey, TValue> > >();
                var kafkaProducer = new ProducerBuilder <TKey, TValue>(producerConfig)
                                    .SetErrorHandler((x, e) => logger.LogError($"ErrorCode: {e.Code}, Reason: {e.Reason}"))
                                    .SetLogHandler((x, e) => logger.LogInformation($"LogLevel: {e.Level}, LogName: {e.Name}, LogMessage: {e.Message}"))
                                    .SetKeySerializer(new JsonSerializer <TKey>())
                                    .SetValueSerializer(new JsonSerializer <TValue>())
                                    .Build();

                return(kafkaProducer);
            });
        }
        private static void ExecuteTopicCreate(ManageOptions options)
        {
            using (var client = GetAdminClient(options.BootstrapServer))
            {
                var topic = new TopicSpecification()
                {
                    Name = options.Name, NumPartitions = options.Partitions, ReplicationFactor = options.ReplicationFactor
                };
                var topics = new List <TopicSpecification>()
                {
                    topic
                };

                try
                {
                    var topicTask = client.CreateTopicsAsync(topics, new CreateTopicsOptions()
                    {
                        RequestTimeout = _requestTimeout, OperationTimeout = _requestTimeout
                    }) as Task <List <CreateTopicReport> >;
                    var topicResults = topicTask.Result;

                    // TODO: Refactor reporting output to generalize it. Colorize based on individual report status.
                    foreach (var result in topicResults)
                    {
                        Console.WriteLine($"Topic '{result.Topic}' creation result: '{result.Error}'.");
                    }
                }
                catch (AggregateException ex) when(ex.InnerException is CreateTopicsException)
                {
                    var cte = ex.InnerException as CreateTopicsException;

                    foreach (var result in cte.Results)
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"Topic '{result.Topic}' creation result: '{result.Error}'.");

                        Console.ResetColor();
                    }
                }
            }
        }
Ejemplo n.º 14
0
        public async Task <bool> EnsureTopicExists(string topicName)
        {
            _logger.LogInformation("Ensuring topic exists...");

            var topicSpecification = new TopicSpecification()
            {
                Name              = topicName,
                NumPartitions     = 1,
                ReplicationFactor = 1
            };
            var topicSpecifications = new List <TopicSpecification>();

            topicSpecifications.Add(topicSpecification);

            // try and get metadata about topic to check that it exists
            var metaData = _adminClient.GetMetadata(topicName, TimeSpan.FromSeconds(10));

            if (!metaData.Topics[0].Error.IsError)
            {
                _logger.LogInformation("    Topic already exists.");
                return(true);
            }

            _logger.LogInformation("    Creating topic...");
            try
            {
                await _adminClient.CreateTopicsAsync(topicSpecifications);

                _logger.LogInformation("        Topic created.");
            }
            catch (CreateTopicsException e)
            {
                // only trap fatal errors so that we let topic already exists error through
                if (e.Error.IsFatal)
                {
                    return(false);
                }
            }

            return(true);
        }
        public async Task AddTopicAsync(Topic topic)
        {
            try
            {
                var topicsSpecification = new TopicSpecification[]
                {
                    new TopicSpecification
                    {
                        Name              = topic.Nome,
                        NumPartitions     = topic.Particoes.GetValueOrDefault(6),
                        ReplicationFactor = topic.Replicas.GetValueOrDefault(3)
                    }
                };

                await client.CreateTopicsAsync(topicsSpecification);
            }
            catch (CreateTopicsException exception)
            {
                throw exception;
            }
        }
Ejemplo n.º 16
0
 protected async Task TryCreateTopic(string topicName, int partitionsCount)
 {
     try
     {
         var topicSpec = new TopicSpecification
         {
             Name = topicName,
             ReplicationFactor = 1,
             NumPartitions     = partitionsCount,
             Configs           = new Dictionary <string, string>()
         };
         if (_useCompact)
         {
             topicSpec.Configs.Add("cleanup.policy", "compact");
         }
         await AdminClient.CreateTopicsAsync(new TopicSpecification[] { topicSpec });
     }
     catch (CreateTopicsException e)
     {
         Console.WriteLine($"{e.Error.Reason}");
     }
 }
Ejemplo n.º 17
0
 public async Task CreateSingleTopic(AdminClientConfig adminConfig, TopicSpecification topicSpecs)
 {
     _logger.LogInformation(" Entered  CreateSingleTopic() with adminConfig : {0} , TopicSpecification : {1} ", adminConfig, topicSpecs);
     using (var adminclient = new AdminClientBuilder(adminConfig).Build())
     {
         try
         {
             await adminclient.CreateTopicsAsync(new TopicSpecification[] { new TopicSpecification {
                                                                                Name = "topic", NumPartitions = 2, ReplicationFactor = 1
                                                                            } });
         }
         catch (CreateTopicsException e)
         {
             e.Results.ForEach(r => Console.WriteLine(r.Error.IsError ? "had error" : r.Error.Reason));
             _logger.LogError("CreateTopicsException occured at CreateSingleTopic() : {0}", e);
         }
         catch (Exception ex)
         {
             _logger.LogError("Exception occured at CreateSingleTopic() : {0}", ex);
         }
     }
 }
Ejemplo n.º 18
0
        private static void CreateTopic()
        {
            var config = new AdminClientConfig()
            {
                BootstrapServers = _server
            };

            var topic = new TopicSpecification()
            {
                Name = _topicName, NumPartitions = 1, ReplicationFactor = 1
            };
            var topics = new List <TopicSpecification>()
            {
                topic
            };

            using (var client = new AdminClient(config))
            {
                var topicTask    = client.CreateTopicsAsync(topics) as Task <List <CreateTopicExceptionResult> >;
                var topicResults = topicTask.Result;
            }
        }
Ejemplo n.º 19
0
        public static async Task CreateTopic(string host, string topic)
        {
            var adminClientConfig = new AdminClientConfig();

            adminClientConfig.BootstrapServers = host;

            using (var adminClient = new AdminClientBuilder(adminClientConfig).Build())
            {
                try
                {
                    var topicSpecification = new TopicSpecification()
                    {
                        Name = topic,
                        ReplicationFactor = 1,
                        NumPartitions     = 1
                    };
                    await adminClient.CreateTopicsAsync(new TopicSpecification[] { topicSpecification });
                }
                catch (Exception ex)
                {
                    throw new Exception($"{nameof(KafkaCommon)} failed to create topic {topic}", ex);
                }
            }
        }
 public ConfigureTopologyFilter(IReadOnlyDictionary <string, string> clientConfig, KafkaTopicOptions options)
 {
     _options       = options;
     _specification = _options.ToSpecification();
     _config        = new AdminClientConfig(clientConfig.ToDictionary(x => x.Key, x => x.Value));
 }
Ejemplo n.º 21
0
        public IActionResult OnPost()
        {
            var resultLink = _repository.Single(LinkSpecification.ByUrl(ShortcutLink));

            if (resultLink == null)
            {
                return(NotFound());
            }

            var resultTopic = _repository.SingleInclude(BaseSpecification <Topic> .ById(resultLink.TopicId), new List <ISpecification <Topic> > {
                TopicSpecification.IncludeQuestions()
            });

            if (resultTopic == null)
            {
                return(NotFound());
            }

            if (resultTopic.PreventNSFW)
            {
                if (IsNsfw(Question.Content))
                {
                    ModelState.AddModelError("Question.Content", "Question contains word/s that are considered as NSFW");
                }
            }

            if (resultTopic.PreventSpam)
            {
                if (!ReCaptchaHelper.ValidateRecaptcha(Request.Form["g-recaptcha-response"]))
                {
                    ModelState.AddModelError("Question.Content", "Invalid ReCaptcha");
                }
            }

            switch (resultTopic.DuplicationCheck)
            {
            case DuplicationCheck.IpAddress:
            {
                if (IpAddressExists(WebHelper.GetRemoteIP, resultTopic.Id))
                {
                    ModelState.AddModelError("Question.Content", "You have already asked a question on this poll");
                    break;
                }
                else if (ModelState.IsValid)
                {
                    AddIpAddress(resultTopic.Id);
                }
                break;
            }

            case DuplicationCheck.BrowserCookie:
            {
                const string cookieKey = "PID";
                if (CookieExists(cookieKey, ShortcutLink))
                {
                    ModelState.AddModelError("Question.Content", "You have already asked a question on this poll");
                    break;
                }
                else if (ModelState.IsValid)
                {
                    WebHelper.SetCookie(cookieKey, ShortcutLink, null);
                }
                break;
            }

            case DuplicationCheck.None: break;

            default:
                _logger.LogError($"Unkown duplication check case {resultTopic.DuplicationCheck}.");
                throw new InvalidEnumArgumentException("Unrecognized case value.");
            }

            if (!ModelState.IsValid)
            {
                return(Page());
            }

            resultTopic.Questions.Add(Question);

            _repository.Update(resultTopic);

            InterrogateClient.UpdateList(ShortcutLink, Question);

            InterrogateClient.UpdateQuestionCount(ShortcutLink, resultTopic.Questions.Count);

            return(RedirectToPage("Result"));
        }
Ejemplo n.º 22
0
        public async Task Should_bypass_if_created()
        {
            var services = new ServiceCollection();

            const ushort partitionCount = 2;
            const short  replicaCount   = BrokersCount;
            const string topicName      = "do-not-create-topic";
            TaskCompletionSource <ConsumeContext <KafkaMessage> > taskCompletionSource = GetTask <ConsumeContext <KafkaMessage> >();

            services.TryAddSingleton <ILoggerFactory>(LoggerFactory);
            services.TryAddSingleton(typeof(ILogger <>), typeof(Logger <>));
            services.AddSingleton(taskCompletionSource);

            services.AddMassTransit(x =>
            {
                x.UsingInMemory((context, cfg) => cfg.ConfigureEndpoints(context));
                x.AddRider(rider =>
                {
                    rider.AddConsumer <KafkaMessageConsumer>();

                    rider.AddProducer <KafkaMessage>(topicName);

                    rider.UsingKafka((context, k) =>
                    {
                        k.Host(Host);

                        k.TopicEndpoint <KafkaMessage>(topicName, nameof(ConfigureTopology_Specs), c =>
                        {
                            c.AutoOffsetReset = AutoOffsetReset.Earliest;
                            c.ConfigureConsumer <KafkaMessageConsumer>(context);

                            c.CreateIfMissing(t =>
                            {
                                t.NumPartitions     = partitionCount;
                                t.ReplicationFactor = replicaCount;
                            });
                        });
                    });
                });
            });

            var provider = services.BuildServiceProvider();

            var busControl = provider.GetRequiredService <IBusControl>();

            var config = new AdminClientConfig {
                BootstrapServers = Host
            };
            var client        = new AdminClientBuilder(config).Build();
            var specification = new TopicSpecification
            {
                Name              = topicName,
                NumPartitions     = partitionCount,
                ReplicationFactor = replicaCount
            };
            await client.CreateTopicsAsync(new[] { specification });

            await busControl.StartAsync(TestCancellationToken);

            var serviceScope = provider.CreateScope();

            var producer = serviceScope.ServiceProvider.GetRequiredService <ITopicProducer <KafkaMessage> >();

            try
            {
                await producer.Produce(new { Text = "text" }, TestCancellationToken);

                ConsumeContext <KafkaMessage> result = await taskCompletionSource.Task;

                Assert.NotNull(result);
            }
            finally
            {
                await busControl.StopAsync(TestCancellationToken);

                await provider.DisposeAsync();

                try
                {
                    await client.DeleteTopicsAsync(new[] { topicName });
                }
                catch (DeleteTopicsException)
                {
                    //suppress
                }
                finally
                {
                    client.Dispose();
                }
            }
        }
        async static Task Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("usage: .. <recreate|fakegen|process|log> <bootstrap servers> [instance id]");
                Environment.Exit(1);
            }

            var command       = args[0];
            var brokerAddress = args[1];
            var instanceId    = args.Length == 3 ? args[2] : null;

            var simulatedWeblogTopic = "simulated-weblog";
            var filteredWeblogTopic  = "filtered-weblog";

            CancellationTokenSource cts = new CancellationTokenSource();

            Console.CancelKeyPress += (_, e) => {
                e.Cancel = true; // prevent the process from terminating.
                cts.Cancel();
            };

            switch (command)
            {
            case "recreate":
                var topicSpecs = new TopicSpecification[]
                {
                    new TopicSpecification {
                        Name = simulatedWeblogTopic, NumPartitions = 1, ReplicationFactor = 1
                    },
                    new TopicSpecification {
                        Name = filteredWeblogTopic, NumPartitions = 1, ReplicationFactor = 1
                    }
                };

                Console.WriteLine("recreating topics...");
                using (var adminClient = new AdminClientBuilder(new AdminClientConfig {
                    BootstrapServers = brokerAddress
                }).Build())
                {
                    try
                    {
                        await adminClient.DeleteTopicsAsync(topicSpecs.Select(ts => ts.Name));
                    }
                    catch (DeleteTopicsException ex)
                    {
                        foreach (var deleteResult in ex.Results)
                        {
                            if (deleteResult.Error.Code != ErrorCode.UnknownTopicOrPart)
                            {
                                throw;
                            }
                        }
                    }
                    await Task.Delay(TimeSpan.FromSeconds(5));

                    await adminClient.CreateTopicsAsync(topicSpecs);
                }
                Console.WriteLine("done.");
                break;

            case "fakegen":
                // 1. A processor that generates some fake weblog data.
                Random r = new Random();
                var    fakeDataSourceProcessor = new Processor <Null, Null, Null, string>
                {
                    BootstrapServers = brokerAddress,
                    OutputTopic      = simulatedWeblogTopic,
                    Function         = (_) =>
                    {
                        Thread.Sleep(r.Next(1000));
                        return(new Message <Null, string> {
                            Value = WebLogLine.GenerateFake()
                        });
                    }
                };
                fakeDataSourceProcessor.Start(instanceId, cts.Token);
                break;

            case "process":
                // 2. A processor that does a (mock) geoip lookup, removes pii information
                //    (IP address), and repartitions by country.
                var transformProcessor = new Processor <Null, string, string, string>
                {
                    Name                  = "geo-lookup-processor",
                    BootstrapServers      = brokerAddress,
                    InputTopic            = simulatedWeblogTopic,
                    OutputTopic           = filteredWeblogTopic,
                    ConsumeErrorTolerance = ErrorTolerance.All,
                    Function              = (m) =>
                    {
                        try
                        {
                            var logline         = m.Value;
                            var firstSpaceIndex = logline.IndexOf(' ');
                            if (firstSpaceIndex < 0)
                            {
                                throw new FormatException("unexpected logline format");
                            }
                            var ip               = logline.Substring(0, firstSpaceIndex);
                            var country          = MockGeoLookup.GetCountryFromIPAsync(ip);
                            var loglineWithoutIP = logline.Substring(firstSpaceIndex + 1);
                            var dateStart        = loglineWithoutIP.IndexOf('[');
                            var dateEnd          = loglineWithoutIP.IndexOf(']');
                            if (dateStart < 0 || dateEnd < 0 || dateEnd < dateStart)
                            {
                                throw new FormatException("unexpected logline format");
                            }
                            var requestInfo = loglineWithoutIP.Substring(dateEnd + 2);
                            return(new Message <string, string> {
                                Key = country, Value = requestInfo
                            });
                        }
                        catch (Exception)
                        {
                            // Unhandled exceptions in your processing function will cause the
                            // processor to terminate.

                            return(null);    // null -> filter (don't write output message corresponding to input message).
                        }
                    }
                };
                transformProcessor.Start(instanceId, cts.Token);
                break;

            case "log":
                // 3. A processor that just writes messages to stdout.
                var consoleWriterProcessor = new Processor <string, string, Null, Null>
                {
                    Name             = "console-writer",
                    BootstrapServers = brokerAddress,
                    InputTopic       = filteredWeblogTopic,
                    Function         = (m) =>
                    {
                        Console.WriteLine($"{m.Key} ~~~ {m.Value}");
                        return(null);
                    }
                };
                consoleWriterProcessor.Start(instanceId, cts.Token);
                break;

            default:
                Console.WriteLine($"unknown command {command}");
                break;
            }
        }