Skip to content

Commit 149fc3c

Browse files
author
Daniel Magliola
committed
Fix the "subtle" bug introduced in the previous commit
So, again, I don't propose we actually do this, this is just illustrative of what we need to do, and didn't want it to get lost. The problem with the previous commit is that for each metric, we open a new FileMappedDict. All of them sharing the same file is "fine", actually. The problem comes from the fact that each instance of FileMappedDict has their own version of `@used`. When one metric adds a new entry to the file, it moves its own `@used` pointed, but the other instances don't notice this. As soon as a second instance that had already been loaded adds a new labelset, it corrupts the file. This only shows up when running the benchmarks, with histograms. Counters (in our benchmark) don't reproduce it, because each metric in our benchmark only has one labelset. They all get added initially to the file, and an already loded Dict doesn't add new ones. The reason Histograms reproduce it is that new observations may report a bucket that wasn't seen before, which is effectively a new labelset. The solution is for all MetricStore instances to share one single FileMappedDict. This is not particularly hard, but the current MetricStore is not written in a way that allows that without doing this crap. We should find a better way. I'm just leaving this here as documentation of this problem, for the next brave soul attempting this
1 parent d8b247b commit 149fc3c

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

lib/prometheus/client/data_stores/direct_file_store.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ def validate_metric_settings(metric_settings)
6767
class MetricStore
6868
attr_reader :metric_name, :store_settings
6969

70+
class << self
71+
attr_accessor :shared_store_opened_by_pid
72+
attr_accessor :shared_internal_store
73+
end
74+
7075
def initialize(metric_name:, store_settings:, metric_settings:)
7176
@metric_name = metric_name
7277
@store_settings = store_settings
@@ -164,6 +169,14 @@ def store_key(labels)
164169
end
165170

166171
def internal_store
172+
if @store_settings[:separate_files_per_metric]
173+
individual_metric_internal_store
174+
else
175+
all_metrics_shared_internal_store
176+
end
177+
end
178+
179+
def individual_metric_internal_store
167180
if @store_opened_by_pid != process_id
168181
@store_opened_by_pid = process_id
169182
@internal_store = FileMappedDict.new(filemap_filename)
@@ -172,6 +185,15 @@ def internal_store
172185
end
173186
end
174187

188+
def all_metrics_shared_internal_store
189+
if self.class.shared_store_opened_by_pid != process_id
190+
self.class.shared_store_opened_by_pid = process_id
191+
self.class.shared_internal_store = FileMappedDict.new(filemap_filename)
192+
else
193+
self.class.shared_internal_store
194+
end
195+
end
196+
175197
# Filename for this metric's PStore (one per process)
176198
def filemap_filename
177199
filename = if @store_settings[:separate_files_per_metric]

spec/prometheus/client/data_stores/direct_file_store_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
# Reset the PStores
1111
before do
1212
Dir.glob('/tmp/prometheus_test/*').each { |file| File.delete(file) }
13+
# This doesn't actually work, btw, but it's what would have to be done.
14+
Prometheus::Client::DataStores::DirectFileStore::MetricStore.shared_store_opened_by_pid = nil
1315
end
1416

1517
it_behaves_like Prometheus::Client::DataStores

0 commit comments

Comments
 (0)