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.
- checksums.yaml +7 -0
- data/.tool-versions +1 -0
- data/README.md +281 -0
- data/ext/fast_mmaped_file_rs/Cargo.toml +35 -0
- data/ext/fast_mmaped_file_rs/README.md +52 -0
- data/ext/fast_mmaped_file_rs/build.rs +5 -0
- data/ext/fast_mmaped_file_rs/extconf.rb +28 -0
- data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
- data/ext/fast_mmaped_file_rs/src/file_entry.rs +784 -0
- data/ext/fast_mmaped_file_rs/src/file_info.rs +240 -0
- data/ext/fast_mmaped_file_rs/src/lib.rs +78 -0
- data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
- data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
- data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +704 -0
- data/ext/fast_mmaped_file_rs/src/mmap.rs +891 -0
- data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
- data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
- data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
- data/lib/3.1/fast_mmaped_file_rs.so +0 -0
- data/lib/3.2/fast_mmaped_file_rs.so +0 -0
- data/lib/3.3/fast_mmaped_file_rs.so +0 -0
- data/lib/3.4/fast_mmaped_file_rs.so +0 -0
- data/lib/prometheus/client/configuration.rb +23 -0
- data/lib/prometheus/client/counter.rb +27 -0
- data/lib/prometheus/client/formats/text.rb +85 -0
- data/lib/prometheus/client/gauge.rb +40 -0
- data/lib/prometheus/client/helper/entry_parser.rb +132 -0
- data/lib/prometheus/client/helper/file_locker.rb +50 -0
- data/lib/prometheus/client/helper/json_parser.rb +23 -0
- data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
- data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
- data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
- data/lib/prometheus/client/helper/plain_file.rb +29 -0
- data/lib/prometheus/client/histogram.rb +80 -0
- data/lib/prometheus/client/label_set_validator.rb +85 -0
- data/lib/prometheus/client/metric.rb +80 -0
- data/lib/prometheus/client/mmaped_dict.rb +79 -0
- data/lib/prometheus/client/mmaped_value.rb +154 -0
- data/lib/prometheus/client/page_size.rb +17 -0
- data/lib/prometheus/client/push.rb +203 -0
- data/lib/prometheus/client/rack/collector.rb +88 -0
- data/lib/prometheus/client/rack/exporter.rb +96 -0
- data/lib/prometheus/client/registry.rb +65 -0
- data/lib/prometheus/client/simple_value.rb +31 -0
- data/lib/prometheus/client/summary.rb +69 -0
- data/lib/prometheus/client/support/puma.rb +44 -0
- data/lib/prometheus/client/support/unicorn.rb +35 -0
- data/lib/prometheus/client/uses_value_type.rb +20 -0
- data/lib/prometheus/client/version.rb +5 -0
- data/lib/prometheus/client.rb +58 -0
- data/lib/prometheus.rb +3 -0
- 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,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
|
+
}
|