fast-prometheus 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc4b46dd402452acfca0d58f4f3230850c4ef19101f8d84027cc903877451efc
4
- data.tar.gz: eb10abc576793f118af30ee6ea4ce44b5339939d5906816b4f92d3e14af64762
3
+ metadata.gz: 25cba069a74bd1f00499ff8d2c3a3feb84e9646b1447de704d2dff860dc5cea4
4
+ data.tar.gz: 533131b2461c719cb5525695a5abdc26b474bc6ce9fe35ba338855a698e887a3
5
5
  SHA512:
6
- metadata.gz: 393888fd071c19df54148a57187c415d3397107a51b96e0d442b5931be691aea0ca23f3570daaf0654b644993f8f5da8ae1b97be8da864c96b01e7948c2f90c9
7
- data.tar.gz: 7a842a1b2381f9b43c38a378c676b77d692619d74d6df7ac6ff979a783a041e248a8b7afcd7f2ec3cd569bf5be386500fffbd7efaad2ce729cf8f9bded0c6791
6
+ metadata.gz: c0a779b8563993d01400a7587852f2ec65c65621facd1bd3e9c44d39404e44eff0656a137a12f6a8da1b60b902d6bfe897b35ab82627064c8238f005171d0623
7
+ data.tar.gz: 8b9c2ce522dc9360c40f1779b7e4240370e5a7dbe4e6b333f9c39583bf2ba5a43805aa4b523763322ca62716b23c619047291beff28cb5b33abe88ad7874f96c
data/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2026-09-19
11
+
12
+ ### Changed
13
+
14
+ - `Formats::Protobuf.render` writes each family and each series straight
15
+ into the output behind a length prefix reserved first and filled in after
16
+ (`Fast::Protowire::Wire.reserve_length` / `close_length` /
17
+ `append_length_delimited_from`), instead of encoding the family header
18
+ to a String and copying every series out of a scratch buffer. Same
19
+ bytes; at 36,000 series x 12 labels a render went from 0.151 s and 29
20
+ objects to 0.144 s and 24, a protobuf scrape over HTTP from 0.162 s to
21
+ 0.151 s (0.200 s to 0.192 s with gzip). Benchmarks page and README
22
+ updated.
23
+
10
24
  ## [0.3.1] - 2026-09-19
11
25
 
12
26
  ### Changed
data/README.md CHANGED
@@ -94,13 +94,13 @@ Single process, locked, thread-safe by default (`benchmark-ips` and allocation
94
94
  comparisons against `prometheus-client` and `google-protobuf`; see
95
95
  [the benchmarks page](docs/explanation/benchmarks.md) for the full runs and conditions):
96
96
 
97
- - A protobuf scrape of 36,000 series renders in **29 objects**, where the
97
+ - A protobuf scrape of 36,000 series renders in **24 objects**, where the
98
98
  `google-protobuf` encoder needed 3.5 million Ruby objects and 504k native arenas, and
99
99
  in a hundredth of the GC time; taking the snapshot it reads is 1 ms and one Hash per metric
100
100
  - The text renderer allocates **66x fewer objects** than prometheus-client's formatter
101
101
  for the same body, and renders it 3.2x faster at 36,000 series
102
- - Bound counter and gauge writes allocate nothing and are **1.4x** faster than
103
- prometheus-client's; histogram observe is **3.0x** faster and summary observe **3.2x**
102
+ - Bound counter and gauge writes allocate nothing and are **1.45x** faster than
103
+ prometheus-client's; histogram observe is **3.1x** faster and summary observe **3.3x**
104
104
  - Native histogram observe runs at **1.32M i/s** with no prometheus-client equivalent
105
105
 
106
106
  ## Development
@@ -12,10 +12,10 @@ module Fast
12
12
  # Each family's header is a declared Proto::MetricFamily; the series
13
13
  # under it are written straight to the wire with Fast::Protowire::Wire,
14
14
  # no LabelPair/Metric/Counter objects, because a scrape's series count
15
- # is the one thing here that is large. A series' bytes go into one
16
- # scratch buffer reused for the whole render and are copied under the
17
- # family's metric tag, so rendering allocates per series only what
18
- # Array#pack needs for a double. Native histograms, few and irregular,
15
+ # is the one thing here that is large. Each series' bytes are written
16
+ # straight into the output behind a length prefix filled in after them,
17
+ # so rendering allocates nothing per series and copies nothing. Native
18
+ # histograms, few and irregular,
19
19
  # still go through Proto::Histogram. Bytes are identical to what the
20
20
  # declared classes (and google-protobuf) produce; the tests check.
21
21
  module Protobuf
@@ -64,32 +64,31 @@ module Fast
64
64
  DOUBLE_SIZE = 9
65
65
  private_constant :DOUBLE_SIZE
66
66
 
67
- # One varint-length-prefixed MetricFamily frame per metric.
67
+ # One varint-length-prefixed MetricFamily frame per metric, each
68
+ # written straight into the output behind a length prefix reserved
69
+ # first and filled in after (see Fast::Protowire::Wire).
68
70
  def self.render(snapshot)
69
- scratch = String.new
70
- snapshot.metrics.each_with_object(String.new) do |ms, buffer|
71
- family = encode_metric_family(ms, scratch)
72
- Fast::Protowire::Wire.append_varint(buffer, family.bytesize)
73
- buffer << family
71
+ snapshot.metrics.each_with_object(String.new) do |ms, out|
72
+ start = Fast::Protowire::Wire.reserve_length(out)
73
+ append_metric_family(out, ms)
74
+ Fast::Protowire::Wire.close_length(out, start)
74
75
  end
75
76
  end
76
77
 
77
78
  # A family's bytes are its header (name, help, type) followed by one
78
- # metric entry per series, each written into +scratch+ and copied.
79
- private_class_method def self.encode_metric_family(metric_snapshot, scratch)
80
- header = Proto::MetricFamily.new(name: metric_snapshot.name.name, help: metric_snapshot.docstring,
81
- type: TYPE_MAP.fetch(metric_snapshot.type))
79
+ # metric entry per series, in place. A family's series are alike, so
80
+ # the entry prefix width carries from one to the next.
81
+ private_class_method def self.append_metric_family(out, metric_snapshot)
82
+ Proto::MetricFamily.new(name: metric_snapshot.name.name, help: metric_snapshot.docstring,
83
+ type: TYPE_MAP.fetch(metric_snapshot.type)).encode(out)
82
84
  type = metric_snapshot.type
83
85
  names = metric_snapshot.label_names
84
- buffer = header.encode
86
+ width = 1
85
87
  metric_snapshot.series.each_pair do |values, value|
86
- scratch.clear
87
- append_metric(scratch, names, values, value, type)
88
- buffer << FAMILY_METRIC
89
- Fast::Protowire::Wire.append_varint(buffer, scratch.bytesize)
90
- buffer << scratch
88
+ width = Fast::Protowire::Wire.append_length_delimited_from(out, FAMILY_METRIC, width) do |buffer|
89
+ append_metric(buffer, names, values, value, type)
90
+ end
91
91
  end
92
- buffer
93
92
  end
94
93
 
95
94
  # Metric: labels (1) then the one value field for the type, in
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fast
4
4
  module Prometheus
5
- VERSION = "0.3.1"
5
+ VERSION = "0.3.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast-prometheus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Jacobs