mammoth 0.6.0 → 0.7.1

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/README.md +16 -0
  4. data/config/mammoth.example.yml +17 -0
  5. data/config/mammoth.schema.json +56 -1
  6. data/lib/mammoth/application.rb +58 -23
  7. data/lib/mammoth/capabilities.rb +59 -0
  8. data/lib/mammoth/cdc_source.rb +1 -1
  9. data/lib/mammoth/cli.rb +16 -24
  10. data/lib/mammoth/commands/bootstrap_command.rb +26 -0
  11. data/lib/mammoth/commands/dead_letters_command.rb +22 -0
  12. data/lib/mammoth/commands/deliver_sample_command.rb +35 -0
  13. data/lib/mammoth/commands/start_command.rb +28 -0
  14. data/lib/mammoth/commands/status_command.rb +28 -0
  15. data/lib/mammoth/commands/validate_command.rb +24 -0
  16. data/lib/mammoth/configuration.rb +66 -7
  17. data/lib/mammoth/dead_letter_commands.rb +17 -9
  18. data/lib/mammoth/dead_letter_store.rb +4 -0
  19. data/lib/mammoth/delivered_envelope_store.rb +3 -0
  20. data/lib/mammoth/destinations/adapter.rb +20 -0
  21. data/lib/mammoth/destinations/registry.rb +46 -0
  22. data/lib/mammoth/destinations/webhook_adapter.rb +29 -0
  23. data/lib/mammoth/lifecycle_hooks.rb +56 -0
  24. data/lib/mammoth/node_identity.rb +54 -0
  25. data/lib/mammoth/observability_server.rb +2 -0
  26. data/lib/mammoth/operational_state/adapter.rb +37 -0
  27. data/lib/mammoth/operational_state/registry.rb +46 -0
  28. data/lib/mammoth/operational_state/sqlite_adapter.rb +44 -0
  29. data/lib/mammoth/registry.rb +46 -0
  30. data/lib/mammoth/runtimes/adapter.rb +23 -0
  31. data/lib/mammoth/runtimes/concurrent_adapter.rb +31 -0
  32. data/lib/mammoth/runtimes/inline_adapter.rb +40 -0
  33. data/lib/mammoth/runtimes/registry.rb +46 -0
  34. data/lib/mammoth/status.rb +55 -13
  35. data/lib/mammoth/version.rb +1 -1
  36. data/lib/mammoth.rb +28 -3
  37. metadata +21 -1
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mammoth
4
+ module Runtimes
5
+ # Inline runtime adapter that processes work in the caller thread.
6
+ class InlineAdapter < Adapter
7
+ attr_reader :processor
8
+
9
+ # @param processor [#process] delivery processor
10
+ def initialize(processor:)
11
+ super()
12
+ @processor = processor
13
+ end
14
+
15
+ # Build an inline runtime.
16
+ #
17
+ # @param processor [#process] delivery processor
18
+ # @return [Mammoth::Runtimes::InlineAdapter]
19
+ def self.build(processor:, **_options)
20
+ new(processor: processor)
21
+ end
22
+
23
+ # @return [String] adapter type name
24
+ def self.adapter_type
25
+ "inline"
26
+ end
27
+
28
+ # @param items [Array<Object>] work units
29
+ # @return [Array<Object>] processor results
30
+ def process_many(items)
31
+ items.map { |item| processor.process(item) }
32
+ end
33
+
34
+ # @return [nil]
35
+ def shutdown
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mammoth
4
+ module Runtimes
5
+ # Registry for delivery runtime adapters.
6
+ module Registry
7
+ class << self
8
+ # Register a runtime adapter.
9
+ #
10
+ # @param name [String, Symbol] adapter name
11
+ # @param adapter [Object] adapter class
12
+ # @return [Object] registered adapter
13
+ def register(name, adapter)
14
+ registry.register(name, adapter)
15
+ end
16
+
17
+ # Fetch a runtime adapter.
18
+ #
19
+ # @param name [String, Symbol] adapter name
20
+ # @return [Object] adapter class
21
+ def fetch(name)
22
+ registry.fetch(name)
23
+ end
24
+
25
+ # Build a runtime adapter.
26
+ #
27
+ # @param name [String, Symbol] adapter name
28
+ # @param options [Hash] adapter-specific build options
29
+ # @return [Object] runtime adapter
30
+ def build(name, **options)
31
+ fetch(name).build(**options)
32
+ end
33
+
34
+ # @return [Array<String>] registered runtime adapter names
35
+ def names
36
+ registry.names
37
+ end
38
+
39
+ # @return [Mammoth::Registry] underlying registry
40
+ def registry
41
+ @registry ||= Mammoth::Registry.new("runtime")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -3,39 +3,73 @@
3
3
  module Mammoth
4
4
  # Builds and prints a boring operational status snapshot.
5
5
  class Status
6
- attr_reader :config, :sqlite_store
6
+ attr_reader :config, :sqlite_store, :output
7
7
 
8
8
  # Print status for a configuration and optional SQLite store.
9
9
  #
10
10
  # @param config [Mammoth::Configuration] loaded configuration
11
11
  # @param sqlite_store [Mammoth::SQLiteStore, nil] operational store
12
12
  # @return [void]
13
- def self.call(config, sqlite_store: nil)
14
- new(config, sqlite_store: sqlite_store).call
13
+ def self.call(config, sqlite_store: nil, output: $stdout)
14
+ new(config, sqlite_store: sqlite_store, output: output).call
15
15
  end
16
16
 
17
17
  # @param config [Mammoth::Configuration] loaded configuration
18
18
  # @param sqlite_store [Mammoth::SQLiteStore, nil] operational store
19
- def initialize(config, sqlite_store: nil)
19
+ # @param output [#puts] output stream
20
+ def initialize(config, sqlite_store: nil, output: $stdout)
20
21
  @config = config
21
22
  @sqlite_store = sqlite_store
23
+ @output = output
22
24
  end
23
25
 
24
26
  # Print the status snapshot.
25
27
  #
26
28
  # @return [void]
27
29
  def call
28
- puts "Mammoth: #{config.dig("mammoth", "name")}"
29
- puts "Replication slot: #{config.dig("replication", "slot")}"
30
- puts "Replication publications: #{Array(config.dig("replication", "publications")).join(", ")}"
31
- puts "Runtime: not started"
32
- puts "SQLite: #{sqlite_path}"
33
- puts "Destinations: #{destination_names.join(", ")}"
30
+ status_lines.each { |line| output.puts(line) }
34
31
  print_store_state if sqlite_store
35
32
  end
36
33
 
37
34
  private
38
35
 
36
+ def status_lines
37
+ identity_lines + replication_lines + runtime_lines + destination_lines + ["SQLite: #{sqlite_path}"]
38
+ end
39
+
40
+ def identity_lines
41
+ [
42
+ "Mammoth: #{config.dig("mammoth", "name")}",
43
+ "Node ID: #{node_identity.node_id}",
44
+ "Node name: #{node_identity.node_name}",
45
+ "Fleet: #{node_identity.fleet_id || "unassigned"}",
46
+ "Environment: #{node_identity.environment || "unspecified"}"
47
+ ]
48
+ end
49
+
50
+ def replication_lines
51
+ [
52
+ "Replication slot: #{config.dig("replication", "slot")}",
53
+ "Replication publications: #{Array(config.dig("replication", "publications")).join(", ")}"
54
+ ]
55
+ end
56
+
57
+ def runtime_lines
58
+ [
59
+ "Runtime: not started",
60
+ "Runtime adapter: #{capabilities.fetch(:runtime)}",
61
+ "Operational state: #{capabilities.fetch(:operational_state)}"
62
+ ]
63
+ end
64
+
65
+ def destination_lines
66
+ [
67
+ "Destinations: #{destination_names.join(", ")}",
68
+ "Destination adapters: #{capabilities.fetch(:destinations).join(", ")}",
69
+ "Features: #{capabilities.fetch(:features).join(", ")}"
70
+ ]
71
+ end
72
+
39
73
  def sqlite_path
40
74
  config.dig("sqlite", "path")
41
75
  end
@@ -47,11 +81,19 @@ module Mammoth
47
81
  [config.dig("webhook", "name")]
48
82
  end
49
83
 
84
+ def node_identity
85
+ @node_identity ||= NodeIdentity.from_config(config)
86
+ end
87
+
88
+ def capabilities
89
+ @capabilities ||= Capabilities.call(config)
90
+ end
91
+
50
92
  def print_store_state
51
93
  store = sqlite_store.bootstrap!
52
- puts "Tables: #{store.tables.join(", ")}"
53
- puts "Checkpoints: #{CheckpointStore.new(store).count}"
54
- puts "Dead Letters: #{DeadLetterStore.new(store).count}"
94
+ output.puts "Tables: #{store.tables.join(", ")}"
95
+ output.puts "Checkpoints: #{CheckpointStore.new(store).count}"
96
+ output.puts "Dead Letters: #{DeadLetterStore.new(store).count}"
55
97
  end
56
98
  end
57
99
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mammoth
4
4
  # Current Mammoth gem version.
5
- VERSION = "0.6.0"
5
+ VERSION = "0.7.1"
6
6
  end
data/lib/mammoth.rb CHANGED
@@ -2,8 +2,18 @@
2
2
 
3
3
  require_relative "mammoth/version"
4
4
  require_relative "mammoth/errors"
5
+ require_relative "mammoth/registry"
5
6
  require_relative "mammoth/configuration"
7
+ require_relative "mammoth/lifecycle_hooks"
8
+ require_relative "mammoth/node_identity"
9
+ require_relative "mammoth/capabilities"
6
10
  require_relative "mammoth/status"
11
+ require_relative "mammoth/commands/validate_command"
12
+ require_relative "mammoth/commands/bootstrap_command"
13
+ require_relative "mammoth/commands/status_command"
14
+ require_relative "mammoth/commands/start_command"
15
+ require_relative "mammoth/commands/deliver_sample_command"
16
+ require_relative "mammoth/commands/dead_letters_command"
7
17
  require_relative "mammoth/observability_snapshot"
8
18
  require_relative "mammoth/observability_server"
9
19
  require_relative "mammoth/sqlite_store"
@@ -14,10 +24,20 @@ require_relative "mammoth/event_serializer"
14
24
  require_relative "mammoth/transaction_envelope_serializer"
15
25
  require_relative "mammoth/route_filter"
16
26
  require_relative "mammoth/webhook_sink"
27
+ require_relative "mammoth/operational_state/adapter"
28
+ require_relative "mammoth/operational_state/sqlite_adapter"
29
+ require_relative "mammoth/operational_state/registry"
30
+ require_relative "mammoth/destinations/adapter"
31
+ require_relative "mammoth/destinations/webhook_adapter"
32
+ require_relative "mammoth/destinations/registry"
17
33
  require_relative "mammoth/delivery_worker"
18
34
  require_relative "mammoth/fanout_delivery_worker"
19
35
  require_relative "mammoth/delivery_processor"
20
36
  require_relative "mammoth/concurrent_delivery_runtime"
37
+ require_relative "mammoth/runtimes/adapter"
38
+ require_relative "mammoth/runtimes/inline_adapter"
39
+ require_relative "mammoth/runtimes/concurrent_adapter"
40
+ require_relative "mammoth/runtimes/registry"
21
41
  require_relative "mammoth/sources/postgres"
22
42
  require_relative "mammoth/cdc_source"
23
43
  require_relative "mammoth/replication_consumer"
@@ -27,8 +47,13 @@ require_relative "mammoth/cli"
27
47
 
28
48
  # Mammoth is a self-hosted PostgreSQL event relay.
29
49
  #
30
- # Mammoth v0.1.0 focuses on a deliberately small, boring product slice:
31
- # PostgreSQL change events are normalized, persisted through local operational
32
- # state, and delivered to webhook destinations.
50
+ # Mammoth 0.7.x is a single-node PostgreSQL CDC relay with webhook fanout,
51
+ # replayable operational SQLite state, health/metrics endpoints, and explicit
52
+ # extension contracts for state, destination, runtime, lifecycle, configuration,
53
+ # and local command integrations.
33
54
  module Mammoth
55
+ OperationalState::Registry.register("sqlite", OperationalState::SQLiteAdapter)
56
+ Destinations::Registry.register("webhook", Destinations::WebhookAdapter)
57
+ Runtimes::Registry.register("inline", Runtimes::InlineAdapter)
58
+ Runtimes::Registry.register("concurrent", Runtimes::ConcurrentAdapter)
34
59
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mammoth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken C. Demanawa
@@ -145,9 +145,16 @@ files:
145
145
  - exe/mammoth
146
146
  - lib/mammoth.rb
147
147
  - lib/mammoth/application.rb
148
+ - lib/mammoth/capabilities.rb
148
149
  - lib/mammoth/cdc_source.rb
149
150
  - lib/mammoth/checkpoint_store.rb
150
151
  - lib/mammoth/cli.rb
152
+ - lib/mammoth/commands/bootstrap_command.rb
153
+ - lib/mammoth/commands/dead_letters_command.rb
154
+ - lib/mammoth/commands/deliver_sample_command.rb
155
+ - lib/mammoth/commands/start_command.rb
156
+ - lib/mammoth/commands/status_command.rb
157
+ - lib/mammoth/commands/validate_command.rb
151
158
  - lib/mammoth/concurrent_delivery_runtime.rb
152
159
  - lib/mammoth/configuration.rb
153
160
  - lib/mammoth/dead_letter_commands.rb
@@ -155,13 +162,26 @@ files:
155
162
  - lib/mammoth/delivered_envelope_store.rb
156
163
  - lib/mammoth/delivery_processor.rb
157
164
  - lib/mammoth/delivery_worker.rb
165
+ - lib/mammoth/destinations/adapter.rb
166
+ - lib/mammoth/destinations/registry.rb
167
+ - lib/mammoth/destinations/webhook_adapter.rb
158
168
  - lib/mammoth/errors.rb
159
169
  - lib/mammoth/event_serializer.rb
160
170
  - lib/mammoth/fanout_delivery_worker.rb
171
+ - lib/mammoth/lifecycle_hooks.rb
172
+ - lib/mammoth/node_identity.rb
161
173
  - lib/mammoth/observability_server.rb
162
174
  - lib/mammoth/observability_snapshot.rb
175
+ - lib/mammoth/operational_state/adapter.rb
176
+ - lib/mammoth/operational_state/registry.rb
177
+ - lib/mammoth/operational_state/sqlite_adapter.rb
178
+ - lib/mammoth/registry.rb
163
179
  - lib/mammoth/replication_consumer.rb
164
180
  - lib/mammoth/route_filter.rb
181
+ - lib/mammoth/runtimes/adapter.rb
182
+ - lib/mammoth/runtimes/concurrent_adapter.rb
183
+ - lib/mammoth/runtimes/inline_adapter.rb
184
+ - lib/mammoth/runtimes/registry.rb
165
185
  - lib/mammoth/sources/postgres.rb
166
186
  - lib/mammoth/sql/__bootstrap__.sql
167
187
  - lib/mammoth/sqlite_store.rb