karafka 2.4.0 → 2.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +26 -34
- data/.github/workflows/ci.yml +18 -6
- data/.ruby-version +1 -1
- data/CHANGELOG.md +146 -1
- data/Gemfile +10 -5
- data/Gemfile.lock +60 -39
- data/LICENSE +8 -3
- data/bin/integrations +13 -1
- data/certs/cert.pem +26 -0
- data/config/locales/errors.yml +18 -2
- data/config/locales/pro_errors.yml +44 -0
- data/docker-compose.yml +1 -3
- data/karafka.gemspec +6 -4
- data/lib/active_job/queue_adapters/karafka_adapter.rb +18 -7
- data/lib/karafka/active_job/dispatcher.rb +13 -0
- data/lib/karafka/active_job/job_extensions.rb +3 -0
- data/lib/karafka/admin.rb +86 -0
- data/lib/karafka/app.rb +17 -0
- data/lib/karafka/base_consumer.rb +130 -19
- data/lib/karafka/cli/base.rb +24 -8
- data/lib/karafka/cli/install.rb +2 -1
- data/lib/karafka/cli/server.rb +1 -0
- data/lib/karafka/cli/swarm.rb +1 -0
- data/lib/karafka/cli/topics/align.rb +12 -2
- data/lib/karafka/cli/topics/plan.rb +54 -6
- data/lib/karafka/cli/topics.rb +45 -18
- data/lib/karafka/connection/client.rb +102 -35
- data/lib/karafka/connection/listener.rb +48 -11
- data/lib/karafka/connection/messages_buffer.rb +19 -6
- data/lib/karafka/connection/proxy.rb +3 -0
- data/lib/karafka/connection/raw_messages_buffer.rb +43 -9
- data/lib/karafka/connection/rebalance_manager.rb +24 -13
- data/lib/karafka/contracts/config.rb +4 -0
- data/lib/karafka/contracts/consumer_group.rb +17 -0
- data/lib/karafka/contracts/routing.rb +59 -0
- data/lib/karafka/contracts/topic.rb +14 -0
- data/lib/karafka/embedded.rb +46 -3
- data/lib/karafka/errors.rb +3 -2
- data/lib/karafka/helpers/async.rb +11 -2
- data/lib/karafka/helpers/config_importer.rb +13 -0
- data/lib/karafka/instrumentation/assignments_tracker.rb +7 -2
- data/lib/karafka/instrumentation/logger_listener.rb +45 -4
- data/lib/karafka/instrumentation/notifications.rb +12 -0
- data/lib/karafka/instrumentation/vendors/appsignal/client.rb +32 -11
- data/lib/karafka/instrumentation/vendors/appsignal/errors_listener.rb +1 -1
- data/lib/karafka/instrumentation/vendors/appsignal/metrics_listener.rb +3 -1
- data/lib/karafka/instrumentation/vendors/datadog/logger_listener.rb +17 -19
- data/lib/karafka/instrumentation/vendors/datadog/metrics_listener.rb +27 -18
- data/lib/karafka/instrumentation/vendors/kubernetes/base_listener.rb +2 -2
- data/lib/karafka/instrumentation/vendors/kubernetes/liveness_listener.rb +41 -13
- data/lib/karafka/messages/message.rb +9 -9
- data/lib/karafka/pro/active_job/consumer.rb +2 -10
- data/lib/karafka/pro/active_job/dispatcher.rb +67 -19
- data/lib/karafka/pro/active_job/job_options_contract.rb +12 -10
- data/lib/karafka/pro/base_consumer.rb +2 -10
- data/lib/karafka/pro/cleaner/errors.rb +2 -10
- data/lib/karafka/pro/cleaner/messages/message.rb +14 -12
- data/lib/karafka/pro/cleaner/messages/messages.rb +2 -10
- data/lib/karafka/pro/cleaner/messages/metadata.rb +41 -0
- data/lib/karafka/pro/cleaner.rb +3 -10
- data/lib/karafka/pro/connection/manager.rb +6 -10
- data/lib/karafka/pro/connection/multiplexing/listener.rb +2 -10
- data/lib/karafka/pro/contracts/base.rb +2 -10
- data/lib/karafka/pro/contracts/server_cli_options.rb +2 -10
- data/lib/karafka/pro/encryption/cipher.rb +2 -10
- data/lib/karafka/pro/encryption/contracts/config.rb +2 -10
- data/lib/karafka/pro/encryption/errors.rb +2 -10
- data/lib/karafka/pro/encryption/messages/middleware.rb +2 -10
- data/lib/karafka/pro/encryption/messages/parser.rb +2 -10
- data/lib/karafka/pro/encryption/setup/config.rb +2 -10
- data/lib/karafka/pro/encryption.rb +2 -10
- data/lib/karafka/pro/instrumentation/performance_tracker.rb +2 -10
- data/lib/karafka/pro/iterator/expander.rb +2 -10
- data/lib/karafka/pro/iterator/tpl_builder.rb +2 -10
- data/lib/karafka/pro/iterator.rb +2 -10
- data/lib/karafka/pro/loader.rb +5 -11
- data/lib/karafka/pro/processing/adaptive_iterator/consumer.rb +54 -0
- data/lib/karafka/pro/processing/adaptive_iterator/tracker.rb +67 -0
- data/lib/karafka/pro/processing/collapser.rb +2 -10
- data/lib/karafka/pro/processing/coordinator.rb +2 -10
- data/lib/karafka/pro/processing/coordinators/errors_tracker.rb +2 -10
- data/lib/karafka/pro/processing/coordinators/filters_applier.rb +19 -10
- data/lib/karafka/pro/processing/coordinators/virtual_offset_manager.rb +2 -10
- data/lib/karafka/pro/processing/executor.rb +2 -10
- data/lib/karafka/pro/processing/expansions_selector.rb +3 -10
- data/lib/karafka/pro/processing/filters/base.rb +14 -10
- data/lib/karafka/pro/processing/filters/delayer.rb +4 -12
- data/lib/karafka/pro/processing/filters/expirer.rb +2 -10
- data/lib/karafka/pro/processing/filters/inline_insights_delayer.rb +2 -10
- data/lib/karafka/pro/processing/filters/throttler.rb +2 -10
- data/lib/karafka/pro/processing/filters/virtual_limiter.rb +2 -10
- data/lib/karafka/pro/processing/jobs/consume_non_blocking.rb +4 -10
- data/lib/karafka/pro/processing/jobs/eofed_non_blocking.rb +26 -0
- data/lib/karafka/pro/processing/jobs/periodic.rb +4 -10
- data/lib/karafka/pro/processing/jobs/periodic_non_blocking.rb +4 -10
- data/lib/karafka/pro/processing/jobs/revoked_non_blocking.rb +4 -10
- data/lib/karafka/pro/processing/jobs_builder.rb +14 -10
- data/lib/karafka/pro/processing/jobs_queue.rb +2 -10
- data/lib/karafka/pro/processing/offset_metadata/consumer.rb +2 -10
- data/lib/karafka/pro/processing/offset_metadata/fetcher.rb +2 -10
- data/lib/karafka/pro/processing/offset_metadata/listener.rb +2 -10
- data/lib/karafka/pro/processing/partitioner.rb +35 -24
- data/lib/karafka/pro/processing/periodic_job/consumer.rb +2 -10
- data/lib/karafka/pro/processing/piping/consumer.rb +2 -10
- data/lib/karafka/pro/processing/schedulers/base.rb +2 -10
- data/lib/karafka/pro/processing/schedulers/default.rb +3 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom.rb +3 -11
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_lrj_mom_vp.rb +3 -11
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_ftr_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom.rb +3 -11
- data/lib/karafka/pro/processing/strategies/aj/dlq_lrj_mom_vp.rb +3 -11
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/dlq_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/ftr_lrj_mom_vp.rb +3 -11
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/ftr_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/lrj_mom_vp.rb +5 -13
- data/lib/karafka/pro/processing/strategies/aj/mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/aj/mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/base.rb +2 -10
- data/lib/karafka/pro/processing/strategies/default.rb +140 -58
- data/lib/karafka/pro/processing/strategies/dlq/default.rb +23 -15
- data/lib/karafka/pro/processing/strategies/dlq/ftr.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj.rb +3 -11
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom.rb +7 -11
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/ftr_lrj_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom.rb +19 -11
- data/lib/karafka/pro/processing/strategies/dlq/ftr_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/ftr_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/lrj.rb +3 -11
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom.rb +19 -11
- data/lib/karafka/pro/processing/strategies/dlq/lrj_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/lrj_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/mom.rb +24 -16
- data/lib/karafka/pro/processing/strategies/dlq/mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/dlq/vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/ftr/default.rb +17 -12
- data/lib/karafka/pro/processing/strategies/ftr/vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/lrj/default.rb +5 -13
- data/lib/karafka/pro/processing/strategies/lrj/ftr.rb +3 -11
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom.rb +2 -10
- data/lib/karafka/pro/processing/strategies/lrj/ftr_mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/lrj/ftr_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/lrj/mom.rb +4 -12
- data/lib/karafka/pro/processing/strategies/lrj/mom_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/lrj/vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/mom/default.rb +2 -10
- data/lib/karafka/pro/processing/strategies/mom/ftr.rb +2 -10
- data/lib/karafka/pro/processing/strategies/mom/ftr_vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/mom/vp.rb +2 -10
- data/lib/karafka/pro/processing/strategies/vp/default.rb +5 -10
- data/lib/karafka/pro/processing/strategies.rb +2 -10
- data/lib/karafka/pro/processing/strategy_selector.rb +2 -10
- data/lib/karafka/pro/processing/subscription_groups_coordinator.rb +2 -10
- data/lib/karafka/pro/recurring_tasks/consumer.rb +97 -0
- data/lib/karafka/pro/recurring_tasks/contracts/config.rb +45 -0
- data/lib/karafka/pro/recurring_tasks/contracts/task.rb +33 -0
- data/lib/karafka/pro/recurring_tasks/deserializer.rb +27 -0
- data/lib/karafka/pro/recurring_tasks/dispatcher.rb +79 -0
- data/lib/karafka/pro/recurring_tasks/errors.rb +26 -0
- data/lib/karafka/pro/recurring_tasks/executor.rb +144 -0
- data/lib/karafka/pro/recurring_tasks/listener.rb +30 -0
- data/lib/karafka/pro/recurring_tasks/matcher.rb +30 -0
- data/lib/karafka/pro/recurring_tasks/schedule.rb +55 -0
- data/lib/karafka/pro/recurring_tasks/serializer.rb +105 -0
- data/lib/karafka/pro/recurring_tasks/setup/config.rb +44 -0
- data/lib/karafka/pro/recurring_tasks/task.rb +143 -0
- data/lib/karafka/pro/recurring_tasks.rb +79 -0
- data/lib/karafka/pro/routing/features/active_job/builder.rb +2 -10
- data/lib/karafka/pro/routing/features/active_job.rb +2 -10
- data/lib/karafka/pro/routing/features/adaptive_iterator/config.rb +26 -0
- data/lib/karafka/pro/routing/features/adaptive_iterator/contracts/topic.rb +66 -0
- data/lib/karafka/pro/routing/features/adaptive_iterator/topic.rb +54 -0
- data/lib/karafka/pro/routing/features/adaptive_iterator.rb +23 -0
- data/lib/karafka/pro/routing/features/base.rb +2 -10
- data/lib/karafka/pro/routing/features/dead_letter_queue/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/dead_letter_queue/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/dead_letter_queue.rb +2 -10
- data/lib/karafka/pro/routing/features/delaying/config.rb +2 -10
- data/lib/karafka/pro/routing/features/delaying/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/delaying/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/delaying.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments/config.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/consumer_group.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments/subscription_group.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/direct_assignments.rb +2 -10
- data/lib/karafka/pro/routing/features/expiring/config.rb +2 -10
- data/lib/karafka/pro/routing/features/expiring/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/expiring/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/expiring.rb +2 -10
- data/lib/karafka/pro/routing/features/filtering/config.rb +2 -10
- data/lib/karafka/pro/routing/features/filtering/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/filtering/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/filtering.rb +2 -10
- data/lib/karafka/pro/routing/features/inline_insights/config.rb +2 -10
- data/lib/karafka/pro/routing/features/inline_insights/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/inline_insights/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/inline_insights.rb +2 -10
- data/lib/karafka/pro/routing/features/long_running_job/config.rb +2 -10
- data/lib/karafka/pro/routing/features/long_running_job/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/long_running_job/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/long_running_job.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/config.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/patches/contracts/consumer_group.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/proxy.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/subscription_group.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing/subscription_groups_builder.rb +2 -10
- data/lib/karafka/pro/routing/features/multiplexing.rb +2 -10
- data/lib/karafka/pro/routing/features/non_blocking_job/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/non_blocking_job.rb +2 -10
- data/lib/karafka/pro/routing/features/offset_metadata/config.rb +2 -10
- data/lib/karafka/pro/routing/features/offset_metadata/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/offset_metadata/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/offset_metadata.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/builder.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/config.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/consumer_group.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/contracts/consumer_group.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/contracts/pattern.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/detector.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/pattern.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/patterns.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns/topics.rb +2 -10
- data/lib/karafka/pro/routing/features/patterns.rb +2 -10
- data/lib/karafka/pro/routing/features/pausing/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/pausing/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/pausing.rb +2 -10
- data/lib/karafka/pro/routing/features/periodic_job/config.rb +2 -10
- data/lib/karafka/pro/routing/features/periodic_job/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/periodic_job/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/periodic_job.rb +2 -10
- data/lib/karafka/pro/routing/features/recurring_tasks/builder.rb +123 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/config.rb +20 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/contracts/topic.rb +32 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/proxy.rb +19 -0
- data/lib/karafka/pro/routing/features/recurring_tasks/topic.rb +36 -0
- data/lib/karafka/pro/routing/features/recurring_tasks.rb +17 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/builder.rb +123 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/config.rb +20 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/contracts/topic.rb +32 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/proxy.rb +19 -0
- data/lib/karafka/pro/routing/features/scheduled_messages/topic.rb +36 -0
- data/lib/karafka/pro/routing/features/scheduled_messages.rb +16 -0
- data/lib/karafka/pro/routing/features/swarm/config.rb +2 -10
- data/lib/karafka/pro/routing/features/swarm/contracts/routing.rb +2 -10
- data/lib/karafka/pro/routing/features/swarm/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/swarm/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/swarm.rb +2 -10
- data/lib/karafka/pro/routing/features/throttling/config.rb +2 -10
- data/lib/karafka/pro/routing/features/throttling/contracts/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/throttling/topic.rb +2 -10
- data/lib/karafka/pro/routing/features/throttling.rb +2 -10
- data/lib/karafka/pro/routing/features/virtual_partitions/config.rb +3 -10
- data/lib/karafka/pro/routing/features/virtual_partitions/contracts/topic.rb +3 -10
- data/lib/karafka/pro/routing/features/virtual_partitions/topic.rb +10 -12
- data/lib/karafka/pro/routing/features/virtual_partitions.rb +2 -10
- data/lib/karafka/pro/scheduled_messages/consumer.rb +177 -0
- data/lib/karafka/pro/scheduled_messages/contracts/config.rb +48 -0
- data/lib/karafka/pro/scheduled_messages/contracts/message.rb +88 -0
- data/lib/karafka/pro/scheduled_messages/daily_buffer.rb +71 -0
- data/lib/karafka/pro/scheduled_messages/day.rb +37 -0
- data/lib/karafka/pro/scheduled_messages/deserializers/headers.rb +38 -0
- data/lib/karafka/pro/scheduled_messages/deserializers/payload.rb +27 -0
- data/lib/karafka/pro/scheduled_messages/dispatcher.rb +114 -0
- data/lib/karafka/pro/scheduled_messages/errors.rb +20 -0
- data/lib/karafka/pro/scheduled_messages/max_epoch.rb +33 -0
- data/lib/karafka/pro/scheduled_messages/proxy.rb +177 -0
- data/lib/karafka/pro/scheduled_messages/schema_validator.rb +29 -0
- data/lib/karafka/pro/scheduled_messages/serializer.rb +47 -0
- data/lib/karafka/pro/scheduled_messages/setup/config.rb +52 -0
- data/lib/karafka/pro/scheduled_messages/state.rb +54 -0
- data/lib/karafka/pro/scheduled_messages/tracker.rb +56 -0
- data/lib/karafka/pro/scheduled_messages.rb +59 -0
- data/lib/karafka/pro/swarm/liveness_listener.rb +2 -10
- data/lib/karafka/processing/coordinator.rb +14 -0
- data/lib/karafka/processing/executor.rb +29 -1
- data/lib/karafka/processing/jobs/base.rb +13 -0
- data/lib/karafka/processing/jobs/consume.rb +2 -0
- data/lib/karafka/processing/jobs/eofed.rb +29 -0
- data/lib/karafka/processing/jobs/idle.rb +2 -0
- data/lib/karafka/processing/jobs/revoked.rb +2 -0
- data/lib/karafka/processing/jobs/shutdown.rb +2 -0
- data/lib/karafka/processing/jobs_builder.rb +6 -0
- data/lib/karafka/processing/schedulers/default.rb +1 -0
- data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
- data/lib/karafka/processing/strategies/default.rb +45 -13
- data/lib/karafka/processing/strategies/dlq.rb +19 -5
- data/lib/karafka/processing/strategies/dlq_mom.rb +27 -8
- data/lib/karafka/processing/worker.rb +26 -13
- data/lib/karafka/railtie.rb +11 -42
- data/lib/karafka/routing/builder.rb +19 -1
- data/lib/karafka/routing/consumer_group.rb +9 -14
- data/lib/karafka/routing/features/dead_letter_queue/config.rb +3 -0
- data/lib/karafka/routing/features/dead_letter_queue/contracts/topic.rb +1 -0
- data/lib/karafka/routing/features/dead_letter_queue/topic.rb +7 -2
- data/lib/karafka/routing/features/eofed/config.rb +15 -0
- data/lib/karafka/routing/features/eofed/contracts/topic.rb +39 -0
- data/lib/karafka/routing/features/eofed/topic.rb +31 -0
- data/lib/karafka/routing/features/eofed.rb +14 -0
- data/lib/karafka/routing/subscription_group.rb +29 -1
- data/lib/karafka/routing/topic.rb +24 -1
- data/lib/karafka/runner.rb +10 -9
- data/lib/karafka/server.rb +37 -1
- data/lib/karafka/setup/attributes_map.rb +11 -4
- data/lib/karafka/setup/config.rb +11 -52
- data/lib/karafka/setup/defaults_injector.rb +64 -0
- data/lib/karafka/swarm/node.rb +2 -0
- data/lib/karafka/swarm/supervisor.rb +11 -2
- data/lib/karafka/templates/karafka.rb.erb +2 -2
- data/lib/karafka/version.rb +1 -1
- data/lib/karafka.rb +47 -7
- data.tar.gz.sig +0 -0
- metadata +116 -33
- metadata.gz.sig +0 -0
- data/certs/cert_chain.pem +0 -26
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Consumer responsible for management of the recurring tasks and their execution
|
|
10
|
+
# There are some assumptions made here that always need to be satisfied:
|
|
11
|
+
# - we only run schedules that are of same or newer version
|
|
12
|
+
# - we always mark as consumed in such a way, that the first message received after
|
|
13
|
+
# assignment (if any) is a state
|
|
14
|
+
class Consumer < ::Karafka::BaseConsumer
|
|
15
|
+
# @param args [Array] all arguments accepted by the consumer
|
|
16
|
+
def initialize(*args)
|
|
17
|
+
super
|
|
18
|
+
@executor = Executor.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def consume
|
|
22
|
+
# There is nothing we can do if we operate on a newer schedule. In such cases we should
|
|
23
|
+
# just wait and re-raise error hoping someone will notice or that this will be
|
|
24
|
+
# reassigned to a process with newer schedule
|
|
25
|
+
raise(Errors::IncompatibleScheduleError) if @executor.incompatible?
|
|
26
|
+
|
|
27
|
+
messages.each do |message|
|
|
28
|
+
payload = message.payload
|
|
29
|
+
type = payload[:type]
|
|
30
|
+
|
|
31
|
+
case type
|
|
32
|
+
when 'schedule'
|
|
33
|
+
# If we're replaying data, we need to record the most recent stored state, so we
|
|
34
|
+
# can use this data to fully initialize the scheduler
|
|
35
|
+
@executor.update_state(payload) if @executor.replaying?
|
|
36
|
+
|
|
37
|
+
# If this is first message we cannot mark it on the previous offset
|
|
38
|
+
next if message.offset.zero?
|
|
39
|
+
|
|
40
|
+
# We always mark as consumed in such a way, that when replaying, we start from a
|
|
41
|
+
# schedule state message. This makes it easier to recover.
|
|
42
|
+
mark_as_consumed Karafka::Messages::Seek.new(
|
|
43
|
+
topic.name,
|
|
44
|
+
partition,
|
|
45
|
+
message.offset - 1
|
|
46
|
+
)
|
|
47
|
+
when 'command'
|
|
48
|
+
@executor.apply_command(payload)
|
|
49
|
+
|
|
50
|
+
next if @executor.replaying?
|
|
51
|
+
|
|
52
|
+
# Execute on each incoming command to have nice latency but only after replaying
|
|
53
|
+
# During replaying we should not execute because there may be more state changes
|
|
54
|
+
# that collectively have a different outcome
|
|
55
|
+
@executor.call
|
|
56
|
+
else
|
|
57
|
+
raise ::Karafka::Errors::UnsupportedCaseError, type
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
eofed if eofed?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Starts the final replay process if we reached eof during replaying
|
|
65
|
+
def eofed
|
|
66
|
+
# We only mark as replayed if we were replaying in the first place
|
|
67
|
+
# If already replayed, nothing to do
|
|
68
|
+
return unless @executor.replaying?
|
|
69
|
+
|
|
70
|
+
@executor.replay
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Runs the cron execution if all good.
|
|
74
|
+
def tick
|
|
75
|
+
# Do nothing until we fully recover the correct state
|
|
76
|
+
return if @executor.replaying?
|
|
77
|
+
|
|
78
|
+
# If the state is incompatible, we can only raise an error.
|
|
79
|
+
# We do it here and in the `#consume` so the pause-retry kicks in basically reporting
|
|
80
|
+
# on this issue once every minute. That way user should not miss this.
|
|
81
|
+
# We use seek to move so we can achieve a pause of 60 seconds in between consecutive
|
|
82
|
+
# errors instead of on each tick because it is much more frequent.
|
|
83
|
+
if @executor.incompatible?
|
|
84
|
+
if messages.empty?
|
|
85
|
+
raise Errors::IncompatibleScheduleError
|
|
86
|
+
else
|
|
87
|
+
return seek(messages.last.offset - 1)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# If all good and compatible we can execute the recurring tasks
|
|
92
|
+
@executor.call
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Recurring Tasks related contracts
|
|
10
|
+
module Contracts
|
|
11
|
+
# Makes sure, all the expected config is defined as it should be
|
|
12
|
+
class Config < ::Karafka::Contracts::Base
|
|
13
|
+
configure do |config|
|
|
14
|
+
config.error_messages = YAML.safe_load(
|
|
15
|
+
File.read(
|
|
16
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
|
17
|
+
)
|
|
18
|
+
).fetch('en').fetch('validations').fetch('config')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
nested(:recurring_tasks) do
|
|
22
|
+
required(:consumer_class) { |val| val < ::Karafka::BaseConsumer }
|
|
23
|
+
required(:deserializer) { |val| !val.nil? }
|
|
24
|
+
required(:logging) { |val| [true, false].include?(val) }
|
|
25
|
+
# Do not allow to run more often than every 5 seconds
|
|
26
|
+
required(:interval) { |val| val.is_a?(Integer) && val >= 1_000 }
|
|
27
|
+
required(:group_id) do |val|
|
|
28
|
+
val.is_a?(String) && Karafka::Contracts::TOPIC_REGEXP.match?(val)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
nested(:topics) do
|
|
32
|
+
required(:schedules) do |val|
|
|
33
|
+
val.is_a?(String) && Karafka::Contracts::TOPIC_REGEXP.match?(val)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
required(:logs) do |val|
|
|
37
|
+
val.is_a?(String) && Karafka::Contracts::TOPIC_REGEXP.match?(val)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Recurring Tasks related contracts
|
|
10
|
+
module Contracts
|
|
11
|
+
# Ensures that task details are as expected
|
|
12
|
+
class Task < ::Karafka::Contracts::Base
|
|
13
|
+
configure do |config|
|
|
14
|
+
config.error_messages = YAML.safe_load(
|
|
15
|
+
File.read(
|
|
16
|
+
File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
|
|
17
|
+
)
|
|
18
|
+
).fetch('en').fetch('validations').fetch('recurring_tasks')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Regexp to ensure all tasks ids are URL safe
|
|
22
|
+
ID_REGEXP = /\A[a-zA-Z0-9_-]{1,}\z/
|
|
23
|
+
|
|
24
|
+
required(:id) { |val| val.is_a?(String) && val.match?(ID_REGEXP) }
|
|
25
|
+
required(:cron) { |val| val.is_a?(String) && val.length.positive? }
|
|
26
|
+
required(:enabled) { |val| [true, false].include?(val) }
|
|
27
|
+
required(:changed) { |val| [true, false].include?(val) }
|
|
28
|
+
required(:previous_time) { |val| val.is_a?(Numeric) || val.is_a?(Time) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Recurring Tasks data deserializer. We compress data ourselves because we cannot rely on
|
|
10
|
+
# any external optional features like certain compression types, etc. By doing this that way
|
|
11
|
+
# we can ensure we have full control over the compression.
|
|
12
|
+
#
|
|
13
|
+
# @note We use `symbolize_names` because we want to use the same convention of hash building
|
|
14
|
+
# for producing, consuming and displaying related data as in other places.
|
|
15
|
+
class Deserializer
|
|
16
|
+
# @param message [::Karafka::Messages::Message]
|
|
17
|
+
# @return [Hash] deserialized data
|
|
18
|
+
def call(message)
|
|
19
|
+
::JSON.parse(
|
|
20
|
+
Zlib::Inflate.inflate(message.raw_payload),
|
|
21
|
+
symbolize_names: true
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Dispatches appropriate recurring tasks related messages to expected topics
|
|
10
|
+
class Dispatcher
|
|
11
|
+
class << self
|
|
12
|
+
# Snapshots to Kafka current schedule state
|
|
13
|
+
def schedule
|
|
14
|
+
produce(
|
|
15
|
+
topics.schedules,
|
|
16
|
+
'state:schedule',
|
|
17
|
+
serializer.schedule(::Karafka::Pro::RecurringTasks.schedule)
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Dispatches the command request
|
|
22
|
+
#
|
|
23
|
+
# @param name [String, Symbol] name of the command we want to deal with in the process
|
|
24
|
+
# @param task_id [String] id of the process. We use name instead of id only
|
|
25
|
+
# because in the web ui we work with the full name and it is easier. Since
|
|
26
|
+
def command(name, task_id)
|
|
27
|
+
produce(
|
|
28
|
+
topics.schedules,
|
|
29
|
+
"command:#{name}:#{task_id}",
|
|
30
|
+
serializer.command(name, task_id)
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Dispatches the task execution log record
|
|
35
|
+
# @param event [Karafka::Core::Monitoring::Event]
|
|
36
|
+
def log(event)
|
|
37
|
+
produce(
|
|
38
|
+
topics.logs,
|
|
39
|
+
event[:task].id,
|
|
40
|
+
serializer.log(event)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# @return [::WaterDrop::Producer] web ui producer
|
|
47
|
+
def producer
|
|
48
|
+
::Karafka::App.config.recurring_tasks.producer
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [String] consumers commands topic
|
|
52
|
+
def topics
|
|
53
|
+
::Karafka::App.config.recurring_tasks.topics
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [Serializer]
|
|
57
|
+
def serializer
|
|
58
|
+
Serializer.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Converts payload to json, compresses it and dispatches to Kafka
|
|
62
|
+
#
|
|
63
|
+
# @param topic [String] target topic
|
|
64
|
+
# @param key [String]
|
|
65
|
+
# @param payload [Hash] hash with payload
|
|
66
|
+
def produce(topic, key, payload)
|
|
67
|
+
producer.produce_async(
|
|
68
|
+
topic: topic,
|
|
69
|
+
key: key,
|
|
70
|
+
partition: 0,
|
|
71
|
+
payload: payload,
|
|
72
|
+
headers: { 'zlib' => 'true' }
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Recurring Tasks related errors
|
|
10
|
+
module Errors
|
|
11
|
+
# Base for all the recurring tasks errors
|
|
12
|
+
BaseError = Class.new(::Karafka::Errors::BaseError)
|
|
13
|
+
|
|
14
|
+
# Raised when older cron manager version is trying to work with newer schema
|
|
15
|
+
IncompatibleSchemaError = Class.new(BaseError)
|
|
16
|
+
|
|
17
|
+
# Raised if you use versioned schedule and an existing schedule from Kafka was picked up
|
|
18
|
+
# by a process with older schedule version.
|
|
19
|
+
# This is a safe-guard to protect against a cases where there would be a temporary
|
|
20
|
+
# reassignment of newer schedule data into older process during deployment. It should go
|
|
21
|
+
# away once all processes are rolled.
|
|
22
|
+
IncompatibleScheduleError = Class.new(BaseError)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Executor is responsible for management of the state of recurring tasks schedule and
|
|
10
|
+
# is the heart of recurring tasks. It coordinates the replaying process as well as
|
|
11
|
+
# tracking of data on changes.
|
|
12
|
+
class Executor
|
|
13
|
+
# Task commands we support and that can be triggered on tasks (if matched)
|
|
14
|
+
COMMANDS = %w[
|
|
15
|
+
disable
|
|
16
|
+
enable
|
|
17
|
+
trigger
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@replaying = true
|
|
22
|
+
@incompatible = false
|
|
23
|
+
@catchup_commands = []
|
|
24
|
+
@catchup_schedule = nil
|
|
25
|
+
@matcher = Matcher.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Boolean] are we in the replaying phase or not (false means, regular operations)
|
|
29
|
+
def replaying?
|
|
30
|
+
@replaying
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Boolean] Is the current process schedule incompatible (older) than the one
|
|
34
|
+
# that we have in memory
|
|
35
|
+
def incompatible?
|
|
36
|
+
@incompatible
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Applies given command to task (or many tasks) by running the command on tasks that match
|
|
40
|
+
# @param command_hash [Hash] deserialized command data
|
|
41
|
+
def apply_command(command_hash)
|
|
42
|
+
cmd_name = command_hash[:command][:name]
|
|
43
|
+
|
|
44
|
+
raise(Karafka::Errors::UnsupportedCaseError, cmd_name) unless COMMANDS.include?(cmd_name)
|
|
45
|
+
|
|
46
|
+
schedule.each do |task|
|
|
47
|
+
next unless @matcher.matches?(task, command_hash)
|
|
48
|
+
|
|
49
|
+
task.public_send(cmd_name)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Updates the catchup state
|
|
54
|
+
# @param schedule_hash [Hash] deserialized schedule hash hash
|
|
55
|
+
def update_state(schedule_hash)
|
|
56
|
+
@catchup_schedule = schedule_hash
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Once all previous data is accumulated runs the catchup process to establish current
|
|
60
|
+
# state of the recurring tasks schedule execution.
|
|
61
|
+
#
|
|
62
|
+
# It includes applying any requested commands as well as synchronizing execution details
|
|
63
|
+
# for existing schedule and making sure all is loaded correctly.
|
|
64
|
+
def replay
|
|
65
|
+
# Ensure replaying happens only once
|
|
66
|
+
return unless @replaying
|
|
67
|
+
|
|
68
|
+
@replaying = false
|
|
69
|
+
|
|
70
|
+
# When there is nothing to replay and synchronize, we can just save the state and
|
|
71
|
+
# proceed
|
|
72
|
+
if @catchup_commands.empty? && @catchup_schedule.nil?
|
|
73
|
+
snapshot
|
|
74
|
+
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# If the schedule version we have in Kafka is higher than ours, we cannot proceed
|
|
79
|
+
# This prevents us from applying older changes to a new schedule
|
|
80
|
+
if @catchup_schedule[:schedule_version] > schedule.version
|
|
81
|
+
@incompatible = true
|
|
82
|
+
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Now we can synchronize the in-memory state based on the last state stored in Kafka
|
|
87
|
+
schedule.each do |task|
|
|
88
|
+
stored_task = @catchup_schedule[:tasks][task.id.to_sym]
|
|
89
|
+
|
|
90
|
+
next unless stored_task
|
|
91
|
+
|
|
92
|
+
stored_previous_time = stored_task[:previous_time]
|
|
93
|
+
task.previous_time = stored_previous_time.zero? ? 0 : Time.at(stored_previous_time)
|
|
94
|
+
|
|
95
|
+
stored_task[:enabled] ? task.enable : task.disable
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
@catchup_commands.each do |cmd|
|
|
99
|
+
apply_command(cmd)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# We make sure to save in Kafka once more once everything is up to date
|
|
103
|
+
snapshot
|
|
104
|
+
|
|
105
|
+
@catchup_schedule = nil
|
|
106
|
+
@catchup_commands = []
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Run all tasks that should run at a given time and if any tasks were changed in any way
|
|
110
|
+
# or executed, stores the most recent state back to Kafka
|
|
111
|
+
def call
|
|
112
|
+
changed = false
|
|
113
|
+
|
|
114
|
+
schedule.each do |task|
|
|
115
|
+
changed = true if task.changed?
|
|
116
|
+
|
|
117
|
+
unless task.call?
|
|
118
|
+
task.clear
|
|
119
|
+
|
|
120
|
+
next
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
changed = true
|
|
124
|
+
task.call
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
snapshot if changed
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# @return [Karafka::Pro::RecurringTasks::Schedule] current in-memory schedule
|
|
133
|
+
def schedule
|
|
134
|
+
::Karafka::Pro::RecurringTasks.schedule
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Dispatches the current schedule state to Kafka
|
|
138
|
+
def snapshot
|
|
139
|
+
Dispatcher.schedule
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Listener we use to track execution of recurring tasks and publish those events into the
|
|
10
|
+
# recurring tasks log table
|
|
11
|
+
class Listener
|
|
12
|
+
# @param event [Karafka::Core::Monitoring::Event] task execution event
|
|
13
|
+
def on_recurring_tasks_task_executed(event)
|
|
14
|
+
Dispatcher.log(event)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param event [Karafka::Core::Monitoring::Event] error that occurred
|
|
18
|
+
# @note We do nothing with other errors. We only want to log and dispatch information about
|
|
19
|
+
# the recurring tasks errors. The general Web UI error tracking may also work but those
|
|
20
|
+
# are independent. It is not to replace the Web UI tracking but to just log failed
|
|
21
|
+
# executions in the same way as successful but just with the failure as an outcome.
|
|
22
|
+
def on_error_occurred(event)
|
|
23
|
+
return unless event[:type] == 'recurring_tasks.task.execute.error'
|
|
24
|
+
|
|
25
|
+
Dispatcher.log(event)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Matcher used to check if given command can be applied to a given task.
|
|
10
|
+
class Matcher
|
|
11
|
+
# @param task [Karafka::Pro::RecurringTasks::Task]
|
|
12
|
+
# @param payload [Hash] command message payload
|
|
13
|
+
# @return [Boolean] is this message dedicated to current process and is actionable
|
|
14
|
+
def matches?(task, payload)
|
|
15
|
+
# We only match commands
|
|
16
|
+
return false unless payload[:type] == 'command'
|
|
17
|
+
|
|
18
|
+
# * is a wildcard to match all for batch commands
|
|
19
|
+
return false unless payload[:task][:id] == '*' || payload[:task][:id] == task.id
|
|
20
|
+
|
|
21
|
+
# Ignore messages that have different schema. This can happen in the middle of
|
|
22
|
+
# upgrades of the framework. We ignore this not to risk compatibility issues
|
|
23
|
+
return false unless payload[:schema_version] == Serializer::SCHEMA_VERSION
|
|
24
|
+
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Represents the current code-context schedule with defined tasks and their cron execution
|
|
10
|
+
# details. Single schedule includes all the information about all the tasks that we have
|
|
11
|
+
# defined and to be executed in a given moment in time.
|
|
12
|
+
class Schedule
|
|
13
|
+
# @return [String]
|
|
14
|
+
attr_reader :version
|
|
15
|
+
|
|
16
|
+
# @return [Hash<String, Task>]
|
|
17
|
+
attr_reader :tasks
|
|
18
|
+
|
|
19
|
+
# @param version [String] schedule version. In case of usage of versioning it is used to
|
|
20
|
+
# ensure, that older still active processes do not intercept the assignment to run older
|
|
21
|
+
# version of the scheduler. It is important to make sure, that this string is comparable.
|
|
22
|
+
def initialize(version:)
|
|
23
|
+
@version = version
|
|
24
|
+
@tasks = {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Adds task into the tasks accumulator
|
|
28
|
+
# @param task [Task]
|
|
29
|
+
# @note In case of multiple tasks with the same id, it will overwrite
|
|
30
|
+
def <<(task)
|
|
31
|
+
@tasks[task.id] = task
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Iterates over tasks yielding them one after another
|
|
35
|
+
# @param block [Proc] block that will be executed with each task
|
|
36
|
+
def each(&block)
|
|
37
|
+
@tasks.each_value(&block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param id [String] id of a particular recurring task
|
|
41
|
+
# @return [Task, nil] task with a given id or nil if not found
|
|
42
|
+
def find(id)
|
|
43
|
+
@tasks[id]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Allows us to have a nice DSL for defining schedules
|
|
47
|
+
# @param args [Array] attributes accepted by the task initializer
|
|
48
|
+
# @param block [Proc] block to execute
|
|
49
|
+
def schedule(**args, &block)
|
|
50
|
+
self << Task.new(**args, &block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This code is part of Karafka Pro, a commercial component not licensed under LGPL.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
module Karafka
|
|
7
|
+
module Pro
|
|
8
|
+
module RecurringTasks
|
|
9
|
+
# Converts schedule command and log details into data we can dispatch to Kafka.
|
|
10
|
+
class Serializer
|
|
11
|
+
# Current recurring tasks related schema structure
|
|
12
|
+
SCHEMA_VERSION = '1.0'
|
|
13
|
+
|
|
14
|
+
# @param schedule [Karafka::Pro::RecurringTasks::Schedule] schedule to serialize
|
|
15
|
+
# @return [String] serialized and compressed current schedule data with its tasks and their
|
|
16
|
+
# current state.
|
|
17
|
+
def schedule(schedule)
|
|
18
|
+
tasks = {}
|
|
19
|
+
|
|
20
|
+
schedule.each do |task|
|
|
21
|
+
tasks[task.id] = {
|
|
22
|
+
id: task.id,
|
|
23
|
+
cron: task.cron.original,
|
|
24
|
+
previous_time: task.previous_time.to_i,
|
|
25
|
+
next_time: task.next_time.to_i,
|
|
26
|
+
enabled: task.enabled?
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
data = {
|
|
31
|
+
schema_version: SCHEMA_VERSION,
|
|
32
|
+
schedule_version: schedule.version,
|
|
33
|
+
dispatched_at: Time.now.to_f,
|
|
34
|
+
type: 'schedule',
|
|
35
|
+
tasks: tasks
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
compress(
|
|
39
|
+
serialize(data)
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param command_name [String] command name
|
|
44
|
+
# @param task_id [String] task id or '*' to match all.
|
|
45
|
+
# @return [String] serialized and compressed command data
|
|
46
|
+
def command(command_name, task_id)
|
|
47
|
+
data = {
|
|
48
|
+
schema_version: SCHEMA_VERSION,
|
|
49
|
+
schedule_version: ::Karafka::Pro::RecurringTasks.schedule.version,
|
|
50
|
+
dispatched_at: Time.now.to_f,
|
|
51
|
+
type: 'command',
|
|
52
|
+
command: {
|
|
53
|
+
name: command_name
|
|
54
|
+
},
|
|
55
|
+
task: {
|
|
56
|
+
id: task_id
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
compress(
|
|
61
|
+
serialize(data)
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @param event [Karafka::Core::Monitoring::Event] recurring task dispatch event
|
|
66
|
+
# @return [String] serialized and compressed event log data
|
|
67
|
+
def log(event)
|
|
68
|
+
task = event[:task]
|
|
69
|
+
|
|
70
|
+
data = {
|
|
71
|
+
schema_version: SCHEMA_VERSION,
|
|
72
|
+
schedule_version: ::Karafka::Pro::RecurringTasks.schedule.version,
|
|
73
|
+
dispatched_at: Time.now.to_f,
|
|
74
|
+
type: 'log',
|
|
75
|
+
task: {
|
|
76
|
+
id: task.id,
|
|
77
|
+
time_taken: event.payload[:time] || -1,
|
|
78
|
+
result: event.payload.key?(:error) ? 'failure' : 'success'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
compress(
|
|
83
|
+
serialize(data)
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
# @param hash [Hash] hash to cast to json
|
|
90
|
+
# @return [String] json hash
|
|
91
|
+
def serialize(hash)
|
|
92
|
+
hash.to_json
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Compresses the provided data
|
|
96
|
+
#
|
|
97
|
+
# @param data [String] data to compress
|
|
98
|
+
# @return [String] compressed data
|
|
99
|
+
def compress(data)
|
|
100
|
+
Zlib::Deflate.deflate(data)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|