prometheus-client-mmap 1.2.1-aarch64-linux-musl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.tool-versions +1 -0
  3. data/README.md +281 -0
  4. data/ext/fast_mmaped_file_rs/Cargo.toml +35 -0
  5. data/ext/fast_mmaped_file_rs/README.md +52 -0
  6. data/ext/fast_mmaped_file_rs/build.rs +5 -0
  7. data/ext/fast_mmaped_file_rs/extconf.rb +28 -0
  8. data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
  9. data/ext/fast_mmaped_file_rs/src/file_entry.rs +784 -0
  10. data/ext/fast_mmaped_file_rs/src/file_info.rs +240 -0
  11. data/ext/fast_mmaped_file_rs/src/lib.rs +78 -0
  12. data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
  13. data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
  14. data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +704 -0
  15. data/ext/fast_mmaped_file_rs/src/mmap.rs +891 -0
  16. data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
  17. data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
  18. data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
  19. data/lib/3.1/fast_mmaped_file_rs.so +0 -0
  20. data/lib/3.2/fast_mmaped_file_rs.so +0 -0
  21. data/lib/3.3/fast_mmaped_file_rs.so +0 -0
  22. data/lib/3.4/fast_mmaped_file_rs.so +0 -0
  23. data/lib/prometheus/client/configuration.rb +23 -0
  24. data/lib/prometheus/client/counter.rb +27 -0
  25. data/lib/prometheus/client/formats/text.rb +85 -0
  26. data/lib/prometheus/client/gauge.rb +40 -0
  27. data/lib/prometheus/client/helper/entry_parser.rb +132 -0
  28. data/lib/prometheus/client/helper/file_locker.rb +50 -0
  29. data/lib/prometheus/client/helper/json_parser.rb +23 -0
  30. data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
  31. data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
  32. data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
  33. data/lib/prometheus/client/helper/plain_file.rb +29 -0
  34. data/lib/prometheus/client/histogram.rb +80 -0
  35. data/lib/prometheus/client/label_set_validator.rb +85 -0
  36. data/lib/prometheus/client/metric.rb +80 -0
  37. data/lib/prometheus/client/mmaped_dict.rb +79 -0
  38. data/lib/prometheus/client/mmaped_value.rb +154 -0
  39. data/lib/prometheus/client/page_size.rb +17 -0
  40. data/lib/prometheus/client/push.rb +203 -0
  41. data/lib/prometheus/client/rack/collector.rb +88 -0
  42. data/lib/prometheus/client/rack/exporter.rb +96 -0
  43. data/lib/prometheus/client/registry.rb +65 -0
  44. data/lib/prometheus/client/simple_value.rb +31 -0
  45. data/lib/prometheus/client/summary.rb +69 -0
  46. data/lib/prometheus/client/support/puma.rb +44 -0
  47. data/lib/prometheus/client/support/unicorn.rb +35 -0
  48. data/lib/prometheus/client/uses_value_type.rb +20 -0
  49. data/lib/prometheus/client/version.rb +5 -0
  50. data/lib/prometheus/client.rb +58 -0
  51. data/lib/prometheus.rb +3 -0
  52. metadata +249 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab65e84f2d0ce3df84d8a9180a3f81b7708475f8859db3d4abe1850bf87ef5f6
4
+ data.tar.gz: 7fc69d3bd000941c24f3f651ad1326eab18905202076df8c1505866a588e21d2
5
+ SHA512:
6
+ metadata.gz: bf6be9cdd5a7dc1208d94b87ce2a0c8477e139ede0148ee1f22bf38742142b92357032b82583289ec762d4d61752bc83d699efd508a9921e40570166c018eff7
7
+ data.tar.gz: 0023b30fd6a366abab852b2c76a2d037783b169501ad710340478872c52bed6abb68b9a7c18e272331ac31be37312e10c6ad0ee435f75787e86d35bb07dcc83f
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ rust 1.73.0
data/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # Prometheus Ruby Mmap Client
2
+
3
+ This Prometheus library is fork of [Prometheus Ruby Client](https://github.com/prometheus/client_ruby)
4
+ that uses mmap'ed files to share metrics from multiple processes.
5
+ This allows efficient metrics processing for Ruby web apps running in multiprocess setups like Unicorn.
6
+
7
+ A suite of instrumentation metric primitives for Ruby that can be exposed
8
+ through a HTTP interface. Intended to be used together with a
9
+ [Prometheus server][1].
10
+
11
+ [![Gem Version][4]](http://badge.fury.io/rb/prometheus-client-mmap)
12
+ [![Build Status][3]](https://gitlab.com/gitlab-org/prometheus-client-mmap/commits/master)
13
+
14
+ ## Installation
15
+
16
+ prometheus-client-mmap ships with [precompiled native gems](https://rubygems.org/gems/prometheus-client-mmap).
17
+ Install the gem via:
18
+
19
+ ```shell
20
+ gem install prometheus-client-mmap
21
+ ```
22
+
23
+ ### Requirements for building from source
24
+
25
+ Building from source requires a number of dependencies:
26
+
27
+ * Rust v1.65+
28
+ * clang 5.0 or higher for [bindgen](https://rust-lang.github.io/rust-bindgen/requirements.html)
29
+ * `make`
30
+ * A C compiler (for legacy extension, which will be removed soon)
31
+
32
+ ## Usage
33
+
34
+ ### Overview
35
+
36
+ ```ruby
37
+ require 'prometheus/client'
38
+
39
+ # returns a default registry
40
+ prometheus = Prometheus::Client.registry
41
+
42
+ # create a new counter metric
43
+ http_requests = Prometheus::Client::Counter.new(:http_requests, 'A counter of HTTP requests made')
44
+ # register the metric
45
+ prometheus.register(http_requests)
46
+
47
+ # equivalent helper function
48
+ http_requests = prometheus.counter(:http_requests, 'A counter of HTTP requests made')
49
+
50
+ # start using the counter
51
+ http_requests.increment
52
+ ```
53
+
54
+ ## Rust extension
55
+
56
+ This gem now uses a rewritten Rust extension instead of C.
57
+ implementation that reads the metric files and outputs the multiprocess
58
+ metrics to text. This implementation is significantly faster than the C extension.
59
+
60
+ ### Rack middleware
61
+
62
+ There are two [Rack][2] middlewares available, one to expose a metrics HTTP
63
+ endpoint to be scraped by a prometheus server ([Exporter][9]) and one to trace all HTTP
64
+ requests ([Collector][10]).
65
+
66
+ It's highly recommended to enable gzip compression for the metrics endpoint,
67
+ for example by including the `Rack::Deflater` middleware.
68
+
69
+ ```ruby
70
+ # config.ru
71
+
72
+ require 'rack'
73
+ require 'prometheus/client/rack/collector'
74
+ require 'prometheus/client/rack/exporter'
75
+
76
+ use Rack::Deflater, if: ->(env, status, headers, body) { body.any? && body[0].length > 512 }
77
+ use Prometheus::Client::Rack::Collector
78
+ use Prometheus::Client::Rack::Exporter
79
+
80
+ run ->(env) { [200, {'Content-Type' => 'text/html'}, ['OK']] }
81
+ ```
82
+
83
+ Start the server and have a look at the metrics endpoint:
84
+ [http://localhost:5000/metrics](http://localhost:5000/metrics).
85
+
86
+ For further instructions and other scripts to get started, have a look at the
87
+ integrated [example application](examples/rack/README.md).
88
+
89
+ ### Pushgateway
90
+
91
+ The Ruby client can also be used to push its collected metrics to a
92
+ [Pushgateway][8]. This comes in handy with batch jobs or in other scenarios
93
+ where it's not possible or feasible to let a Prometheus server scrape a Ruby
94
+ process. TLS and HTTP basic authentication are supported.
95
+
96
+ ```ruby
97
+ require 'prometheus/client'
98
+ require 'prometheus/client/push'
99
+
100
+ registry = Prometheus::Client.registry
101
+ # ... register some metrics, set/increment/observe/etc. their values
102
+
103
+ # push the registry state to the default gateway
104
+ Prometheus::Client::Push.new(job: 'my-batch-job').add(registry)
105
+
106
+ # optional: specify a grouping key that uniquely identifies a job instance, and gateway.
107
+ #
108
+ # Note: the labels you use in the grouping key must not conflict with labels set on the
109
+ # metrics being pushed. If they do, an error will be raised.
110
+ Prometheus::Client::Push.new(
111
+ job: 'my-batch-job',
112
+ gateway: 'https://example.domain:1234',
113
+ grouping_key: { instance: 'some-instance', extra_key: 'foobar' }
114
+ ).add(registry)
115
+
116
+ # If you want to replace any previously pushed metrics for a given grouping key,
117
+ # use the #replace method.
118
+ #
119
+ # Unlike #add, this will completely replace the metrics under the specified grouping key
120
+ # (i.e. anything currently present in the pushgateway for the specified grouping key, but
121
+ # not present in the registry for that grouping key will be removed).
122
+ #
123
+ # See https://github.com/prometheus/pushgateway#put-method for a full explanation.
124
+ Prometheus::Client::Push.new(job: 'my-batch-job').replace(registry)
125
+
126
+ # If you want to delete all previously pushed metrics for a given grouping key,
127
+ # use the #delete method.
128
+ Prometheus::Client::Push.new(job: 'my-batch-job').delete
129
+ ```
130
+
131
+ ## Metrics
132
+
133
+ The following metric types are currently supported.
134
+
135
+ ### Counter
136
+
137
+ Counter is a metric that exposes merely a sum or tally of things.
138
+
139
+ ```ruby
140
+ counter = Prometheus::Client::Counter.new(:service_requests_total, '...')
141
+
142
+ # increment the counter for a given label set
143
+ counter.increment({ service: 'foo' })
144
+
145
+ # increment by a given value
146
+ counter.increment({ service: 'bar' }, 5)
147
+
148
+ # get current value for a given label set
149
+ counter.get({ service: 'bar' })
150
+ # => 5
151
+ ```
152
+
153
+ ### Gauge
154
+
155
+ Gauge is a metric that exposes merely an instantaneous value or some snapshot
156
+ thereof.
157
+
158
+ ```ruby
159
+ gauge = Prometheus::Client::Gauge.new(:room_temperature_celsius, '...')
160
+
161
+ # set a value
162
+ gauge.set({ room: 'kitchen' }, 21.534)
163
+
164
+ # retrieve the current value for a given label set
165
+ gauge.get({ room: 'kitchen' })
166
+ # => 21.534
167
+ ```
168
+
169
+ ### Histogram
170
+
171
+ A histogram samples observations (usually things like request durations or
172
+ response sizes) and counts them in configurable buckets. It also provides a sum
173
+ of all observed values.
174
+
175
+ ```ruby
176
+ histogram = Prometheus::Client::Histogram.new(:service_latency_seconds, '...')
177
+
178
+ # record a value
179
+ histogram.observe({ service: 'users' }, Benchmark.realtime { service.call(arg) })
180
+
181
+ # retrieve the current bucket values
182
+ histogram.get({ service: 'users' })
183
+ # => { 0.005 => 3, 0.01 => 15, 0.025 => 18, ..., 2.5 => 42, 5 => 42, 10 = >42 }
184
+ ```
185
+
186
+ ### Summary
187
+
188
+ Summary, similar to histograms, is an accumulator for samples. It captures
189
+ Numeric data and provides an efficient percentile calculation mechanism.
190
+
191
+ ```ruby
192
+ summary = Prometheus::Client::Summary.new(:service_latency_seconds, '...')
193
+
194
+ # record a value
195
+ summary.observe({ service: 'database' }, Benchmark.realtime { service.call() })
196
+
197
+ # retrieve the current quantile values
198
+ summary.get({ service: 'database' })
199
+ # => { 0.5 => 0.1233122, 0.9 => 3.4323, 0.99 => 5.3428231 }
200
+ ```
201
+
202
+ ## Configuration
203
+
204
+ ### Memory mapped files storage location
205
+
206
+ Set `prometheus_multiproc_dir` environment variable to the path where you want metric files to be stored. Example:
207
+
208
+ ```
209
+ prometheus_multiproc_dir=/tmp
210
+ ```
211
+
212
+ ## Pitfalls
213
+
214
+ ### PID cardinality
215
+
216
+ In multiprocess setup e.g. running under Unicorn or Puma, having worker process restart often
217
+ can lead to performance problems when proccesing metric files. By default each process using
218
+ Prometheus metrics will create a set of files based on that process PID. With high worker
219
+ churn this will lead to creation of thousands of files and in turn will cause very noticable
220
+ slowdown when displaying metrics
221
+
222
+ To reduce this problem, a surrogate process id can be used. Set of all such IDs needs
223
+ have low cardinality, and each process id must be unique among all running process.
224
+
225
+ For Unicorn and Puma a worker id/number can be used to greatly speedup the metrics rendering.
226
+
227
+ If you are using Unicorn, add this line to your `configure` block:
228
+
229
+ ```ruby
230
+ config.pid_provider = Prometheus::Client::Support::Unicorn.method(:worker_pid_provider)
231
+ ```
232
+
233
+ If you are using Puma, add this line to your `configure` block:
234
+
235
+ ```ruby
236
+ config.pid_provider = Prometheus::Client::Support::Puma.method(:worker_pid_provider)
237
+ ```
238
+
239
+ ## Tools
240
+
241
+ ### `bin/parse`
242
+
243
+ This command can be used to parse metric files located on the filesystem just like a metric exporter would.
244
+ It outputs either `json` formatted raw data or digested data in prometheus `text` format.
245
+
246
+ #### Usage:
247
+
248
+ ```bash
249
+ $ ./bin/parse -h
250
+ Usage: parse [options] files...
251
+ -t, --to-prometheus-text format output using Prometheus text formatter
252
+ -p, --profile enable profiling
253
+ -h, --help Show this message
254
+ ```
255
+
256
+ ## Development
257
+
258
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
259
+
260
+ To install this gem onto your local machine, run `bundle exec rake install`.
261
+
262
+ ### Releasing a new version
263
+
264
+ To release a new version:
265
+
266
+ 1. Update `lib/prometheus/client/version.rb` with the version number.
267
+ 1. Update `CHANGELOG.md` with the changes in the release.
268
+ 1. Create a merge request and merge it to `master`.
269
+ 1. Push a new tag to the repository.
270
+
271
+ The new version with precompiled, native gems will automatically be
272
+ published to [RubyGems](https://rubygems.org/gems/prometheus-client-mmap) when the
273
+ pipeline for the tag completes.
274
+
275
+ [1]: https://github.com/prometheus/prometheus
276
+ [2]: http://rack.github.io/
277
+ [3]: https://gitlab.com/gitlab-org/prometheus-client-mmap/badges/master/pipeline.svg
278
+ [4]: https://badge.fury.io/rb/prometheus-client.svg
279
+ [8]: https://github.com/prometheus/pushgateway
280
+ [9]: lib/prometheus/client/rack/exporter.rb
281
+ [10]: lib/prometheus/client/rack/collector.rb
@@ -0,0 +1,35 @@
1
+ [package]
2
+ name = "fast_mmaped_file_rs"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
+
8
+ [dependencies]
9
+ hashbrown = "0.14"
10
+ libc = "0.2"
11
+ magnus = { version = "0.7", features = ["rb-sys"] }
12
+ memmap2 = "0.9"
13
+ # v0.26 cannot be built on CentOS 7 https://github.com/nix-rust/nix/issues/1972
14
+ nix = { version = "0.25", features = ["mman"] } # mman used for MsFlags
15
+ rb-sys = { version = "0.9", features = ["stable-api-compiled-fallback"] }
16
+ serde = { version = "1.0", features = ["derive"] }
17
+ serde_json = { version = "1.0", features = ["raw_value"] }
18
+ smallvec = { version = "1.13", features = ["serde"] }
19
+ thiserror = "2.0"
20
+
21
+ [dev-dependencies]
22
+ bstr = "1.11"
23
+ indoc = "2.0"
24
+ # We need the `embed` feature to run tests, but this triggers failures when building as a Gem.
25
+ magnus = { version = "0.7", features = ["rb-sys","embed"] }
26
+ rand = "0.8"
27
+ sha2 = "0.10"
28
+ tempfile = "3.14"
29
+
30
+ [build-dependencies]
31
+ rb-sys-env = { git = "https://github.com/oxidize-rb/rb-sys.git", rev = "5d1999ba70d301581524b8252c8ff3b8a825afa2" }
32
+
33
+ [lib]
34
+ # Integration tests won't work if crate is only `cdylib`.
35
+ crate-type = ["cdylib","lib"]
@@ -0,0 +1,52 @@
1
+ # Testing
2
+
3
+ ## Running Tests
4
+
5
+ Use [cargo nextest](https://nexte.st/) to execute the Rust unit tests.
6
+
7
+ ```sh
8
+ $ cargo nextest run
9
+ ```
10
+
11
+ ## Why not use 'cargo test'?
12
+
13
+ We need to embed Ruby into the test binary to access Ruby types. This requires
14
+ us to run `magnus::embed::init()` no more than once before calling Ruby.
15
+ See [the magnus docs](https://docs.rs/magnus/latest/magnus/embed/fn.init.html)
16
+ for more details.
17
+
18
+ If we try to create separate `#[test]` functions that call `init()` these will
19
+ conflict, as Cargo runs tests in parallel using a single process with separate
20
+ threads. Running `cargo test` will result in errors like:
21
+
22
+ ```
23
+ ---- file_info::test::with_ruby stdout ----
24
+ thread 'file_info::test::with_ruby' panicked at 'Ruby already initialized'
25
+ ```
26
+
27
+ The simplest workaround for this is to avoid using `cargo test` to run unit
28
+ tests. [nextest](https://nexte.st/) is an alternate test harness that runs each
29
+ test as its own process, enabling each test to intitialize Ruby without
30
+ conflict.
31
+
32
+ ## 'symbol not found' errors when running tests
33
+
34
+ If you see errors like the following when running tests:
35
+
36
+ ```
37
+ Caused by:
38
+ for `fast_mmaped_file_rs`, command `/Users/myuser/prometheus-client-mmap/ext/fast_mmaped_file_rs/target/debug/deps/fast_mmaped_file_rs-c81ccc96a6484e04 --list --format terse` exited with signal 6 (SIGABRT)
39
+ --- stdout:
40
+
41
+ --- stderr:
42
+ dyld[17861]: symbol not found in flat namespace '_rb_cArray'
43
+ ```
44
+
45
+ Clearing the build cache will resolve the problem.
46
+
47
+ ```sh
48
+ $ cargo clean
49
+ ```
50
+
51
+ This is probably due to separate features being used with `magnus` in
52
+ development builds.
@@ -0,0 +1,5 @@
1
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
2
+ let _ = rb_sys_env::activate()?;
3
+
4
+ Ok(())
5
+ }
@@ -0,0 +1,28 @@
1
+ require "mkmf"
2
+ require "rb_sys/mkmf"
3
+
4
+ if find_executable('rustc')
5
+ create_rust_makefile("fast_mmaped_file_rs") do |r|
6
+ r.auto_install_rust_toolchain = false
7
+
8
+ if enable_config('fail-on-warning')
9
+ r.extra_rustflags = ["-Dwarnings"]
10
+ end
11
+
12
+ if enable_config('debug')
13
+ r.profile = :dev
14
+ end
15
+
16
+ if enable_config('address-sanitizer')
17
+ r.extra_rustflags = ["-Zsanitizer=address"]
18
+ end
19
+
20
+ # `rb_sys/mkmf` passes all arguments after `--` directly to `cargo rustc`.
21
+ # We use this awful hack to keep compatibility with existing flags used by
22
+ # the C implementation.
23
+ trimmed_argv = ARGV.take_while { |arg| arg != "--" }
24
+ ARGV = trimmed_argv
25
+ end
26
+ else
27
+ raise 'rustc not found. prometheus-client-mmap now requires Rust.'
28
+ end
@@ -0,0 +1,174 @@
1
+ use magnus::{exception, Ruby};
2
+ use std::any;
3
+ use std::fmt::Display;
4
+ use std::io;
5
+ use std::path::Path;
6
+ use thiserror::Error;
7
+
8
+ use crate::util;
9
+ use crate::PROM_EPARSING_ERROR;
10
+
11
+ /// A lightweight representation of Ruby ExceptionClasses.
12
+ #[derive(PartialEq, Eq, Clone, Copy, Debug)]
13
+ pub enum RubyError {
14
+ Arg,
15
+ Encoding,
16
+ Frozen,
17
+ Index,
18
+ Io,
19
+ NoMem,
20
+ PromParsing,
21
+ Runtime,
22
+ Type,
23
+ }
24
+
25
+ impl From<RubyError> for magnus::ExceptionClass {
26
+ fn from(err: RubyError) -> magnus::ExceptionClass {
27
+ match err {
28
+ RubyError::Arg => exception::arg_error(),
29
+ RubyError::Encoding => exception::encoding_error(),
30
+ RubyError::Frozen => exception::frozen_error(),
31
+ RubyError::Index => exception::index_error(),
32
+ RubyError::Io => exception::io_error(),
33
+ RubyError::NoMem => exception::no_mem_error(),
34
+ RubyError::Runtime => exception::runtime_error(),
35
+ RubyError::PromParsing => {
36
+ // UNWRAP: this will panic if called outside of a Ruby thread.
37
+ let ruby = Ruby::get().unwrap();
38
+ ruby.get_inner(&PROM_EPARSING_ERROR)
39
+ }
40
+ RubyError::Type => exception::type_error(),
41
+ }
42
+ }
43
+ }
44
+
45
+ /// Errors returned internally within the crate. Methods called directly by Ruby return
46
+ /// `magnus::error::Error` as do functions that interact heavily with Ruby. This can be
47
+ /// converted into a `magnus::error::Error` at the boundary between Rust and Ruby.
48
+ #[derive(PartialEq, Eq, Error, Debug)]
49
+ pub enum MmapError {
50
+ /// A read or write was made while another thread had mutable access to the mmap.
51
+ #[error("read/write operation attempted while mmap was being written to")]
52
+ ConcurrentAccess,
53
+ /// An error message used to exactly match the messages returned by the C
54
+ /// implementation.
55
+ #[error("{0}")]
56
+ Legacy(String, RubyError),
57
+ /// A String had invalid UTF-8 sequences.
58
+ #[error("{0}")]
59
+ Encoding(String),
60
+ /// A failed attempt to cast an integer from one type to another.
61
+ #[error("failed to cast {object_name} {value} from {from} to {to}")]
62
+ FailedCast {
63
+ from: &'static str,
64
+ to: &'static str,
65
+ value: String,
66
+ object_name: String,
67
+ },
68
+ /// The mmap was frozen when a mutable operation was attempted.
69
+ #[error("mmap")]
70
+ Frozen,
71
+ /// An io operation failed.
72
+ #[error("failed to {operation} path '{path}': {err}")]
73
+ Io {
74
+ operation: String,
75
+ path: String,
76
+ err: String,
77
+ },
78
+ #[error("string length gt {}", i32::MAX)]
79
+ KeyLength,
80
+ /// Failed to allocate memory.
81
+ #[error("Couldn't allocate for {0} memory")]
82
+ OutOfMemory(usize),
83
+ /// A memory operation fell outside of the containers bounds.
84
+ #[error("offset {index} out of bounds of len {len}")]
85
+ OutOfBounds { index: String, len: String },
86
+ /// A numeric operation overflowed.
87
+ #[error("overflow when {op} {value} and {added} of type {ty}")]
88
+ Overflow {
89
+ value: String,
90
+ added: String,
91
+ op: String,
92
+ ty: &'static str,
93
+ },
94
+ /// A miscellaneous error.
95
+ #[error("{0}")]
96
+ Other(String),
97
+ /// A failure when parsing a `.db` file containing Prometheus metrics.
98
+ #[error("{0}")]
99
+ PromParsing(String),
100
+ /// No mmap open.
101
+ #[error("unmapped file")]
102
+ UnmappedFile,
103
+ /// A custom error message with `strerror(3)` appended.
104
+ #[error("{0}")]
105
+ WithErrno(String),
106
+ }
107
+
108
+ impl MmapError {
109
+ pub fn legacy<T: Into<String>>(msg: T, ruby_err: RubyError) -> Self {
110
+ MmapError::Legacy(msg.into(), ruby_err)
111
+ }
112
+
113
+ pub fn failed_cast<T: Display, U>(value: T, object_name: &str) -> Self {
114
+ MmapError::FailedCast {
115
+ from: any::type_name::<T>(),
116
+ to: any::type_name::<U>(),
117
+ value: value.to_string(),
118
+ object_name: object_name.to_string(),
119
+ }
120
+ }
121
+ pub fn io(operation: &str, path: &Path, err: io::Error) -> Self {
122
+ MmapError::Io {
123
+ operation: operation.to_string(),
124
+ path: path.display().to_string(),
125
+ err: err.to_string(),
126
+ }
127
+ }
128
+
129
+ pub fn overflowed<T: Display>(value: T, added: T, op: &str) -> Self {
130
+ MmapError::Overflow {
131
+ value: value.to_string(),
132
+ added: added.to_string(),
133
+ op: op.to_string(),
134
+ ty: any::type_name::<T>(),
135
+ }
136
+ }
137
+
138
+ pub fn out_of_bounds<T: Display>(index: T, len: T) -> Self {
139
+ MmapError::OutOfBounds {
140
+ index: index.to_string(),
141
+ len: len.to_string(),
142
+ }
143
+ }
144
+
145
+ pub fn with_errno<T: Into<String>>(msg: T) -> Self {
146
+ let strerror = util::strerror(util::errno());
147
+ MmapError::WithErrno(format!("{}: ({strerror})", msg.into()))
148
+ }
149
+
150
+ pub fn ruby_err(&self) -> RubyError {
151
+ match self {
152
+ MmapError::ConcurrentAccess => RubyError::Arg,
153
+ MmapError::Legacy(_, e) => *e,
154
+ MmapError::Encoding(_) => RubyError::Encoding,
155
+ MmapError::Io { .. } => RubyError::Io,
156
+ MmapError::FailedCast { .. } => RubyError::Arg,
157
+ MmapError::Frozen => RubyError::Frozen,
158
+ MmapError::KeyLength => RubyError::Arg,
159
+ MmapError::Overflow { .. } => RubyError::Arg,
160
+ MmapError::OutOfBounds { .. } => RubyError::Index,
161
+ MmapError::OutOfMemory { .. } => RubyError::NoMem,
162
+ MmapError::Other(_) => RubyError::Arg,
163
+ MmapError::PromParsing(_) => RubyError::PromParsing,
164
+ MmapError::UnmappedFile => RubyError::Io,
165
+ MmapError::WithErrno(_) => RubyError::Io,
166
+ }
167
+ }
168
+ }
169
+
170
+ impl From<MmapError> for magnus::error::Error {
171
+ fn from(err: MmapError) -> magnus::error::Error {
172
+ magnus::error::Error::new(err.ruby_err().into(), err.to_string())
173
+ }
174
+ }