mammoth 0.6.0 → 0.7.0
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
- data/CHANGELOG.md +15 -1
- data/README.md +12 -0
- data/config/mammoth.example.yml +17 -0
- data/config/mammoth.schema.json +56 -1
- data/lib/mammoth/application.rb +19 -17
- data/lib/mammoth/capabilities.rb +59 -0
- data/lib/mammoth/cli.rb +1 -2
- data/lib/mammoth/commands/status_command.rb +28 -0
- data/lib/mammoth/dead_letter_commands.rb +1 -5
- data/lib/mammoth/dead_letter_store.rb +4 -0
- data/lib/mammoth/delivered_envelope_store.rb +3 -0
- data/lib/mammoth/destinations/adapter.rb +20 -0
- data/lib/mammoth/destinations/registry.rb +46 -0
- data/lib/mammoth/destinations/webhook_adapter.rb +29 -0
- data/lib/mammoth/node_identity.rb +54 -0
- data/lib/mammoth/observability_server.rb +2 -0
- data/lib/mammoth/operational_state/adapter.rb +37 -0
- data/lib/mammoth/operational_state/registry.rb +46 -0
- data/lib/mammoth/operational_state/sqlite_adapter.rb +44 -0
- data/lib/mammoth/registry.rb +46 -0
- data/lib/mammoth/runtimes/adapter.rb +23 -0
- data/lib/mammoth/runtimes/concurrent_adapter.rb +31 -0
- data/lib/mammoth/runtimes/inline_adapter.rb +40 -0
- data/lib/mammoth/runtimes/registry.rb +46 -0
- data/lib/mammoth/status.rb +55 -13
- data/lib/mammoth/version.rb +1 -1
- data/lib/mammoth.rb +18 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5fe6f64a39d214c62e11a29fccb091f58cd2a1b8038a0bb803c39c4e018b09b3
|
|
4
|
+
data.tar.gz: f557c7288df617029808c327b656a8b7cb1f7689eb7f4b6d99032b01bcc0535e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe49156b57ff2355fd0e78368cd613fa3fa5dcf5d0a5ebf423ec799558b61a70da7529a123f5b254b904a93e7c7355a049b115308eb051e211894b84443f3444
|
|
7
|
+
data.tar.gz: 7b24591aa6f3996e51b5762022f680f212cbb1e8412e0e90ff42e34c3485d9f18fb2f4f4c9b9ca4420a697767400a2d37b6ebc600d203e7c4f5f0fbe5e904da2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Unreleased
|
|
3
|
+
## 0.7.0 Unreleased
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added explicit adapter registries for operational state, destinations, and delivery runtimes.
|
|
8
|
+
- Added the built-in SQLite operational state adapter.
|
|
9
|
+
- Added the built-in webhook destination adapter registration.
|
|
10
|
+
- Added inline and concurrent runtime adapter registration.
|
|
11
|
+
- Added optional node identity configuration for future control-plane integration.
|
|
12
|
+
- Added local capability reporting for state, destination, runtime, and relay features.
|
|
13
|
+
- Added a reusable status command object behind the CLI status command.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Bumped Mammoth version to `0.7.0`.
|
|
4
18
|
|
|
5
19
|
## 0.6.0
|
|
6
20
|
|
data/README.md
CHANGED
|
@@ -57,6 +57,8 @@ https://kanutocd.github.io/mammoth/Mammoth.html
|
|
|
57
57
|
- public Helm chart support
|
|
58
58
|
- unit and e2e test tasks
|
|
59
59
|
- health and metrics endpoints
|
|
60
|
+
- explicit extension registries for state, destination, and runtime adapters
|
|
61
|
+
- node identity and local capability reporting
|
|
60
62
|
|
|
61
63
|
## Boundary
|
|
62
64
|
|
|
@@ -66,6 +68,16 @@ Mammoth does not own pgoutput protocol parsing, value decoding, source
|
|
|
66
68
|
normalization, ordering policy, or runtime execution. Those belong to the
|
|
67
69
|
upstream CDC Ecosystem components.
|
|
68
70
|
|
|
71
|
+
## Extensions
|
|
72
|
+
|
|
73
|
+
Mammoth OSS exposes small adapter registries for future extensions:
|
|
74
|
+
|
|
75
|
+
- operational state adapters
|
|
76
|
+
- destination adapters
|
|
77
|
+
- runtime adapters
|
|
78
|
+
|
|
79
|
+
See [`docs/EXTENSIONS.md`](docs/EXTENSIONS.md).
|
|
80
|
+
|
|
69
81
|
## Configuration
|
|
70
82
|
|
|
71
83
|
Mammoth configuration is YAML-backed and IDE-friendly.
|
data/config/mammoth.example.yml
CHANGED
|
@@ -34,6 +34,19 @@ mammoth:
|
|
|
34
34
|
# production_mammoth
|
|
35
35
|
name: local_mammoth
|
|
36
36
|
|
|
37
|
+
node:
|
|
38
|
+
# Optional local node identity for status output and future control-plane
|
|
39
|
+
# agents. Omit this section to default node_id to the host name and node_name
|
|
40
|
+
# to mammoth.name.
|
|
41
|
+
node_id: local-mammoth-1
|
|
42
|
+
node_name: local-mammoth-dev
|
|
43
|
+
fleet_id: local-dev
|
|
44
|
+
environment: development
|
|
45
|
+
labels:
|
|
46
|
+
region: local
|
|
47
|
+
metadata:
|
|
48
|
+
owner: platform
|
|
49
|
+
|
|
37
50
|
postgres:
|
|
38
51
|
# PostgreSQL host reachable from the Mammoth process or pod.
|
|
39
52
|
#
|
|
@@ -331,6 +344,10 @@ sqlite:
|
|
|
331
344
|
# records here. In Kubernetes, back this path with a PersistentVolumeClaim.
|
|
332
345
|
path: data/mammoth.db
|
|
333
346
|
|
|
347
|
+
operational_state:
|
|
348
|
+
# Mammoth OSS 0.7.0 ships the sqlite operational state adapter.
|
|
349
|
+
adapter: sqlite
|
|
350
|
+
|
|
334
351
|
observability:
|
|
335
352
|
# Bind host for the optional health, readiness, and metrics server.
|
|
336
353
|
host: 0.0.0.0
|
data/config/mammoth.schema.json
CHANGED
|
@@ -40,6 +40,47 @@
|
|
|
40
40
|
},
|
|
41
41
|
"description": "Mammoth instance identity and operator-facing metadata."
|
|
42
42
|
},
|
|
43
|
+
"node": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"properties": {
|
|
47
|
+
"node_id": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"description": "Stable node identifier used by status output and future control-plane agents."
|
|
51
|
+
},
|
|
52
|
+
"node_name": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"description": "Human-readable node name."
|
|
56
|
+
},
|
|
57
|
+
"fleet_id": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"minLength": 1,
|
|
60
|
+
"description": "Optional fleet identifier for grouping Mammoth nodes."
|
|
61
|
+
},
|
|
62
|
+
"environment": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"description": "Optional deployment environment such as development, staging, or production."
|
|
66
|
+
},
|
|
67
|
+
"labels": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"description": "Operator-defined node labels."
|
|
73
|
+
},
|
|
74
|
+
"metadata": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
},
|
|
79
|
+
"description": "Operator-defined node metadata."
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"description": "Optional local node identity. Mammoth OSS uses this for status and future extension surfaces only."
|
|
83
|
+
},
|
|
43
84
|
"postgres": {
|
|
44
85
|
"type": "object",
|
|
45
86
|
"additionalProperties": false,
|
|
@@ -223,7 +264,7 @@
|
|
|
223
264
|
"enum": [
|
|
224
265
|
"webhook"
|
|
225
266
|
],
|
|
226
|
-
"description": "Destination adapter type. Mammoth OSS 0.
|
|
267
|
+
"description": "Destination adapter type. Mammoth OSS 0.7.0 supports webhook destinations."
|
|
227
268
|
},
|
|
228
269
|
"enabled": {
|
|
229
270
|
"type": "boolean",
|
|
@@ -385,6 +426,20 @@
|
|
|
385
426
|
},
|
|
386
427
|
"description": "SQLite operational store settings for checkpoints, attempts, and dead-letter records."
|
|
387
428
|
},
|
|
429
|
+
"operational_state": {
|
|
430
|
+
"type": "object",
|
|
431
|
+
"additionalProperties": false,
|
|
432
|
+
"properties": {
|
|
433
|
+
"adapter": {
|
|
434
|
+
"type": "string",
|
|
435
|
+
"enum": [
|
|
436
|
+
"sqlite"
|
|
437
|
+
],
|
|
438
|
+
"description": "Operational state adapter. Mammoth OSS 0.7.0 ships the sqlite adapter."
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
"description": "Operational state adapter selection. SQLite remains the default OSS adapter."
|
|
442
|
+
},
|
|
388
443
|
"logging": {
|
|
389
444
|
"type": "object",
|
|
390
445
|
"additionalProperties": false,
|
data/lib/mammoth/application.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Mammoth
|
|
|
10
10
|
# injected CDC work source rather than owning upstream CDC source-adapter
|
|
11
11
|
# lifecycle decisions.
|
|
12
12
|
class Application
|
|
13
|
-
attr_reader :config, :
|
|
13
|
+
attr_reader :config, :state_adapter, :consumer, :delivery_worker, :checkpoint_store
|
|
14
14
|
|
|
15
15
|
# @param config [Mammoth::Configuration] loaded configuration
|
|
16
16
|
# @param source [#each, nil] injectable event source for tests and demos
|
|
@@ -18,12 +18,17 @@ module Mammoth
|
|
|
18
18
|
# @param sleeper [#call] retry sleep strategy
|
|
19
19
|
def initialize(config, source: nil, sink: nil, sleeper: Kernel.method(:sleep))
|
|
20
20
|
@config = config
|
|
21
|
-
@
|
|
22
|
-
@checkpoint_store =
|
|
21
|
+
@state_adapter = build_state_adapter
|
|
22
|
+
@checkpoint_store = state_adapter.checkpoint_store
|
|
23
23
|
@consumer = ReplicationConsumer.new(source: source || build_source, delivery_unit: delivery_unit)
|
|
24
24
|
@delivery_worker = sink ? build_delivery_worker(sink: sink, sleeper: sleeper) : build_configured_delivery_worker(sleeper:)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# @return [Mammoth::SQLiteStore] underlying SQLite store for compatibility
|
|
28
|
+
def sqlite_store
|
|
29
|
+
state_adapter.respond_to?(:sqlite_store) ? state_adapter.sqlite_store : nil
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
# Start the application runtime and deliver consumed CDC work.
|
|
28
33
|
#
|
|
29
34
|
# @return [Integer] number of processed work units
|
|
@@ -54,13 +59,7 @@ module Mammoth
|
|
|
54
59
|
private
|
|
55
60
|
|
|
56
61
|
def process_work(runtime, work)
|
|
57
|
-
|
|
58
|
-
runtime.process_many([work])
|
|
59
|
-
elsif transaction_delivery?
|
|
60
|
-
delivery_worker.deliver_transaction(work)
|
|
61
|
-
else
|
|
62
|
-
delivery_worker.deliver(work)
|
|
63
|
-
end
|
|
62
|
+
runtime.process_many([work])
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
def process_batch(runtime, batch)
|
|
@@ -73,9 +72,8 @@ module Mammoth
|
|
|
73
72
|
end
|
|
74
73
|
|
|
75
74
|
def build_runtime
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ConcurrentDeliveryRuntime.new(
|
|
75
|
+
Runtimes::Registry.build(
|
|
76
|
+
runtime_adapter,
|
|
79
77
|
processor: DeliveryProcessor.new(delivery_worker:, delivery_unit: delivery_unit),
|
|
80
78
|
concurrency: runtime_concurrency,
|
|
81
79
|
timeout: runtime_timeout,
|
|
@@ -92,7 +90,7 @@ module Mammoth
|
|
|
92
90
|
config,
|
|
93
91
|
sink: sink,
|
|
94
92
|
checkpoint_store: checkpoint_store,
|
|
95
|
-
dead_letter_store:
|
|
93
|
+
dead_letter_store: state_adapter.dead_letter_store,
|
|
96
94
|
sleeper: sleeper,
|
|
97
95
|
delivery_policy: delivery_policy
|
|
98
96
|
)
|
|
@@ -116,7 +114,7 @@ module Mammoth
|
|
|
116
114
|
|
|
117
115
|
destinations.map.with_index(1) do |destination, index|
|
|
118
116
|
{
|
|
119
|
-
sink:
|
|
117
|
+
sink: Destinations::Registry.build(destination, label: "destinations[#{index - 1}]"),
|
|
120
118
|
delivery_policy: destination_delivery_policy(destination)
|
|
121
119
|
}
|
|
122
120
|
end
|
|
@@ -132,8 +130,12 @@ module Mammoth
|
|
|
132
130
|
}
|
|
133
131
|
end
|
|
134
132
|
|
|
135
|
-
def
|
|
136
|
-
|
|
133
|
+
def build_state_adapter
|
|
134
|
+
OperationalState::Registry.build(operational_state_adapter, config)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def operational_state_adapter
|
|
138
|
+
config.dig("operational_state", "adapter") || "sqlite"
|
|
137
139
|
end
|
|
138
140
|
|
|
139
141
|
def delivery_unit
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Reports local Mammoth capabilities without inspecting private internals.
|
|
5
|
+
class Capabilities
|
|
6
|
+
# Built-in OSS data-plane feature capabilities.
|
|
7
|
+
FEATURES = %w[
|
|
8
|
+
checkpointing
|
|
9
|
+
delivery_ledger
|
|
10
|
+
dead_letters
|
|
11
|
+
replay
|
|
12
|
+
health
|
|
13
|
+
metrics
|
|
14
|
+
routing
|
|
15
|
+
fanout
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
attr_reader :config
|
|
19
|
+
|
|
20
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
21
|
+
def initialize(config)
|
|
22
|
+
@config = config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
26
|
+
# @return [Hash] JSON-friendly capabilities
|
|
27
|
+
def self.call(config)
|
|
28
|
+
new(config).to_h
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Hash] JSON-friendly capabilities
|
|
32
|
+
def to_h
|
|
33
|
+
{
|
|
34
|
+
operational_state: operational_state_adapter,
|
|
35
|
+
destinations: destination_types,
|
|
36
|
+
runtimes: Runtimes::Registry.names,
|
|
37
|
+
runtime: runtime_adapter,
|
|
38
|
+
features: FEATURES
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def operational_state_adapter
|
|
45
|
+
config.dig("operational_state", "adapter") || "sqlite"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def runtime_adapter
|
|
49
|
+
config.dig("runtime", "adapter") || "inline"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def destination_types
|
|
53
|
+
destinations = config.data["destinations"]
|
|
54
|
+
return [config.dig("webhook", "type") || "webhook"] unless destinations
|
|
55
|
+
|
|
56
|
+
destinations.map { |destination| destination.fetch("type", "webhook") }.uniq.sort
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/mammoth/cli.rb
CHANGED
|
@@ -102,8 +102,7 @@ module Mammoth
|
|
|
102
102
|
def status
|
|
103
103
|
config = load_config
|
|
104
104
|
store = SQLiteStore.connect(config.dig("sqlite", "path"))
|
|
105
|
-
|
|
106
|
-
0
|
|
105
|
+
Commands::StatusCommand.new(config, sqlite_store: store).call
|
|
107
106
|
end
|
|
108
107
|
|
|
109
108
|
def start
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Local command objects shared by CLI and future control-plane agents.
|
|
5
|
+
module Commands
|
|
6
|
+
# Reusable local command object for status inspection.
|
|
7
|
+
class StatusCommand
|
|
8
|
+
attr_reader :config, :sqlite_store, :output
|
|
9
|
+
|
|
10
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
11
|
+
# @param sqlite_store [Mammoth::SQLiteStore, nil] optional operational store
|
|
12
|
+
# @param output [#puts] output stream
|
|
13
|
+
def initialize(config, sqlite_store: nil, output: $stdout)
|
|
14
|
+
@config = config
|
|
15
|
+
@sqlite_store = sqlite_store
|
|
16
|
+
@output = output
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Execute the status command.
|
|
20
|
+
#
|
|
21
|
+
# @return [Integer] process-style status code
|
|
22
|
+
def call
|
|
23
|
+
Status.call(config, sqlite_store: sqlite_store, output: output)
|
|
24
|
+
0
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -144,8 +144,6 @@ module Mammoth
|
|
|
144
144
|
|
|
145
145
|
raise ConfigurationError, "unexpected argument #{args[index]}\n#{CLI::USAGE}"
|
|
146
146
|
end
|
|
147
|
-
rescue ArgumentError
|
|
148
|
-
raise ConfigurationError, "invalid dead letter list option"
|
|
149
147
|
rescue IndexError
|
|
150
148
|
raise ConfigurationError, "missing value for dead letter option"
|
|
151
149
|
end
|
|
@@ -188,13 +186,11 @@ module Mammoth
|
|
|
188
186
|
end
|
|
189
187
|
|
|
190
188
|
ids.map do |raw_id|
|
|
191
|
-
id =
|
|
189
|
+
id = raw_id
|
|
192
190
|
row = dead_letter_store.fetch(id)
|
|
193
191
|
raise ConfigurationError, "dead letter not found: #{id}" unless row
|
|
194
192
|
|
|
195
193
|
row
|
|
196
|
-
rescue ArgumentError
|
|
197
|
-
raise ConfigurationError, "dead letter id must be an integer"
|
|
198
194
|
end
|
|
199
195
|
end
|
|
200
196
|
|
|
@@ -100,6 +100,10 @@ module Mammoth
|
|
|
100
100
|
database.get_first_value("SELECT COUNT(*) FROM dead_letters#{where}", values)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
# Count dead letters grouped by destination.
|
|
104
|
+
#
|
|
105
|
+
# @param status [String, nil] optional status filter
|
|
106
|
+
# @return [Array<Hash>] rows with destination_name and count
|
|
103
107
|
def counts_by_destination(status: nil)
|
|
104
108
|
where, values = row_filters(status:)
|
|
105
109
|
database.execute(
|
|
@@ -93,6 +93,9 @@ module Mammoth
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
# Count delivered envelopes grouped by destination.
|
|
97
|
+
#
|
|
98
|
+
# @return [Array<Hash>] rows with destination_name and count
|
|
96
99
|
def counts_by_destination
|
|
97
100
|
database.execute(
|
|
98
101
|
"SELECT destination_name, COUNT(*) AS count FROM delivered_envelopes GROUP BY destination_name"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Destination adapter contracts and registry.
|
|
5
|
+
module Destinations
|
|
6
|
+
# Base contract for destination adapters.
|
|
7
|
+
class Adapter
|
|
8
|
+
# @return [Hash] JSON-friendly adapter capabilities
|
|
9
|
+
def self.capabilities
|
|
10
|
+
{ type: adapter_type }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String] adapter type name
|
|
14
|
+
def self.adapter_type
|
|
15
|
+
type = name.split("::").last.to_s.delete_suffix("Adapter").downcase
|
|
16
|
+
type.empty? ? "adapter" : type
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Destinations
|
|
5
|
+
# Registry for destination adapters.
|
|
6
|
+
module Registry
|
|
7
|
+
class << self
|
|
8
|
+
# Register a destination 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 destination 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 destination sink from config.
|
|
26
|
+
#
|
|
27
|
+
# @param destination [Hash] destination config
|
|
28
|
+
# @param label [String] config label for errors
|
|
29
|
+
# @return [Object] delivery sink
|
|
30
|
+
def build(destination, label:)
|
|
31
|
+
fetch(destination.fetch("type", "webhook")).build(destination, label: label)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Array<String>] registered destination 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("destination")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Destinations
|
|
5
|
+
# Built-in webhook destination adapter.
|
|
6
|
+
class WebhookAdapter < Adapter
|
|
7
|
+
class << self
|
|
8
|
+
# @return [String] adapter type name
|
|
9
|
+
def adapter_type
|
|
10
|
+
"webhook"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Build a webhook sink from destination configuration.
|
|
14
|
+
#
|
|
15
|
+
# @param destination [Hash] destination configuration
|
|
16
|
+
# @param label [String] config path label for errors
|
|
17
|
+
# @return [Mammoth::WebhookSink]
|
|
18
|
+
def build(destination, label:)
|
|
19
|
+
WebhookSink.from_destination_config(destination, label: label)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Hash] JSON-friendly capabilities
|
|
23
|
+
def capabilities
|
|
24
|
+
{ type: adapter_type, delivery_units: %w[event transaction], signing: true, header_env: true }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
|
|
5
|
+
module Mammoth
|
|
6
|
+
# Local Mammoth node identity used by status and future control-plane agents.
|
|
7
|
+
class NodeIdentity
|
|
8
|
+
attr_reader :node_id, :node_name, :fleet_id, :environment, :labels, :metadata
|
|
9
|
+
|
|
10
|
+
# @param node_id [String] stable node identifier
|
|
11
|
+
# @param node_name [String] human-readable node name
|
|
12
|
+
# @param fleet_id [String, nil] optional fleet identifier
|
|
13
|
+
# @param environment [String, nil] optional environment name
|
|
14
|
+
# @param labels [Hash] operator-defined labels
|
|
15
|
+
# @param metadata [Hash] operator-defined metadata
|
|
16
|
+
def initialize(node_id:, node_name:, fleet_id: nil, environment: nil, labels: {}, metadata: {})
|
|
17
|
+
@node_id = node_id
|
|
18
|
+
@node_name = node_name
|
|
19
|
+
@fleet_id = fleet_id
|
|
20
|
+
@environment = environment
|
|
21
|
+
@labels = labels || {}
|
|
22
|
+
@metadata = metadata || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Build identity from Mammoth configuration.
|
|
26
|
+
#
|
|
27
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
28
|
+
# @return [Mammoth::NodeIdentity]
|
|
29
|
+
def self.from_config(config)
|
|
30
|
+
identity = config.data["node"] || {}
|
|
31
|
+
hostname = Socket.gethostname
|
|
32
|
+
new(
|
|
33
|
+
node_id: identity["node_id"] || hostname,
|
|
34
|
+
node_name: identity["node_name"] || config.dig("mammoth", "name") || hostname,
|
|
35
|
+
fleet_id: identity["fleet_id"],
|
|
36
|
+
environment: identity["environment"],
|
|
37
|
+
labels: identity["labels"] || {},
|
|
38
|
+
metadata: identity["metadata"] || {}
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Hash] JSON-friendly node identity
|
|
43
|
+
def to_h
|
|
44
|
+
{
|
|
45
|
+
node_id: node_id,
|
|
46
|
+
node_name: node_name,
|
|
47
|
+
fleet_id: fleet_id,
|
|
48
|
+
environment: environment,
|
|
49
|
+
labels: labels,
|
|
50
|
+
metadata: metadata
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -10,7 +10,9 @@ module Mammoth
|
|
|
10
10
|
# may run it as a sidecar-like process or in a separate process that points at
|
|
11
11
|
# the same SQLite operational database.
|
|
12
12
|
class ObservabilityServer
|
|
13
|
+
# Default bind host for the observability server.
|
|
13
14
|
DEFAULT_HOST = "0.0.0.0"
|
|
15
|
+
# Default TCP port for the observability server.
|
|
14
16
|
DEFAULT_PORT = 9393
|
|
15
17
|
|
|
16
18
|
attr_reader :config, :host, :port, :sqlite_store, :logger, :server
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Operational state adapter contracts and registry.
|
|
5
|
+
module OperationalState
|
|
6
|
+
# Base contract for operational state adapters.
|
|
7
|
+
class Adapter
|
|
8
|
+
# @return [void]
|
|
9
|
+
def initialize; end
|
|
10
|
+
|
|
11
|
+
# @return [Mammoth::CheckpointStore]
|
|
12
|
+
def checkpoint_store
|
|
13
|
+
raise NotImplementedError, "#{self.class} must implement #checkpoint_store"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Mammoth::DeadLetterStore]
|
|
17
|
+
def dead_letter_store
|
|
18
|
+
raise NotImplementedError, "#{self.class} must implement #dead_letter_store"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Mammoth::DeliveredEnvelopeStore]
|
|
22
|
+
def delivered_envelope_store
|
|
23
|
+
raise NotImplementedError, "#{self.class} must implement #delivered_envelope_store"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Hash] JSON-friendly state summary
|
|
27
|
+
def summary
|
|
28
|
+
{
|
|
29
|
+
adapter: "unknown",
|
|
30
|
+
checkpoints: checkpoint_store.count,
|
|
31
|
+
dead_letters: dead_letter_store.count,
|
|
32
|
+
delivered_envelopes: delivered_envelope_store.count
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module OperationalState
|
|
5
|
+
# Registry for operational state adapters.
|
|
6
|
+
module Registry
|
|
7
|
+
class << self
|
|
8
|
+
# Register an operational state 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 an operational state 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 an operational state adapter from config.
|
|
26
|
+
#
|
|
27
|
+
# @param name [String, Symbol] adapter name
|
|
28
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
29
|
+
# @return [Mammoth::OperationalState::Adapter] adapter instance
|
|
30
|
+
def build(name, config)
|
|
31
|
+
fetch(name).from_config(config)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Array<String>] registered operational state 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("operational_state")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module OperationalState
|
|
5
|
+
# SQLite-backed operational state adapter used by Mammoth OSS.
|
|
6
|
+
class SQLiteAdapter < Adapter
|
|
7
|
+
attr_reader :sqlite_store
|
|
8
|
+
|
|
9
|
+
# @param sqlite_store [Mammoth::SQLiteStore] bootstrapped SQLite store
|
|
10
|
+
def initialize(sqlite_store)
|
|
11
|
+
super()
|
|
12
|
+
@sqlite_store = sqlite_store.bootstrap!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Build a SQLite state adapter from Mammoth configuration.
|
|
16
|
+
#
|
|
17
|
+
# @param config [Mammoth::Configuration] loaded configuration
|
|
18
|
+
# @return [Mammoth::OperationalState::SQLiteAdapter]
|
|
19
|
+
def self.from_config(config)
|
|
20
|
+
new(SQLiteStore.connect(config.dig("sqlite", "path")))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Mammoth::CheckpointStore]
|
|
24
|
+
def checkpoint_store
|
|
25
|
+
@checkpoint_store ||= CheckpointStore.new(sqlite_store)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Mammoth::DeadLetterStore]
|
|
29
|
+
def dead_letter_store
|
|
30
|
+
@dead_letter_store ||= DeadLetterStore.new(sqlite_store)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Mammoth::DeliveredEnvelopeStore]
|
|
34
|
+
def delivered_envelope_store
|
|
35
|
+
@delivered_envelope_store ||= DeliveredEnvelopeStore.new(sqlite_store)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Hash] JSON-friendly SQLite state summary
|
|
39
|
+
def summary
|
|
40
|
+
super.merge(adapter: "sqlite", path: sqlite_store.path, tables: sqlite_store.tables)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Small explicit registry for built-in and extension-provided adapters.
|
|
5
|
+
class Registry
|
|
6
|
+
attr_reader :namespace, :entries
|
|
7
|
+
|
|
8
|
+
# @param namespace [String] human-readable registry name for errors
|
|
9
|
+
def initialize(namespace)
|
|
10
|
+
@namespace = namespace
|
|
11
|
+
@entries = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Register an adapter under a stable name.
|
|
15
|
+
#
|
|
16
|
+
# @param name [String, Symbol] adapter name
|
|
17
|
+
# @param adapter [Object] adapter object or class
|
|
18
|
+
# @return [Object] registered adapter
|
|
19
|
+
def register(name, adapter)
|
|
20
|
+
key = normalize_name(name)
|
|
21
|
+
raise ConfigurationError, "#{namespace} adapter already registered: #{key}" if entries.key?(key)
|
|
22
|
+
|
|
23
|
+
entries[key] = adapter
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Fetch a registered adapter.
|
|
27
|
+
#
|
|
28
|
+
# @param name [String, Symbol] adapter name
|
|
29
|
+
# @return [Object] registered adapter
|
|
30
|
+
def fetch(name)
|
|
31
|
+
key = normalize_name(name)
|
|
32
|
+
entries.fetch(key) { raise ConfigurationError, "unknown #{namespace} adapter: #{key}" }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Array<String>] registered adapter names
|
|
36
|
+
def names
|
|
37
|
+
entries.keys.sort
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def normalize_name(name)
|
|
43
|
+
name.to_s
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
# Delivery runtime adapter contracts and registry.
|
|
5
|
+
module Runtimes
|
|
6
|
+
# Base contract for delivery runtime adapters.
|
|
7
|
+
class Adapter
|
|
8
|
+
# @return [void]
|
|
9
|
+
def initialize; end
|
|
10
|
+
|
|
11
|
+
# @return [Hash] JSON-friendly adapter capabilities
|
|
12
|
+
def self.capabilities
|
|
13
|
+
{ type: adapter_type }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [String] adapter type name
|
|
17
|
+
def self.adapter_type
|
|
18
|
+
type = name.split("::").last.to_s.delete_suffix("Adapter").downcase
|
|
19
|
+
type.empty? ? "adapter" : type
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mammoth
|
|
4
|
+
module Runtimes
|
|
5
|
+
# Runtime adapter backed by Mammoth's existing cdc-concurrent wrapper.
|
|
6
|
+
class ConcurrentAdapter < Adapter
|
|
7
|
+
class << self
|
|
8
|
+
# @return [String] adapter type name
|
|
9
|
+
def adapter_type
|
|
10
|
+
"concurrent"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Build a concurrent runtime adapter.
|
|
14
|
+
#
|
|
15
|
+
# @param processor [#process] delivery processor
|
|
16
|
+
# @param concurrency [Integer] worker count
|
|
17
|
+
# @param timeout [Numeric, nil] optional timeout
|
|
18
|
+
# @param preserve_order [Boolean] preserve ordering when supported
|
|
19
|
+
# @return [Mammoth::ConcurrentDeliveryRuntime]
|
|
20
|
+
def build(processor:, concurrency:, timeout:, preserve_order:)
|
|
21
|
+
ConcurrentDeliveryRuntime.new(
|
|
22
|
+
processor: processor,
|
|
23
|
+
concurrency: concurrency,
|
|
24
|
+
timeout: timeout,
|
|
25
|
+
preserve_order: preserve_order
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -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
|
data/lib/mammoth/status.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
data/lib/mammoth/version.rb
CHANGED
data/lib/mammoth.rb
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
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/node_identity"
|
|
8
|
+
require_relative "mammoth/capabilities"
|
|
6
9
|
require_relative "mammoth/status"
|
|
10
|
+
require_relative "mammoth/commands/status_command"
|
|
7
11
|
require_relative "mammoth/observability_snapshot"
|
|
8
12
|
require_relative "mammoth/observability_server"
|
|
9
13
|
require_relative "mammoth/sqlite_store"
|
|
@@ -14,10 +18,20 @@ require_relative "mammoth/event_serializer"
|
|
|
14
18
|
require_relative "mammoth/transaction_envelope_serializer"
|
|
15
19
|
require_relative "mammoth/route_filter"
|
|
16
20
|
require_relative "mammoth/webhook_sink"
|
|
21
|
+
require_relative "mammoth/operational_state/adapter"
|
|
22
|
+
require_relative "mammoth/operational_state/sqlite_adapter"
|
|
23
|
+
require_relative "mammoth/operational_state/registry"
|
|
24
|
+
require_relative "mammoth/destinations/adapter"
|
|
25
|
+
require_relative "mammoth/destinations/webhook_adapter"
|
|
26
|
+
require_relative "mammoth/destinations/registry"
|
|
17
27
|
require_relative "mammoth/delivery_worker"
|
|
18
28
|
require_relative "mammoth/fanout_delivery_worker"
|
|
19
29
|
require_relative "mammoth/delivery_processor"
|
|
20
30
|
require_relative "mammoth/concurrent_delivery_runtime"
|
|
31
|
+
require_relative "mammoth/runtimes/adapter"
|
|
32
|
+
require_relative "mammoth/runtimes/inline_adapter"
|
|
33
|
+
require_relative "mammoth/runtimes/concurrent_adapter"
|
|
34
|
+
require_relative "mammoth/runtimes/registry"
|
|
21
35
|
require_relative "mammoth/sources/postgres"
|
|
22
36
|
require_relative "mammoth/cdc_source"
|
|
23
37
|
require_relative "mammoth/replication_consumer"
|
|
@@ -31,4 +45,8 @@ require_relative "mammoth/cli"
|
|
|
31
45
|
# PostgreSQL change events are normalized, persisted through local operational
|
|
32
46
|
# state, and delivered to webhook destinations.
|
|
33
47
|
module Mammoth
|
|
48
|
+
OperationalState::Registry.register("sqlite", OperationalState::SQLiteAdapter)
|
|
49
|
+
Destinations::Registry.register("webhook", Destinations::WebhookAdapter)
|
|
50
|
+
Runtimes::Registry.register("inline", Runtimes::InlineAdapter)
|
|
51
|
+
Runtimes::Registry.register("concurrent", Runtimes::ConcurrentAdapter)
|
|
34
52
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ken C. Demanawa
|
|
@@ -145,9 +145,11 @@ 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/status_command.rb
|
|
151
153
|
- lib/mammoth/concurrent_delivery_runtime.rb
|
|
152
154
|
- lib/mammoth/configuration.rb
|
|
153
155
|
- lib/mammoth/dead_letter_commands.rb
|
|
@@ -155,13 +157,25 @@ files:
|
|
|
155
157
|
- lib/mammoth/delivered_envelope_store.rb
|
|
156
158
|
- lib/mammoth/delivery_processor.rb
|
|
157
159
|
- lib/mammoth/delivery_worker.rb
|
|
160
|
+
- lib/mammoth/destinations/adapter.rb
|
|
161
|
+
- lib/mammoth/destinations/registry.rb
|
|
162
|
+
- lib/mammoth/destinations/webhook_adapter.rb
|
|
158
163
|
- lib/mammoth/errors.rb
|
|
159
164
|
- lib/mammoth/event_serializer.rb
|
|
160
165
|
- lib/mammoth/fanout_delivery_worker.rb
|
|
166
|
+
- lib/mammoth/node_identity.rb
|
|
161
167
|
- lib/mammoth/observability_server.rb
|
|
162
168
|
- lib/mammoth/observability_snapshot.rb
|
|
169
|
+
- lib/mammoth/operational_state/adapter.rb
|
|
170
|
+
- lib/mammoth/operational_state/registry.rb
|
|
171
|
+
- lib/mammoth/operational_state/sqlite_adapter.rb
|
|
172
|
+
- lib/mammoth/registry.rb
|
|
163
173
|
- lib/mammoth/replication_consumer.rb
|
|
164
174
|
- lib/mammoth/route_filter.rb
|
|
175
|
+
- lib/mammoth/runtimes/adapter.rb
|
|
176
|
+
- lib/mammoth/runtimes/concurrent_adapter.rb
|
|
177
|
+
- lib/mammoth/runtimes/inline_adapter.rb
|
|
178
|
+
- lib/mammoth/runtimes/registry.rb
|
|
165
179
|
- lib/mammoth/sources/postgres.rb
|
|
166
180
|
- lib/mammoth/sql/__bootstrap__.sql
|
|
167
181
|
- lib/mammoth/sqlite_store.rb
|