prometheus-client-mmap 0.20.3-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +253 -0
  3. data/ext/fast_mmaped_file/extconf.rb +30 -0
  4. data/ext/fast_mmaped_file/fast_mmaped_file.c +122 -0
  5. data/ext/fast_mmaped_file/file_format.c +5 -0
  6. data/ext/fast_mmaped_file/file_format.h +11 -0
  7. data/ext/fast_mmaped_file/file_parsing.c +195 -0
  8. data/ext/fast_mmaped_file/file_parsing.h +27 -0
  9. data/ext/fast_mmaped_file/file_reading.c +102 -0
  10. data/ext/fast_mmaped_file/file_reading.h +30 -0
  11. data/ext/fast_mmaped_file/globals.h +14 -0
  12. data/ext/fast_mmaped_file/mmap.c +427 -0
  13. data/ext/fast_mmaped_file/mmap.h +61 -0
  14. data/ext/fast_mmaped_file/rendering.c +199 -0
  15. data/ext/fast_mmaped_file/rendering.h +8 -0
  16. data/ext/fast_mmaped_file/utils.c +56 -0
  17. data/ext/fast_mmaped_file/utils.h +22 -0
  18. data/ext/fast_mmaped_file/value_access.c +242 -0
  19. data/ext/fast_mmaped_file/value_access.h +15 -0
  20. data/ext/fast_mmaped_file_rs/.cargo/config.toml +23 -0
  21. data/ext/fast_mmaped_file_rs/Cargo.lock +790 -0
  22. data/ext/fast_mmaped_file_rs/Cargo.toml +30 -0
  23. data/ext/fast_mmaped_file_rs/README.md +52 -0
  24. data/ext/fast_mmaped_file_rs/extconf.rb +30 -0
  25. data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
  26. data/ext/fast_mmaped_file_rs/src/file_entry.rs +579 -0
  27. data/ext/fast_mmaped_file_rs/src/file_info.rs +190 -0
  28. data/ext/fast_mmaped_file_rs/src/lib.rs +79 -0
  29. data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
  30. data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
  31. data/ext/fast_mmaped_file_rs/src/mmap.rs +151 -0
  32. data/ext/fast_mmaped_file_rs/src/parser.rs +346 -0
  33. data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
  34. data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
  35. data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
  36. data/lib/2.7/fast_mmaped_file.so +0 -0
  37. data/lib/2.7/fast_mmaped_file_rs.so +0 -0
  38. data/lib/3.0/fast_mmaped_file.so +0 -0
  39. data/lib/3.0/fast_mmaped_file_rs.so +0 -0
  40. data/lib/3.1/fast_mmaped_file.so +0 -0
  41. data/lib/3.1/fast_mmaped_file_rs.so +0 -0
  42. data/lib/3.2/fast_mmaped_file.so +0 -0
  43. data/lib/3.2/fast_mmaped_file_rs.so +0 -0
  44. data/lib/prometheus/client/configuration.rb +23 -0
  45. data/lib/prometheus/client/counter.rb +27 -0
  46. data/lib/prometheus/client/formats/text.rb +118 -0
  47. data/lib/prometheus/client/gauge.rb +40 -0
  48. data/lib/prometheus/client/helper/entry_parser.rb +132 -0
  49. data/lib/prometheus/client/helper/file_locker.rb +50 -0
  50. data/lib/prometheus/client/helper/json_parser.rb +23 -0
  51. data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
  52. data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
  53. data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
  54. data/lib/prometheus/client/helper/plain_file.rb +29 -0
  55. data/lib/prometheus/client/histogram.rb +80 -0
  56. data/lib/prometheus/client/label_set_validator.rb +86 -0
  57. data/lib/prometheus/client/metric.rb +80 -0
  58. data/lib/prometheus/client/mmaped_dict.rb +79 -0
  59. data/lib/prometheus/client/mmaped_value.rb +154 -0
  60. data/lib/prometheus/client/page_size.rb +17 -0
  61. data/lib/prometheus/client/push.rb +203 -0
  62. data/lib/prometheus/client/rack/collector.rb +88 -0
  63. data/lib/prometheus/client/rack/exporter.rb +96 -0
  64. data/lib/prometheus/client/registry.rb +65 -0
  65. data/lib/prometheus/client/simple_value.rb +31 -0
  66. data/lib/prometheus/client/summary.rb +69 -0
  67. data/lib/prometheus/client/support/unicorn.rb +35 -0
  68. data/lib/prometheus/client/uses_value_type.rb +20 -0
  69. data/lib/prometheus/client/version.rb +5 -0
  70. data/lib/prometheus/client.rb +58 -0
  71. data/lib/prometheus.rb +3 -0
  72. data/vendor/c/hashmap/.gitignore +52 -0
  73. data/vendor/c/hashmap/LICENSE +21 -0
  74. data/vendor/c/hashmap/README.md +90 -0
  75. data/vendor/c/hashmap/_config.yml +1 -0
  76. data/vendor/c/hashmap/src/hashmap.c +692 -0
  77. data/vendor/c/hashmap/src/hashmap.h +267 -0
  78. data/vendor/c/hashmap/test/Makefile +22 -0
  79. data/vendor/c/hashmap/test/hashmap_test.c +608 -0
  80. data/vendor/c/jsmn/.travis.yml +4 -0
  81. data/vendor/c/jsmn/LICENSE +20 -0
  82. data/vendor/c/jsmn/Makefile +41 -0
  83. data/vendor/c/jsmn/README.md +168 -0
  84. data/vendor/c/jsmn/example/jsondump.c +126 -0
  85. data/vendor/c/jsmn/example/simple.c +76 -0
  86. data/vendor/c/jsmn/jsmn.c +314 -0
  87. data/vendor/c/jsmn/jsmn.h +76 -0
  88. data/vendor/c/jsmn/library.json +16 -0
  89. data/vendor/c/jsmn/test/test.h +27 -0
  90. data/vendor/c/jsmn/test/tests.c +407 -0
  91. data/vendor/c/jsmn/test/testutil.h +94 -0
  92. metadata +243 -0
@@ -0,0 +1,64 @@
1
+ require 'prometheus/client/helper/entry_parser'
2
+ require 'prometheus/client/helper/file_locker'
3
+
4
+ # load precompiled extension if available
5
+ begin
6
+ ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
7
+ require_relative "../../../#{ruby_version}/fast_mmaped_file"
8
+ rescue LoadError
9
+ require 'fast_mmaped_file'
10
+ end
11
+
12
+ module Prometheus
13
+ module Client
14
+ module Helper
15
+ class MmapedFile < FastMmapedFile
16
+ include EntryParser
17
+
18
+ attr_reader :filepath, :size
19
+
20
+ def initialize(filepath)
21
+ @filepath = filepath
22
+
23
+ File.open(filepath, 'a+b') do |file|
24
+ file.truncate(initial_mmap_file_size) if file.size < MINIMUM_SIZE
25
+ @size = file.size
26
+ end
27
+
28
+ super(filepath)
29
+ end
30
+
31
+ def close
32
+ munmap
33
+ FileLocker.unlock(filepath)
34
+ end
35
+
36
+ private
37
+
38
+ def initial_mmap_file_size
39
+ Prometheus::Client.configuration.initial_mmap_file_size
40
+ end
41
+
42
+ public
43
+
44
+ class << self
45
+ def open(filepath)
46
+ MmapedFile.new(filepath)
47
+ end
48
+
49
+ def ensure_exclusive_file(file_prefix = 'mmaped_file')
50
+ (0..Float::INFINITY).lazy
51
+ .map { |f_num| "#{file_prefix}_#{Prometheus::Client.pid}-#{f_num}.db" }
52
+ .map { |filename| File.join(Prometheus::Client.configuration.multiprocess_files_dir, filename) }
53
+ .find { |path| Helper::FileLocker.lock_to_process(path) }
54
+ end
55
+
56
+ def open_exclusive_file(file_prefix = 'mmaped_file')
57
+ filename = Helper::MmapedFile.ensure_exclusive_file(file_prefix)
58
+ open(filename)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,29 @@
1
+ require 'prometheus/client/helper/entry_parser'
2
+
3
+ module Prometheus
4
+ module Client
5
+ module Helper
6
+ # Parses DB files without using mmap
7
+ class PlainFile
8
+ include EntryParser
9
+ attr_reader :filepath
10
+
11
+ def source
12
+ @data ||= File.read(filepath, mode: 'rb')
13
+ end
14
+
15
+ def initialize(filepath)
16
+ @filepath = filepath
17
+ end
18
+
19
+ def slice(*args)
20
+ source.slice(*args)
21
+ end
22
+
23
+ def size
24
+ source.length
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ require 'prometheus/client/metric'
2
+ require 'prometheus/client/uses_value_type'
3
+
4
+ module Prometheus
5
+ module Client
6
+ # A histogram samples observations (usually things like request durations
7
+ # or response sizes) and counts them in configurable buckets. It also
8
+ # provides a sum of all observed values.
9
+ class Histogram < Metric
10
+ # Value represents the state of a Histogram at a given point.
11
+ class Value < Hash
12
+ include UsesValueType
13
+ attr_accessor :sum, :total, :total_inf
14
+
15
+ def initialize(type, name, labels, buckets)
16
+ @sum = value_object(type, name, "#{name}_sum", labels)
17
+ @total = value_object(type, name, "#{name}_count", labels)
18
+ @total_inf = value_object(type, name, "#{name}_bucket", labels.merge(le: "+Inf"))
19
+
20
+ buckets.each do |bucket|
21
+ self[bucket] = value_object(type, name, "#{name}_bucket", labels.merge(le: bucket.to_s))
22
+ end
23
+ end
24
+
25
+ def observe(value)
26
+ @sum.increment(value)
27
+ @total.increment()
28
+ @total_inf.increment()
29
+
30
+ each_key do |bucket|
31
+ self[bucket].increment() if value <= bucket
32
+ end
33
+ end
34
+
35
+ def get()
36
+ hash = {}
37
+ each_key do |bucket|
38
+ hash[bucket] = self[bucket].get()
39
+ end
40
+ hash
41
+ end
42
+ end
43
+
44
+ # DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
45
+ # are tailored to broadly measure the response time (in seconds) of a
46
+ # network service. (From DefBuckets client_golang)
47
+ DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
48
+ 2.5, 5, 10].freeze
49
+
50
+ # Offer a way to manually specify buckets
51
+ def initialize(name, docstring, base_labels = {},
52
+ buckets = DEFAULT_BUCKETS)
53
+ raise ArgumentError, 'Unsorted buckets, typo?' unless sorted? buckets
54
+
55
+ @buckets = buckets
56
+ super(name, docstring, base_labels)
57
+ end
58
+
59
+ def type
60
+ :histogram
61
+ end
62
+
63
+ def observe(labels, value)
64
+ label_set = label_set_for(labels)
65
+ synchronize { @values[label_set].observe(value) }
66
+ end
67
+
68
+ private
69
+
70
+ def default(labels)
71
+ # TODO: default function needs to know key of hash info (label names and values)
72
+ Value.new(type, @name, labels, @buckets)
73
+ end
74
+
75
+ def sorted?(bucket)
76
+ bucket.each_cons(2).all? { |i, j| i <= j }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: UTF-8
2
+
3
+ module Prometheus
4
+ module Client
5
+ # LabelSetValidator ensures that all used label sets comply with the
6
+ # Prometheus specification.
7
+ class LabelSetValidator
8
+ # TODO: we might allow setting :instance in the future
9
+ RESERVED_LABELS = [:job, :instance].freeze
10
+
11
+ class LabelSetError < StandardError; end
12
+ class InvalidLabelSetError < LabelSetError; end
13
+ class InvalidLabelError < LabelSetError; end
14
+ class ReservedLabelError < LabelSetError; end
15
+
16
+ def initialize(reserved_labels = [])
17
+ @reserved_labels = (reserved_labels + RESERVED_LABELS).freeze
18
+ @validated = {}
19
+ end
20
+
21
+ def valid?(labels)
22
+ unless labels.is_a?(Hash)
23
+ raise InvalidLabelSetError, "#{labels} is not a valid label set"
24
+ end
25
+
26
+ labels.all? do |key, value|
27
+ validate_symbol(key)
28
+ validate_name(key)
29
+ validate_reserved_key(key)
30
+ validate_value(key, value)
31
+ end
32
+ end
33
+
34
+ def validate(labels)
35
+ return labels if @validated.key?(labels.hash)
36
+
37
+ valid?(labels)
38
+
39
+ unless @validated.empty? || match?(labels, @validated.first.last)
40
+ raise InvalidLabelSetError, "labels must have the same signature: (#{label_diff(labels, @validated.first.last)})"
41
+ end
42
+
43
+ @validated[labels.hash] = labels
44
+ end
45
+
46
+ private
47
+
48
+ def label_diff(a, b)
49
+ "expected keys: #{b.keys.sort}, got: #{a.keys.sort}"
50
+ end
51
+
52
+ def match?(a, b)
53
+ a.keys.sort == b.keys.sort
54
+ end
55
+
56
+ def validate_symbol(key)
57
+ return true if key.is_a?(Symbol)
58
+
59
+ raise InvalidLabelError, "label #{key} is not a symbol"
60
+ end
61
+
62
+ def validate_name(key)
63
+ return true unless key.to_s.start_with?('__')
64
+
65
+ raise ReservedLabelError, "label #{key} must not start with __"
66
+ end
67
+
68
+ def validate_reserved_key(key)
69
+ return true unless @reserved_labels.include?(key)
70
+
71
+ raise ReservedLabelError, "#{key} is reserved"
72
+ end
73
+
74
+ def validate_value(key, value)
75
+ return true if value.is_a?(String) ||
76
+ value.is_a?(Numeric) ||
77
+ value.is_a?(Symbol) ||
78
+ value.is_a?(FalseClass) ||
79
+ value.is_a?(TrueClass) ||
80
+ value.nil?
81
+
82
+ raise InvalidLabelError, "#{key} does not contain a valid value (type #{value.class})"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,80 @@
1
+ require 'thread'
2
+ require 'prometheus/client/label_set_validator'
3
+ require 'prometheus/client/uses_value_type'
4
+
5
+ module Prometheus
6
+ module Client
7
+ class Metric
8
+ include UsesValueType
9
+ attr_reader :name, :docstring, :base_labels
10
+
11
+ def initialize(name, docstring, base_labels = {})
12
+ @mutex = Mutex.new
13
+ @validator = case type
14
+ when :summary
15
+ LabelSetValidator.new(['quantile'])
16
+ when :histogram
17
+ LabelSetValidator.new(['le'])
18
+ else
19
+ LabelSetValidator.new
20
+ end
21
+ @values = Hash.new { |hash, key| hash[key] = default(key) }
22
+
23
+ validate_name(name)
24
+ validate_docstring(docstring)
25
+ @validator.valid?(base_labels)
26
+
27
+ @name = name
28
+ @docstring = docstring
29
+ @base_labels = base_labels
30
+ end
31
+
32
+ # Returns the value for the given label set
33
+ def get(labels = {})
34
+ label_set = label_set_for(labels)
35
+ @validator.valid?(label_set)
36
+
37
+ @values[label_set].get
38
+ end
39
+
40
+ # Returns all label sets with their values
41
+ def values
42
+ synchronize do
43
+ @values.each_with_object({}) do |(labels, value), memo|
44
+ memo[labels] = value
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def touch_default_value
52
+ @values[label_set_for({})]
53
+ end
54
+
55
+ def default(labels)
56
+ value_object(type, @name, @name, labels)
57
+ end
58
+
59
+ def validate_name(name)
60
+ return true if name.is_a?(Symbol)
61
+
62
+ raise ArgumentError, 'given name must be a symbol'
63
+ end
64
+
65
+ def validate_docstring(docstring)
66
+ return true if docstring.respond_to?(:empty?) && !docstring.empty?
67
+
68
+ raise ArgumentError, 'docstring must be given'
69
+ end
70
+
71
+ def label_set_for(labels)
72
+ @validator.validate(@base_labels.merge(labels))
73
+ end
74
+
75
+ def synchronize(&block)
76
+ @mutex.synchronize(&block)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,79 @@
1
+ require 'prometheus/client/helper/mmaped_file'
2
+ require 'prometheus/client/helper/plain_file'
3
+ require 'prometheus/client'
4
+
5
+ module Prometheus
6
+ module Client
7
+ class ParsingError < StandardError
8
+ end
9
+
10
+ # A dict of doubles, backed by an mmapped file.
11
+ #
12
+ # The file starts with a 4 byte int, indicating how much of it is used.
13
+ # Then 4 bytes of padding.
14
+ # There's then a number of entries, consisting of a 4 byte int which is the
15
+ # size of the next field, a utf-8 encoded string key, padding to an 8 byte
16
+ # alignment, and then a 8 byte float which is the value.
17
+ class MmapedDict
18
+ attr_reader :m, :used, :positions
19
+
20
+ def self.read_all_values(f)
21
+ Helper::PlainFile.new(f).entries.map do |data, encoded_len, value_offset, _|
22
+ encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
23
+ [encoded, value]
24
+ end
25
+ end
26
+
27
+ def initialize(m)
28
+ @mutex = Mutex.new
29
+
30
+ @m = m
31
+ # @m.mlock # TODO: Ensure memory is locked to RAM
32
+
33
+ @positions = {}
34
+ read_all_positions.each do |key, pos|
35
+ @positions[key] = pos
36
+ end
37
+ rescue StandardError => e
38
+ raise ParsingError, "exception #{e} while processing metrics file #{path}"
39
+ end
40
+
41
+ def read_value(key)
42
+ @m.fetch_entry(@positions, key, 0.0)
43
+ end
44
+
45
+ def write_value(key, value)
46
+ @m.upsert_entry(@positions, key, value)
47
+ end
48
+
49
+ def path
50
+ @m.filepath if @m
51
+ end
52
+
53
+ def close
54
+ @m.sync
55
+ @m.close
56
+ rescue TypeError => e
57
+ Prometheus::Client.logger.warn("munmap raised error #{e}")
58
+ end
59
+
60
+ def inspect
61
+ "#<#{self.class}:0x#{(object_id << 1).to_s(16)}>"
62
+ end
63
+
64
+ private
65
+
66
+ def init_value(key)
67
+ @m.add_entry(@positions, key, 0.0)
68
+ end
69
+
70
+ # Yield (key, pos). No locking is performed.
71
+ def read_all_positions
72
+ @m.entries.map do |data, encoded_len, _, absolute_pos|
73
+ encoded, = data.unpack(format('@4A%d', encoded_len))
74
+ [encoded, absolute_pos]
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,154 @@
1
+ require 'prometheus/client'
2
+ require 'prometheus/client/mmaped_dict'
3
+ require 'json'
4
+
5
+ module Prometheus
6
+ module Client
7
+ # A float protected by a mutex backed by a per-process mmaped file.
8
+ class MmapedValue
9
+ VALUE_LOCK = Mutex.new
10
+
11
+ @@files = {}
12
+ @@pid = -1
13
+
14
+ def initialize(type, metric_name, name, labels, multiprocess_mode = '')
15
+ @file_prefix = type.to_s
16
+ @metric_name = metric_name
17
+ @name = name
18
+ @labels = labels
19
+ if type == :gauge
20
+ @file_prefix += '_' + multiprocess_mode.to_s
21
+ end
22
+
23
+ @pid = -1
24
+
25
+ @mutex = Mutex.new
26
+ initialize_file
27
+ end
28
+
29
+ def increment(amount = 1)
30
+ @mutex.synchronize do
31
+ initialize_file if pid_changed?
32
+
33
+ @value += amount
34
+ write_value(@key, @value)
35
+ @value
36
+ end
37
+ end
38
+
39
+ def decrement(amount = 1)
40
+ increment(-amount)
41
+ end
42
+
43
+ def set(value)
44
+ @mutex.synchronize do
45
+ initialize_file if pid_changed?
46
+
47
+ @value = value
48
+ write_value(@key, @value)
49
+ @value
50
+ end
51
+ end
52
+
53
+ def get
54
+ @mutex.synchronize do
55
+ initialize_file if pid_changed?
56
+ return @value
57
+ end
58
+ end
59
+
60
+ def pid_changed?
61
+ @pid != Process.pid
62
+ end
63
+
64
+ # method needs to be run in VALUE_LOCK mutex
65
+ def unsafe_reinitialize_file(check_pid = true)
66
+ unsafe_initialize_file if !check_pid || pid_changed?
67
+ end
68
+
69
+ def self.reset_and_reinitialize
70
+ VALUE_LOCK.synchronize do
71
+ @@pid = Process.pid
72
+ @@files = {}
73
+
74
+ ObjectSpace.each_object(MmapedValue).each do |v|
75
+ v.unsafe_reinitialize_file(false)
76
+ end
77
+ end
78
+ end
79
+
80
+ def self.reset_on_pid_change
81
+ if pid_changed?
82
+ @@pid = Process.pid
83
+ @@files = {}
84
+ end
85
+ end
86
+
87
+ def self.reinitialize_on_pid_change
88
+ VALUE_LOCK.synchronize do
89
+ reset_on_pid_change
90
+
91
+ ObjectSpace.each_object(MmapedValue, &:unsafe_reinitialize_file)
92
+ end
93
+ end
94
+
95
+ def self.pid_changed?
96
+ @@pid != Process.pid
97
+ end
98
+
99
+ def self.multiprocess
100
+ true
101
+ end
102
+
103
+ private
104
+
105
+ def initialize_file
106
+ VALUE_LOCK.synchronize do
107
+ unsafe_initialize_file
108
+ end
109
+ end
110
+
111
+ def unsafe_initialize_file
112
+ self.class.reset_on_pid_change
113
+
114
+ @pid = Process.pid
115
+ unless @@files.has_key?(@file_prefix)
116
+ unless @file.nil?
117
+ @file.close
118
+ end
119
+ mmaped_file = Helper::MmapedFile.open_exclusive_file(@file_prefix)
120
+
121
+ @@files[@file_prefix] = MmapedDict.new(mmaped_file)
122
+ end
123
+
124
+ @file = @@files[@file_prefix]
125
+ @key = rebuild_key
126
+
127
+ @value = read_value(@key)
128
+ end
129
+
130
+
131
+ def rebuild_key
132
+ keys = @labels.keys.sort
133
+ values = @labels.values_at(*keys)
134
+
135
+ [@metric_name, @name, keys, values].to_json
136
+ end
137
+
138
+ def write_value(key, val)
139
+ @file.write_value(key, val)
140
+ rescue StandardError => e
141
+ Prometheus::Client.logger.warn("writing value to #{@file.path} failed with #{e}")
142
+ Prometheus::Client.logger.debug(e.backtrace.join("\n"))
143
+ end
144
+
145
+ def read_value(key)
146
+ @file.read_value(key)
147
+ rescue StandardError => e
148
+ Prometheus::Client.logger.warn("reading value from #{@file.path} failed with #{e}")
149
+ Prometheus::Client.logger.debug(e.backtrace.join("\n"))
150
+ 0
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,17 @@
1
+ require 'open3'
2
+
3
+ module Prometheus
4
+ module Client
5
+ module PageSize
6
+ def self.page_size(fallback_page_size: 4096)
7
+ stdout, status = Open3.capture2('getconf PAGESIZE')
8
+ return fallback_page_size if status.nil? || !status.success?
9
+
10
+ page_size = stdout.chomp.to_i
11
+ return fallback_page_size if page_size <= 0
12
+
13
+ page_size
14
+ end
15
+ end
16
+ end
17
+ end