cosmos 5.0.3 → 5.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/cosmos +1 -1
- data/lib/cosmos/api/api.rb +1 -25
- data/lib/cosmos/api/cmd_api.rb +6 -6
- data/lib/cosmos/api/config_api.rb +10 -4
- data/lib/cosmos/api/limits_api.rb +1 -1
- data/lib/cosmos/api/settings_api.rb +19 -7
- data/lib/cosmos/api/target_api.rb +2 -2
- data/lib/cosmos/api/tlm_api.rb +8 -8
- data/lib/cosmos/config/config_parser.rb +2 -2
- data/lib/cosmos/config/meta_config_parser.rb +1 -1
- data/lib/cosmos/logs/log_writer.rb +2 -2
- data/lib/cosmos/microservices/decom_microservice.rb +1 -1
- data/lib/cosmos/microservices/interface_microservice.rb +0 -1
- data/lib/cosmos/microservices/microservice.rb +2 -2
- data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
- data/lib/cosmos/models/cvt_model.rb +6 -6
- data/lib/cosmos/models/gem_model.rb +2 -2
- data/lib/cosmos/models/info_model.rb +1 -1
- data/lib/cosmos/models/interface_status_model.rb +1 -1
- data/lib/cosmos/models/metadata_model.rb +42 -216
- data/lib/cosmos/models/metric_model.rb +2 -2
- data/lib/cosmos/models/microservice_model.rb +1 -1
- data/lib/cosmos/models/microservice_status_model.rb +1 -1
- data/lib/cosmos/models/model.rb +16 -16
- data/lib/cosmos/models/note_model.rb +124 -0
- data/lib/cosmos/models/ping_model.rb +2 -1
- data/lib/cosmos/models/plugin_model.rb +1 -1
- data/lib/cosmos/models/process_status_model.rb +1 -1
- data/lib/cosmos/models/scope_model.rb +9 -6
- data/lib/cosmos/models/settings_model.rb +55 -0
- data/lib/cosmos/models/sorted_model.rb +165 -0
- data/lib/cosmos/models/target_model.rb +20 -20
- data/lib/cosmos/models/tool_config_model.rb +38 -0
- data/lib/cosmos/models/tool_model.rb +1 -1
- data/lib/cosmos/models/widget_model.rb +1 -1
- data/lib/cosmos/operators/microservice_operator.rb +2 -1
- data/lib/cosmos/script/calendar.rb +26 -15
- data/lib/cosmos/system/system.rb +2 -1
- data/lib/cosmos/top_level.rb +5 -1
- data/lib/cosmos/topics/autonomic_topic.rb +2 -2
- data/lib/cosmos/topics/calendar_topic.rb +1 -1
- data/lib/cosmos/topics/command_decom_topic.rb +31 -1
- data/lib/cosmos/topics/command_topic.rb +6 -4
- data/lib/cosmos/topics/interface_topic.rb +8 -8
- data/lib/cosmos/topics/limits_event_topic.rb +5 -3
- data/lib/cosmos/topics/notifications_topic.rb +1 -1
- data/lib/cosmos/topics/router_topic.rb +9 -9
- data/lib/cosmos/topics/telemetry_decom_topic.rb +1 -1
- data/lib/cosmos/topics/telemetry_topic.rb +1 -1
- data/lib/cosmos/topics/timeline_topic.rb +1 -1
- data/lib/cosmos/topics/topic.rb +21 -16
- data/lib/cosmos/utilities/logger.rb +3 -3
- data/lib/cosmos/utilities/metric.rb +32 -26
- data/lib/cosmos/utilities/s3.rb +1 -1
- data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
- data/lib/cosmos/utilities/store.rb +1 -0
- data/lib/cosmos/utilities/store_autoload.rb +27 -126
- data/lib/cosmos/version.rb +5 -5
- metadata +7 -4
- data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -34,38 +34,38 @@ module Cosmos
|
|
34
34
|
|
35
35
|
def self.receive_commands(interface, scope:)
|
36
36
|
while true
|
37
|
-
|
37
|
+
Topic.read_topics(InterfaceTopic.topics(interface, scope: scope)) do |topic, msg_id, msg_hash, redis|
|
38
38
|
result = yield topic, msg_hash
|
39
39
|
ack_topic = topic.split("__")
|
40
40
|
ack_topic[1] = 'ACK' + ack_topic[1]
|
41
41
|
ack_topic = ack_topic.join("__")
|
42
|
-
|
42
|
+
Topic.write_topic(ack_topic, { 'result' => result, 'id' => msg_id }, '*', 100)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
def self.write_raw(interface_name, data, scope:)
|
48
|
-
|
48
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'raw' => data }, '*', 100)
|
49
49
|
end
|
50
50
|
|
51
51
|
def self.connect_interface(interface_name, scope:)
|
52
|
-
|
52
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'connect' => 'true' }, '*', 100)
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.disconnect_interface(interface_name, scope:)
|
56
|
-
|
56
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'disconnect' => 'true' }, '*', 100)
|
57
57
|
end
|
58
58
|
|
59
59
|
def self.start_raw_logging(interface_name, scope:)
|
60
|
-
|
60
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_raw' => 'true' }, '*', 100)
|
61
61
|
end
|
62
62
|
|
63
63
|
def self.stop_raw_logging(interface_name, scope:)
|
64
|
-
|
64
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface_name}", { 'log_raw' => 'false' }, '*', 100)
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.shutdown(interface, scope:)
|
68
|
-
|
68
|
+
Topic.write_topic("{#{scope}__CMD}INTERFACE__#{interface.name}", { 'shutdown' => 'true' }, '*', 100)
|
69
69
|
sleep 1 # Give some time for the interface to shutdown
|
70
70
|
InterfaceTopic.clear_topics(InterfaceTopic.topics(interface, scope: scope))
|
71
71
|
end
|
@@ -52,13 +52,13 @@ module Cosmos
|
|
52
52
|
raise "Invalid limits event type '#{event[:type]}'"
|
53
53
|
end
|
54
54
|
|
55
|
-
|
55
|
+
Topic.write_topic("#{scope}__cosmos_limits_events", event, '*', 1000)
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.read(offset = nil, count: 100, scope:)
|
59
59
|
topic = "#{scope}__cosmos_limits_events"
|
60
60
|
if offset
|
61
|
-
result =
|
61
|
+
result = Topic.read_topics([topic], [offset], nil, count)
|
62
62
|
if result.empty?
|
63
63
|
[] # We want to return an empty array rather than an empty hash
|
64
64
|
else
|
@@ -67,7 +67,9 @@ module Cosmos
|
|
67
67
|
result[topic]
|
68
68
|
end
|
69
69
|
else
|
70
|
-
|
70
|
+
result = Topic.get_newest_message(topic)
|
71
|
+
return [result] if result
|
72
|
+
return []
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
@@ -22,7 +22,7 @@ require 'cosmos/topics/topic'
|
|
22
22
|
module Cosmos
|
23
23
|
class NotificationsTopic < Topic
|
24
24
|
def self.write_notification(notification, scope:)
|
25
|
-
|
25
|
+
Topic.write_topic("#{scope}__cosmos_notifications", notification)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -36,13 +36,13 @@ module Cosmos
|
|
36
36
|
|
37
37
|
def self.receive_telemetry(router, scope:)
|
38
38
|
while true
|
39
|
-
|
39
|
+
Topic.read_topics(RouterTopic.topics(router, scope: scope)) do |topic, msg_id, msg_hash, redis|
|
40
40
|
result = yield topic, msg_hash
|
41
41
|
if /CMD}ROUTER/.match?(topic)
|
42
42
|
ack_topic = topic.split("__")
|
43
43
|
ack_topic[1] = 'ACK' + ack_topic[1]
|
44
44
|
ack_topic = ack_topic.join("__")
|
45
|
-
|
45
|
+
Topic.write_topic(ack_topic, { 'result' => result }, msg_id, 100)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -51,33 +51,33 @@ module Cosmos
|
|
51
51
|
def self.route_command(packet, target_names, scope:)
|
52
52
|
if packet.identified?
|
53
53
|
topic = "{#{scope}__CMD}TARGET__#{packet.target_name}"
|
54
|
-
|
54
|
+
Topic.write_topic(topic, { 'target_name' => packet.target_name, 'cmd_name' => packet.packet_name, 'cmd_buffer' => packet.buffer(false) }, '*', 100)
|
55
55
|
elsif target_names.length == 1
|
56
56
|
topic = "{#{scope}__CMD}TARGET__#{target_names[0]}"
|
57
|
-
|
57
|
+
Topic.write_topic(topic, { 'target_name' => packet.target_name, 'cmd_name' => 'UNKNOWN', 'cmd_buffer' => packet.buffer(false) }, '*', 100)
|
58
58
|
else
|
59
59
|
raise "No route for command: #{packet.target_name} #{packet.packet_name}"
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
def self.connect_router(router_name, scope:)
|
64
|
-
|
64
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'connect' => true }, '*', 100)
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.disconnect_router(router_name, scope:)
|
68
|
-
|
68
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'disconnect' => true }, '*', 100)
|
69
69
|
end
|
70
70
|
|
71
71
|
def self.start_raw_logging(router_name, scope:)
|
72
|
-
|
72
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_raw' => 'true' }, '*', 100)
|
73
73
|
end
|
74
74
|
|
75
75
|
def self.stop_raw_logging(router_name, scope:)
|
76
|
-
|
76
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router_name}", { 'log_raw' => 'false' }, '*', 100)
|
77
77
|
end
|
78
78
|
|
79
79
|
def self.shutdown(router, scope:)
|
80
|
-
|
80
|
+
Topic.write_topic("{#{scope}__CMD}ROUTER__#{router.name}", { 'shutdown' => 'true' }, '*', 100)
|
81
81
|
sleep 1 # Give some time for the interface to shutdown
|
82
82
|
RouterTopic.clear_topics(RouterTopic.topics(router, scope: scope))
|
83
83
|
end
|
@@ -43,7 +43,7 @@ module Cosmos
|
|
43
43
|
:received_count => packet.received_count,
|
44
44
|
:json_data => JSON.generate(json_hash.as_json),
|
45
45
|
}
|
46
|
-
|
46
|
+
Topic.write_topic("#{scope}__DECOM__{#{packet.target_name}}__#{packet.packet_name}", msg_hash, id)
|
47
47
|
# Also update the current value table with the latest decommutated data
|
48
48
|
CvtModel.set(json_hash, target_name: packet.target_name, packet_name: packet.packet_name, scope: scope)
|
49
49
|
end
|
@@ -30,7 +30,7 @@ module Cosmos
|
|
30
30
|
:received_count => packet.received_count,
|
31
31
|
:buffer => packet.buffer(false),
|
32
32
|
}
|
33
|
-
|
33
|
+
Topic.write_topic("#{scope}__TELEMETRY__{#{packet.target_name}}__#{packet.packet_name}", msg_hash)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/lib/cosmos/topics/topic.rb
CHANGED
@@ -21,28 +21,33 @@ require 'cosmos/utilities/store'
|
|
21
21
|
|
22
22
|
module Cosmos
|
23
23
|
class Topic
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
if RUBY_VERSION < "3"
|
25
|
+
# Delegate all unknown class methods to delegate to the EphemeralStore
|
26
|
+
def self.method_missing(message, *args, &block)
|
27
|
+
EphemeralStore.public_send(message, *args, &block)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
# Delegate all unknown class methods to delegate to the EphemeralStore
|
31
|
+
def self.method_missing(message, *args, **kwargs, &block)
|
32
|
+
EphemeralStore.public_send(message, *args, **kwargs, &block)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
def self.clear_topics(topics, maxlen = 0)
|
33
|
-
topics.each
|
34
|
-
Store.xtrim(topic, maxlen)
|
35
|
-
end
|
37
|
+
topics.each { |topic| EphemeralStore.xtrim(topic, maxlen) }
|
36
38
|
end
|
37
39
|
|
38
40
|
def self.topics(scope, key)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
EphemeralStore
|
42
|
+
.scan_each(match: "#{scope}__#{key}__*", type: 'stream', count: 100)
|
43
|
+
.to_a # Change the enumerator into an array
|
44
|
+
.uniq # Scan can return duplicates so ensure unique
|
45
|
+
.sort # Sort not entirely necessary but nice
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.get_cnt(topic)
|
49
|
+
_, packet = EphemeralStore.get_newest_message(topic)
|
50
|
+
packet ? packet["received_count"].to_i : 0
|
46
51
|
end
|
47
52
|
end
|
48
53
|
end
|
@@ -19,7 +19,7 @@
|
|
19
19
|
|
20
20
|
require 'cosmos/core_ext/class'
|
21
21
|
require 'cosmos/core_ext/time'
|
22
|
-
require 'cosmos/
|
22
|
+
require 'cosmos/topics/topic'
|
23
23
|
require 'socket'
|
24
24
|
require 'logger'
|
25
25
|
require 'time'
|
@@ -174,11 +174,11 @@ module Cosmos
|
|
174
174
|
puts data.to_json if @stdout
|
175
175
|
unless @no_store
|
176
176
|
if scope
|
177
|
-
|
177
|
+
Topic.write_topic("#{scope}__cosmos_log_messages", data)
|
178
178
|
else
|
179
179
|
# The base cosmos_log_messages doesn't have an associated logger
|
180
180
|
# so it must be limited to prevent unbounded stream growth
|
181
|
-
|
181
|
+
Topic.write_topic("cosmos_log_messages", data, '*', 1000)
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
@@ -18,6 +18,7 @@
|
|
18
18
|
# copyright holder
|
19
19
|
|
20
20
|
require 'cosmos/models/metric_model'
|
21
|
+
require 'thread'
|
21
22
|
|
22
23
|
module Cosmos
|
23
24
|
class Metric
|
@@ -48,6 +49,7 @@ module Cosmos
|
|
48
49
|
@scope = scope
|
49
50
|
@microservice = microservice
|
50
51
|
@size = 5000
|
52
|
+
@mutex = Mutex.new
|
51
53
|
end
|
52
54
|
|
53
55
|
def add_sample(name:, value:, labels:)
|
@@ -69,15 +71,17 @@ module Cosmos
|
|
69
71
|
# the value is added to @items and the count of the value is increased
|
70
72
|
# if the count of the values exceed the size of the array it sets the
|
71
73
|
# count back to zero and the array will over write older data.
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
@mutex.synchronize do
|
75
|
+
key = "#{name}|" + labels.map { |k, v| "#{k}=#{v}" }.join(',')
|
76
|
+
if not @items.has_key?(key)
|
77
|
+
Logger.debug("new data for #{@scope}, #{key}")
|
78
|
+
@items[key] = { 'values' => Array.new(@size), 'count' => 0 }
|
79
|
+
end
|
80
|
+
count = @items[key]['count']
|
81
|
+
# Logger.info("adding data for #{@scope}, #{count} #{key}, #{value}")
|
82
|
+
@items[key]['values'][count] = value
|
83
|
+
@items[key]['count'] = count + 1 >= @size ? 0 : count + 1
|
76
84
|
end
|
77
|
-
count = @items[key]['count']
|
78
|
-
# Logger.info("adding data for #{@scope}, #{count} #{key}, #{value}")
|
79
|
-
@items[key]['values'][count] = value
|
80
|
-
@items[key]['count'] = count + 1 >= @size ? 0 : count + 1
|
81
85
|
end
|
82
86
|
|
83
87
|
def percentile(sorted_values, percentile)
|
@@ -106,24 +110,26 @@ module Cosmos
|
|
106
110
|
# array. to store the array as the value with the metric name again joined
|
107
111
|
# with the @microservice and @scope.
|
108
112
|
Logger.debug("#{@microservice} #{@scope} sending metrics to redis, #{@items.length}") if @items.length > 0
|
109
|
-
@
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
113
|
+
@mutex.synchronize do
|
114
|
+
@items.each do |key, values|
|
115
|
+
label_list = []
|
116
|
+
name, labels = key.split('|')
|
117
|
+
metric_labels = labels.nil? ? {} : labels.split(',').map { |x| x.split('=') }.map { |k, v| { k => v } }.reduce({}, :merge)
|
118
|
+
sorted_values = values['values'].compact.sort
|
119
|
+
for percentile_value in [10, 50, 90, 95, 99]
|
120
|
+
percentile_result = percentile(sorted_values, percentile_value)
|
121
|
+
labels = metric_labels.clone.merge({ 'scope' => @scope, 'microservice' => @microservice })
|
122
|
+
labels['percentile'] = percentile_value
|
123
|
+
labels['metric__value'] = percentile_result
|
124
|
+
label_list.append(labels)
|
125
|
+
end
|
126
|
+
begin
|
127
|
+
Logger.debug("sending metrics summary to redis key: #{@microservice}")
|
128
|
+
metric = MetricModel.new(name: @microservice, scope: @scope, metric_name: name, label_list: label_list)
|
129
|
+
metric.create(force: true)
|
130
|
+
rescue RuntimeError
|
131
|
+
Logger.error("failed attempt to update metric, #{key}, #{name} #{@scope}")
|
132
|
+
end
|
127
133
|
end
|
128
134
|
end
|
129
135
|
end
|
data/lib/cosmos/utilities/s3.rb
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
# copyright holder
|
19
19
|
|
20
20
|
require 'fileutils'
|
21
|
+
require 'tmpdir'
|
21
22
|
require 'cosmos'
|
22
23
|
require 'cosmos/utilities/s3'
|
23
24
|
|
@@ -48,7 +49,7 @@ class S3File
|
|
48
49
|
|
49
50
|
def retrieve
|
50
51
|
local_path = "#{S3FileCache.instance.cache_dir}/#{File.basename(@s3_path)}"
|
51
|
-
Cosmos::Logger.
|
52
|
+
Cosmos::Logger.debug "Retrieving #{@s3_path} from logs bucket"
|
52
53
|
@rubys3_client.get_object(bucket: "logs", key: @s3_path, response_target: local_path)
|
53
54
|
if File.exist?(local_path)
|
54
55
|
@size = File.size(local_path)
|
@@ -57,6 +58,7 @@ class S3File
|
|
57
58
|
rescue => err
|
58
59
|
@error = err
|
59
60
|
Cosmos::Logger.error "Failed to retrieve #{@s3_path}\n#{err.formatted}"
|
61
|
+
raise err
|
60
62
|
end
|
61
63
|
|
62
64
|
def reserve
|
@@ -165,11 +167,11 @@ class S3FileCache
|
|
165
167
|
end
|
166
168
|
|
167
169
|
# Create local file cache location
|
168
|
-
@cache_dir =
|
170
|
+
@cache_dir = Dir.mktmpdir
|
169
171
|
FileUtils.mkdir_p(@cache_dir)
|
170
|
-
|
171
|
-
|
172
|
-
|
172
|
+
at_exit do
|
173
|
+
FileUtils.remove_dir(@cache_dir, true)
|
174
|
+
end
|
173
175
|
|
174
176
|
@cached_files = S3FileCollection.new
|
175
177
|
|
@@ -178,7 +180,11 @@ class S3FileCache
|
|
178
180
|
file = @cached_files.get_next_to_retrieve
|
179
181
|
# Cosmos::Logger.debug "Next file: #{file}"
|
180
182
|
if file and (file.size + @cached_files.current_disk_usage()) <= @max_disk_usage
|
181
|
-
|
183
|
+
begin
|
184
|
+
file.retrieve
|
185
|
+
rescue
|
186
|
+
# Will be automatically retried
|
187
|
+
end
|
182
188
|
else
|
183
189
|
sleep(1)
|
184
190
|
end
|
@@ -31,7 +31,7 @@ end
|
|
31
31
|
module Cosmos
|
32
32
|
class Store
|
33
33
|
# Variable that holds the singleton instance
|
34
|
-
|
34
|
+
@instance = nil
|
35
35
|
|
36
36
|
# Mutex used to ensure that only one instance is created
|
37
37
|
@@instance_mutex = Mutex.new
|
@@ -42,11 +42,11 @@ module Cosmos
|
|
42
42
|
# Get the singleton instance
|
43
43
|
def self.instance(pool_size = 100)
|
44
44
|
# Logger.level = Logger::DEBUG
|
45
|
-
return
|
45
|
+
return @instance if @instance
|
46
46
|
|
47
47
|
@@instance_mutex.synchronize do
|
48
|
-
|
49
|
-
return
|
48
|
+
@instance ||= self.new(pool_size)
|
49
|
+
return @instance
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -87,44 +87,10 @@ module Cosmos
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
def get_cmd_item(target_name, packet_name, param_name, type: :WITH_UNITS, scope: $cosmos_scope)
|
91
|
-
msg_id, msg_hash = read_topic_last("#{scope}__DECOMCMD__{#{target_name}}__#{packet_name}")
|
92
|
-
if msg_id
|
93
|
-
# TODO: We now have these reserved items directly on command packets
|
94
|
-
# Do we still calculate from msg_hash['time'] or use the times directly?
|
95
|
-
#
|
96
|
-
# if param_name == 'RECEIVED_TIMESECONDS' || param_name == 'PACKET_TIMESECONDS'
|
97
|
-
# Time.from_nsec_from_epoch(msg_hash['time'].to_i).to_f
|
98
|
-
# elsif param_name == 'RECEIVED_TIMEFORMATTED' || param_name == 'PACKET_TIMEFORMATTED'
|
99
|
-
# Time.from_nsec_from_epoch(msg_hash['time'].to_i).formatted
|
100
|
-
if param_name == 'RECEIVED_COUNT'
|
101
|
-
msg_hash['received_count'].to_i
|
102
|
-
else
|
103
|
-
json = msg_hash['json_data']
|
104
|
-
hash = JSON.parse(json)
|
105
|
-
# Start from the most complex down to the basic raw value
|
106
|
-
value = hash["#{param_name}__U"]
|
107
|
-
return value if value && type == :WITH_UNITS
|
108
|
-
|
109
|
-
value = hash["#{param_name}__F"]
|
110
|
-
return value if value && (type == :WITH_UNITS || type == :FORMATTED)
|
111
|
-
|
112
|
-
value = hash["#{param_name}__C"]
|
113
|
-
return value if value && (type == :WITH_UNITS || type == :FORMATTED || type == :CONVERTED)
|
114
|
-
|
115
|
-
return hash[param_name]
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
90
|
###########################################################################
|
121
91
|
# Stream APIs
|
122
92
|
###########################################################################
|
123
93
|
|
124
|
-
def self.initialize_streams(topics)
|
125
|
-
self.instance.initialize_streams(topics)
|
126
|
-
end
|
127
|
-
|
128
94
|
def initialize_streams(topics)
|
129
95
|
@redis_pool.with do |redis|
|
130
96
|
topics.each do |topic|
|
@@ -134,48 +100,18 @@ module Cosmos
|
|
134
100
|
end
|
135
101
|
end
|
136
102
|
|
137
|
-
def self.get_oldest_message(topic)
|
138
|
-
self.instance.get_oldest_message(topic)
|
139
|
-
end
|
140
|
-
|
141
103
|
def get_oldest_message(topic)
|
142
104
|
@redis_pool.with do |redis|
|
143
105
|
result = redis.xrange(topic, count: 1)
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
def self.get_newest_message(topic)
|
149
|
-
self.instance.get_newest_message(topic)
|
150
|
-
end
|
151
|
-
|
152
|
-
def get_newest_message(topic)
|
153
|
-
@redis_pool.with do |redis|
|
154
|
-
result = redis.xrevrange(topic, count: 1)
|
155
|
-
return result[0]
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.get_last_offset(topic)
|
160
|
-
self.instance.get_last_offset(topic)
|
161
|
-
end
|
162
|
-
|
163
|
-
def get_last_offset(topic)
|
164
|
-
@redis_pool.with do |redis|
|
165
|
-
result = redis.xrevrange(topic, count: 1)
|
166
|
-
if result and result[0] and result[0][0]
|
167
|
-
result[0][0]
|
106
|
+
if result and result.length > 0
|
107
|
+
return result[0]
|
168
108
|
else
|
169
|
-
|
109
|
+
return nil
|
170
110
|
end
|
171
111
|
end
|
172
112
|
end
|
173
113
|
|
174
|
-
def
|
175
|
-
self.instance.read_topic_last(topic)
|
176
|
-
end
|
177
|
-
|
178
|
-
def read_topic_last(topic)
|
114
|
+
def get_newest_message(topic)
|
179
115
|
@redis_pool.with do |redis|
|
180
116
|
# Default in xrevrange is range end '+', start '-' which means get all
|
181
117
|
# elements from higher ID to lower ID and since we're limiting to 1
|
@@ -189,18 +125,15 @@ module Cosmos
|
|
189
125
|
end
|
190
126
|
end
|
191
127
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
def self.update_topic_offsets(topics)
|
203
|
-
self.instance.update_topic_offsets(topics)
|
128
|
+
def get_last_offset(topic)
|
129
|
+
@redis_pool.with do |redis|
|
130
|
+
result = redis.xrevrange(topic, count: 1)
|
131
|
+
if result and result[0] and result[0][0]
|
132
|
+
result[0][0]
|
133
|
+
else
|
134
|
+
"0-0"
|
135
|
+
end
|
136
|
+
end
|
204
137
|
end
|
205
138
|
|
206
139
|
def update_topic_offsets(topics)
|
@@ -221,15 +154,12 @@ module Cosmos
|
|
221
154
|
return offsets
|
222
155
|
end
|
223
156
|
|
224
|
-
def self.read_topics(topics, offsets = nil, timeout_ms = 1000, &block)
|
225
|
-
self.instance.read_topics(topics, offsets, timeout_ms, &block)
|
226
|
-
end
|
227
157
|
unless $enterprise_cosmos
|
228
|
-
def read_topics(topics, offsets = nil, timeout_ms = 1000)
|
158
|
+
def read_topics(topics, offsets = nil, timeout_ms = 1000, count = nil)
|
229
159
|
# Logger.debug "read_topics: #{topics}, #{offsets} pool:#{@redis_pool}"
|
230
160
|
@redis_pool.with do |redis|
|
231
161
|
offsets = update_topic_offsets(topics) unless offsets
|
232
|
-
result = redis.xread(topics, offsets, block: timeout_ms)
|
162
|
+
result = redis.xread(topics, offsets, block: timeout_ms, count: count)
|
233
163
|
if result and result.length > 0
|
234
164
|
result.each do |topic, messages|
|
235
165
|
messages.each do |msg_id, msg_hash|
|
@@ -244,26 +174,6 @@ module Cosmos
|
|
244
174
|
end
|
245
175
|
end
|
246
176
|
|
247
|
-
# Add new entry to the redis stream.
|
248
|
-
# > https://www.rubydoc.info/github/redis/redis-rb/Redis:xadd
|
249
|
-
#
|
250
|
-
# @example Without options
|
251
|
-
# COSMOS::Store().write_topic('MANGO__TOPIC', {'message' => 'something'})
|
252
|
-
# @example With options
|
253
|
-
# COSMOS::Store().write_topic('MANGO__TOPIC', {'message' => 'something'}, id: '0-0', maxlen: 1000, approximate: false)
|
254
|
-
#
|
255
|
-
# @param topic [String] the stream / topic
|
256
|
-
# @param msg_hash [Hash] one or multiple field-value pairs
|
257
|
-
#
|
258
|
-
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
259
|
-
# @option opts [Integer] :maxlen max length of entries
|
260
|
-
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
261
|
-
#
|
262
|
-
# @return [String] the entry id
|
263
|
-
def self.write_topic(topic, msg_hash, id = '*', maxlen = nil, approximate = true)
|
264
|
-
self.instance.write_topic(topic, msg_hash, id, maxlen, approximate)
|
265
|
-
end
|
266
|
-
|
267
177
|
# Add new entry to the redis stream.
|
268
178
|
# > https://www.rubydoc.info/github/redis/redis-rb/Redis:xadd
|
269
179
|
#
|
@@ -289,23 +199,6 @@ module Cosmos
|
|
289
199
|
end
|
290
200
|
end
|
291
201
|
|
292
|
-
# Trims older entries of the redis stream if needed.
|
293
|
-
# > https://www.rubydoc.info/github/redis/redis-rb/Redis:xtrim
|
294
|
-
#
|
295
|
-
# @example Without options
|
296
|
-
# COSMOS::Store.trim_topic('MANGO__TOPIC', 1000)
|
297
|
-
# @example With options
|
298
|
-
# COSMOS::Store.trim_topic('MANGO__TOPIC', 1000, approximate: true, limit: 0)
|
299
|
-
#
|
300
|
-
# @param topic [String] the stream key
|
301
|
-
# @param minid [Integer] max length of entries to trim
|
302
|
-
# @param limit [Boolean] whether to add `~` modifier of maxlen or not
|
303
|
-
#
|
304
|
-
# @return [Integer] the number of entries actually deleted
|
305
|
-
def self.trim_topic(topic, minid, approximate = true, limit: 0)
|
306
|
-
self.instance.trim_topic(topic, minid, approximate, limit: limit)
|
307
|
-
end
|
308
|
-
|
309
202
|
# Trims older entries of the redis stream if needed.
|
310
203
|
# > https://www.rubydoc.info/github/redis/redis-rb/Redis:xtrim
|
311
204
|
#
|
@@ -325,6 +218,14 @@ module Cosmos
|
|
325
218
|
end
|
326
219
|
end
|
327
220
|
end
|
221
|
+
|
222
|
+
class EphemeralStore < Store
|
223
|
+
def initialize(pool_size = 10)
|
224
|
+
super(pool_size)
|
225
|
+
@redis_url = "redis://#{ENV['COSMOS_REDIS_EPHEMERAL_HOSTNAME']}:#{ENV['COSMOS_REDIS_EPHEMERAL_PORT']}"
|
226
|
+
@redis_pool = ConnectionPool.new(size: pool_size) { build_redis() }
|
227
|
+
end
|
228
|
+
end
|
328
229
|
end
|
329
230
|
|
330
231
|
class Redis
|
data/lib/cosmos/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
COSMOS_VERSION = '5.0.
|
3
|
+
COSMOS_VERSION = '5.0.4'
|
4
4
|
module Cosmos
|
5
5
|
module Version
|
6
6
|
MAJOR = '5'
|
7
7
|
MINOR = '0'
|
8
|
-
PATCH = '
|
8
|
+
PATCH = '4'
|
9
9
|
OTHER = ''
|
10
|
-
BUILD = '
|
10
|
+
BUILD = '84d6dcebd4bc21e5559963be4e5fde2ddb7e9822'
|
11
11
|
end
|
12
|
-
VERSION = '5.0.
|
13
|
-
GEM_VERSION = '5.0.
|
12
|
+
VERSION = '5.0.4'
|
13
|
+
GEM_VERSION = '5.0.4'
|
14
14
|
end
|