maxmind-db-rust 0.4.0-x86_64-linux → 0.6.0-x86_64-linux

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: a96d9fc813a0f748b38ac9cb59d063863176f5f0981917675a3cc0b292ba16ca
4
- data.tar.gz: 7dfbeab46f5323f4fc3a6c55331d047a2b2138fe7c2e84a2bfe81b2832589bcc
3
+ metadata.gz: 9a5acfd1e34141f95708f52bc8ded3d7c7a66f3e265de27f5f6622cc54937dfd
4
+ data.tar.gz: 57eed3dd6edfc9ecfc9fa460ec6bcc9de1d84d74cf9a40e02539bc37a24548bd
5
5
  SHA512:
6
- metadata.gz: aeb73ff6afb0872cb895c2af4a876ad89df58b6f8222b083921afd88c0dd6bc26d17558e06ebac45a18201100cf4c47ca78c0cd4ef4cd6a31aaaa52f72756cd5
7
- data.tar.gz: 1eb7d959c5ca910c61a72f03a4f024119f692c2da2c1078486c6da8a3aa3e8b9a06475fb99f2b398a2f89f4e7a719723059782687c62466570a4fe81b8ad1c26
6
+ metadata.gz: e9081ef975be35d614db2ba467ace5729f2c0467bc5bac354d2e50d64526f7cfb48c87f152f1dbeea3eb4c816cfc97b5b5dedd5b0cbd7882b41367690df62611
7
+ data.tar.gz: fbf97ed0b4805005989354dcb972df5ffb59bb14f455273eec2e491746a4b074a0eebff7a00a456ffc67a7c11957e9ed5cecdca4fd5d5a10d7c7339f228d0300
data/CHANGELOG.md CHANGED
@@ -5,6 +5,107 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-07-19
9
+
10
+ ### Added
11
+
12
+ - Added `Reader#verify` for opt-in comprehensive database integrity checks
13
+ using the verifier introduced by `maxminddb` 0.30.0.
14
+ - Added iteration and shared-reader thread-scaling benchmarks, including a
15
+ cache-root retention check across thread churn.
16
+
17
+ ### Performance
18
+
19
+ - Batched Ruby array and hash insertion while decoding MMDB records to reduce
20
+ protected Ruby C API calls during full-record lookups.
21
+ - Replaced per-thread string caches with one fixed-size process cache that uses
22
+ its Ruby root array directly, improving reuse while bounding retained memory.
23
+ - Batched `get_many` and `get_many_path` result construction to reduce Ruby
24
+ array insertion overhead for large input collections.
25
+ - Improved `IPAddr` lookup performance by converting its public integer
26
+ representation directly instead of allocating packed address strings.
27
+ - Avoided allocating a temporary candidate vector on parsed-path cache hits.
28
+
29
+ ### Changed
30
+
31
+ - Upgraded the `maxminddb` crate to 0.30.0, improving selective path decoding
32
+ and hardening iteration and verification of corrupt databases.
33
+ - Documented selective lookup and input-type performance guidance, and
34
+ clarified that thread-safe lookups remain serialized by MRI's global VM lock.
35
+ - Documented the file-lifecycle safety contract for memory-mapped readers and
36
+ the requirement to atomically replace database paths during updates.
37
+ - Simplified reader ownership by removing redundant outer `Arc` wrappers and a
38
+ duplicate closed flag while preserving atomic close semantics.
39
+ - Removed the redundant extension-level Cargo lockfile; workspace builds use
40
+ the root lockfile as the single dependency lock.
41
+ - Moved MMDB-to-Ruby decoding and string-cache logic into a focused Rust module.
42
+ - Moved metadata storage and Ruby method registration into a focused Rust
43
+ module.
44
+ - Moved parsed-path ownership, hashing, matching, and conversion into a focused
45
+ Rust module.
46
+ - Moved reader behavior and Ruby method registration into a focused Rust module,
47
+ leaving the crate root responsible only for extension initialization.
48
+ - Moved extension namespace and cache-root setup into a focused initialization
49
+ module.
50
+ - Alternated baseline and candidate subprocesses between benchmark samples to
51
+ reduce order and thermal bias in git-ref comparisons.
52
+ - Added fixed-record and prebuilt-`IPAddr` benchmark cases for cache-hot and
53
+ input-conversion performance comparisons.
54
+ - Extended allocation benchmarking to cover selective batch lookups.
55
+
56
+ ### Fixed
57
+
58
+ - Made decoded MMDB strings consistently frozen regardless of whether their
59
+ length qualifies for the bounded string cache.
60
+ - Decoded MMDB UTF-8 strings and map keys directly into Ruby strings from raw
61
+ bytes, avoiding unchecked Rust strings while preserving Ruby's behavior for
62
+ invalid UTF-8 in corrupt databases.
63
+ - Propagated Ruby hash insertion errors from `Metadata#description` instead of
64
+ silently returning a partial description.
65
+
66
+ ## [0.5.0] - 2026-06-14
67
+
68
+ ### Added
69
+
70
+ - Added `MODE_FILE` as an official-gem compatibility alias for path-backed
71
+ memory-mapped readers.
72
+ - Added `MODE_PARAM_IS_BUFFER` for constructing readers from Ruby strings
73
+ containing database bytes.
74
+ - Added `Reader#get_path` and `Reader#get_many_path` for selective path lookups.
75
+ - Added `Reader#get_many` for batch lookups over arrays and enumerables.
76
+ - Added Enumerator return support for `Reader#each` when called without a block.
77
+ - Added `Reader#inspect` with closed state and database IP version.
78
+ - Added benchmark tooling for comparing git refs and measuring Ruby object
79
+ allocations.
80
+ - Added official gem parity tests, compatibility audit tests, bad data corpus
81
+ tests, and reader concurrency stress tests.
82
+
83
+ ### Changed
84
+
85
+ - Upgraded the `maxminddb` crate to 0.28.1.
86
+ - Improved IPv4 string lookup performance with a strict fast-path parser.
87
+ - Streamed non-array `get_many` and `get_many_path` inputs instead of
88
+ materializing enumerables.
89
+ - Cached parsed lookup paths per reader to reduce repeated path parsing.
90
+ - Simplified reader open mode parsing and centralized lookup error handling.
91
+ - Documented Rust extension safety invariants and `Send` requirements.
92
+
93
+ ### Fixed
94
+
95
+ - Fixed source gem native extension installation so `require "maxmind/db/rust"`
96
+ works after installing the source gem.
97
+ - Removed an unsafe reader iterator transmute.
98
+ - Improved invalid database error construction consistency across reader open
99
+ and iteration paths.
100
+ - Fixed dead test assertions and made adapted MaxMind tests independent of the
101
+ current working directory.
102
+
103
+ ### Security
104
+
105
+ - Pinned GitHub Actions to commit SHAs.
106
+ - Restricted workflow permissions and disabled persisted checkout credentials.
107
+ - Added a zizmor workflow and source-gem install smoke test to release checks.
108
+
8
109
  ## [0.4.0] - 2026-04-25
9
110
 
10
111
  ### Performance
@@ -113,3 +214,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
113
214
 
114
215
  - MODE_FILE support (use MODE_MMAP instead)
115
216
  - File descriptor support in constructor
217
+
218
+ [0.6.0]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.5.0...v0.6.0
219
+ [0.5.0]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.4.0...v0.5.0
220
+ [0.4.0]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.3.0...v0.4.0
221
+ [0.3.0]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.2.1...v0.3.0
222
+ [0.2.1]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.2.0...v0.2.1
223
+ [0.2.0]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.1.4...v0.2.0
224
+ [0.1.4]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.1.3...v0.1.4
225
+ [0.1.3]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.1.2...v0.1.3
226
+ [0.1.2]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.1.1...v0.1.2
227
+ [0.1.1]: https://github.com/oschwald/maxmind-db-rust-ruby/compare/v0.1.0...v0.1.1
228
+ [0.1.0]: https://github.com/oschwald/maxmind-db-rust-ruby/releases/tag/v0.1.0
data/README.md CHANGED
@@ -13,9 +13,10 @@ It keeps the API close to the official `maxmind-db` gem while adding Rust-backed
13
13
  - Rust implementation focused on fast lookups
14
14
  - API modeled after the official `maxmind-db` gem
15
15
  - Thread-safe lookups
16
- - Supports MMAP and in-memory modes
16
+ - Supports file-backed, MMAP, in-memory, and buffer-backed modes
17
17
  - Includes network iteration support
18
18
  - Accepts both `String` and `IPAddr` inputs
19
+ - Includes selective path lookup and batch lookup extensions
19
20
 
20
21
  ## Installation
21
22
 
@@ -81,6 +82,29 @@ puts "Prefix length: #{prefix_length}"
81
82
  reader.close
82
83
  ```
83
84
 
85
+ ### Selective and Batch Lookups
86
+
87
+ ```ruby
88
+ require 'maxmind/db/rust'
89
+
90
+ reader = MaxMind::DB::Rust::Reader.new('GeoIP2-City.mmdb')
91
+
92
+ # Decode one field without materializing the full record.
93
+ iso_code = reader.get_path('8.8.8.8', ['country', 'iso_code'])
94
+
95
+ # Batch full-record lookups.
96
+ ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222']
97
+ records = reader.get_many(ips)
98
+
99
+ # Batch one-field lookups.
100
+ iso_codes = reader.get_many_path(ips, ['country', 'iso_code'])
101
+
102
+ reader.close
103
+ ```
104
+
105
+ Decoded MMDB strings, including hash keys, are frozen. Call `dup` before
106
+ modifying a decoded string.
107
+
84
108
  ### Using IPAddr Objects
85
109
 
86
110
  ```ruby
@@ -112,13 +136,31 @@ reader = MaxMind::DB::Rust::Reader.new(
112
136
  mode: MaxMind::DB::Rust::MODE_MMAP
113
137
  )
114
138
 
139
+ # MODE_FILE: Official-gem compatibility alias for path-backed MMAP
140
+ reader = MaxMind::DB::Rust::Reader.new(
141
+ 'GeoIP2-City.mmdb',
142
+ mode: MaxMind::DB::Rust::MODE_FILE
143
+ )
144
+
115
145
  # MODE_MEMORY: Load entire database into memory
116
146
  reader = MaxMind::DB::Rust::Reader.new(
117
147
  'GeoIP2-City.mmdb',
118
148
  mode: MaxMind::DB::Rust::MODE_MEMORY
119
149
  )
150
+
151
+ # MODE_PARAM_IS_BUFFER: Read from a String containing database bytes
152
+ buffer = File.binread('GeoIP2-City.mmdb')
153
+ reader = MaxMind::DB::Rust::Reader.new(
154
+ buffer,
155
+ mode: MaxMind::DB::Rust::MODE_PARAM_IS_BUFFER
156
+ )
120
157
  ```
121
158
 
159
+ `MODE_AUTO`, `MODE_FILE`, and `MODE_MMAP` require the underlying file not to be
160
+ modified or truncated while the reader is alive. To refresh a database, write
161
+ the replacement to a new file and atomically rename it over the old path. Use
162
+ `MODE_MEMORY` if the file lifecycle cannot be controlled this way.
163
+
122
164
  ### Accessing Metadata
123
165
 
124
166
  ```ruby
@@ -176,15 +218,15 @@ reader.close
176
218
 
177
219
  ### `MaxMind::DB::Rust::Reader`
178
220
 
179
- #### `new(database_path, options = {})`
221
+ #### `new(database, options = {})`
180
222
 
181
223
  Create a new Reader instance.
182
224
 
183
225
  **Parameters:**
184
226
 
185
- - `database_path` (String): Path to the MaxMind DB file
227
+ - `database` (String): Path to the MaxMind DB file, or database bytes when using `:MODE_PARAM_IS_BUFFER`
186
228
  - `options` (Hash): Optional configuration
187
- - `:mode` (Symbol): One of `:MODE_AUTO`, `:MODE_MEMORY`, or `:MODE_MMAP`
229
+ - `:mode` (Symbol): One of `:MODE_AUTO`, `:MODE_FILE`, `:MODE_MEMORY`, `:MODE_MMAP`, or `:MODE_PARAM_IS_BUFFER`
188
230
 
189
231
  **Returns:** Reader instance
190
232
 
@@ -208,6 +250,17 @@ Look up an IP address in the database.
208
250
  - `ArgumentError`: If looking up IPv6 in an IPv4-only database
209
251
  - `MaxMind::DB::Rust::InvalidDatabaseError`: If the database is corrupt
210
252
 
253
+ #### `get_path(ip_address, path)`
254
+
255
+ Look up an IP address and return only the value at `path`.
256
+
257
+ **Parameters:**
258
+
259
+ - `ip_address` (String or IPAddr): The IP address to look up
260
+ - `path` (Array): String map keys and Integer array indexes. Negative indexes count from the end.
261
+
262
+ **Returns:** The value at the path, or `nil` if the record or path is not found
263
+
211
264
  #### `get_with_prefix_length(ip_address)`
212
265
 
213
266
  Look up an IP address and return the prefix length.
@@ -218,12 +271,45 @@ Look up an IP address and return the prefix length.
218
271
 
219
272
  **Returns:** Array `[record, prefix_length]` where record is a Hash or `nil`
220
273
 
274
+ #### `get_many(ip_addresses)`
275
+
276
+ Look up multiple IP addresses.
277
+
278
+ **Parameters:**
279
+
280
+ - `ip_addresses` (Array or Enumerable): IP address strings or IPAddr objects
281
+
282
+ **Returns:** Array of record values in input order
283
+
284
+ #### `get_many_path(ip_addresses, path)`
285
+
286
+ Look up one path for multiple IP addresses.
287
+
288
+ **Parameters:**
289
+
290
+ - `ip_addresses` (Array or Enumerable): IP address strings or IPAddr objects
291
+ - `path` (Array): String map keys and Integer array indexes
292
+
293
+ **Returns:** Array of path values in input order
294
+
221
295
  #### `metadata()`
222
296
 
223
297
  Get metadata about the database.
224
298
 
225
299
  **Returns:** `MaxMind::DB::Rust::Metadata` instance
226
300
 
301
+ #### `verify()`
302
+
303
+ Perform a comprehensive validation of the database metadata, search tree, data
304
+ section separator, and referenced data records. Verification traverses the
305
+ entire database and may use memory proportional to the number of distinct
306
+ referenced values, so it is intended for explicit integrity checks rather than
307
+ the lookup hot path.
308
+
309
+ **Returns:** `true` when the database passes verification
310
+
311
+ **Raises:** `MaxMind::DB::Rust::InvalidDatabaseError` when verification fails
312
+
227
313
  #### `close()`
228
314
 
229
315
  Close the database and release resources.
@@ -269,8 +355,10 @@ Metadata attributes:
269
355
  ### Constants
270
356
 
271
357
  - `MaxMind::DB::Rust::MODE_AUTO` - Automatically choose the best mode (uses MMAP)
358
+ - `MaxMind::DB::Rust::MODE_FILE` - Official-gem compatibility alias for path-backed MMAP
272
359
  - `MaxMind::DB::Rust::MODE_MEMORY` - Load entire database into memory
273
360
  - `MaxMind::DB::Rust::MODE_MMAP` - Use memory-mapped file I/O (recommended)
361
+ - `MaxMind::DB::Rust::MODE_PARAM_IS_BUFFER` - Read database bytes from a Ruby String
274
362
 
275
363
  ### Exceptions
276
364
 
@@ -278,27 +366,35 @@ Metadata attributes:
278
366
 
279
367
  ## Comparison with Official Gem
280
368
 
281
- | Feature | maxmind-db (official) | maxmind-db-rust (this gem) |
282
- | ---------------- | --------------------- | ------------------------------------------ |
283
- | Implementation | Pure Ruby | Rust with Ruby bindings |
284
- | Performance | Baseline | Faster lookup throughput in our benchmarks |
285
- | API | MaxMind::DB | MaxMind::DB::Rust |
286
- | MODE_FILE | ✓ | |
287
- | MODE_MEMORY | ✓ | ✓ |
288
- | MODE_AUTO | ✓ | ✓ |
289
- | MODE_MMAP | | ✓ |
290
- | Iterator support | ✗ | ✓ |
291
- | Thread-safe | | ✓ |
369
+ | Feature | maxmind-db (official) | maxmind-db-rust (this gem) |
370
+ | -------------------- | --------------------- | ------------------------------------------ |
371
+ | Implementation | Pure Ruby | Rust with Ruby bindings |
372
+ | Performance | Baseline | Faster lookup throughput in our benchmarks |
373
+ | API | MaxMind::DB | MaxMind::DB::Rust |
374
+ | MODE_FILE | ✓ | |
375
+ | MODE_MEMORY | ✓ | ✓ |
376
+ | MODE_AUTO | ✓ | ✓ |
377
+ | MODE_PARAM_IS_BUFFER | | ✓ |
378
+ | MODE_MMAP | ✗ | ✓ |
379
+ | Iterator support | | ✓ |
380
+ | Thread-safe | ✓ | ✓ |
292
381
 
293
382
  ## Performance
294
383
 
295
384
  Lookup performance depends on hardware, Ruby version, database, and workload.
296
385
 
297
386
  - In this project’s random-lookup benchmarks, this gem is consistently faster than the official Ruby implementation.
298
- - On `/var/lib/GeoIP/GeoIP2-City.mmdb` in this environment, random lookup throughput was about `47x` higher than the official gem.
299
387
  - `MODE_MMAP` and `MODE_MEMORY` both perform well; which is faster can vary by environment.
300
- - For reproducible numbers on your own data, run `benchmark/compare_lookups.rb` against your database.
301
- - Safe for concurrent lookups across threads.
388
+ - Prefer `get_path` or `get_many_path` when only a small part of a record is
389
+ needed. Selective decoding avoids constructing the rest of the Ruby object
390
+ graph and can be substantially faster than a full-record lookup.
391
+ - Textual IP address strings use the fastest input path. `IPAddr` objects are
392
+ supported but require Ruby method calls to extract their address family and
393
+ integer value.
394
+ - For current, reproducible numbers on your own data and Ruby version, run `benchmark/compare_lookups.rb` against your database.
395
+ - Readers are safe to share across Ruby threads. Ruby-facing lookup and decode
396
+ work currently runs under MRI's global VM lock, so additional Ruby threads do
397
+ not make one process perform lookups in parallel.
302
398
 
303
399
  ## Development
304
400
 
@@ -10,7 +10,7 @@ module MaxMind
10
10
  # - Reader class
11
11
  # - Metadata class
12
12
  # - InvalidDatabaseError exception
13
- # - MODE_AUTO, MODE_MEMORY, MODE_MMAP constants
13
+ # - MODE_AUTO, MODE_FILE, MODE_MEMORY, MODE_MMAP, MODE_PARAM_IS_BUFFER constants
14
14
  end
15
15
  end
16
16
  end
Binary file
Binary file
Binary file
Binary file
@@ -66,7 +66,7 @@ module MaxMind
66
66
  # - Reader class
67
67
  # - Metadata class
68
68
  # - InvalidDatabaseError exception
69
- # - MODE_AUTO, MODE_MEMORY, MODE_MMAP constants
69
+ # - MODE_AUTO, MODE_FILE, MODE_MEMORY, MODE_MMAP, MODE_PARAM_IS_BUFFER constants
70
70
  end
71
71
  else
72
72
  # Official gem not loaded - define DB as a module
@@ -128,7 +128,7 @@ module MaxMind
128
128
  # - Reader class
129
129
  # - Metadata class
130
130
  # - InvalidDatabaseError exception
131
- # - MODE_AUTO, MODE_MEMORY, MODE_MMAP constants
131
+ # - MODE_AUTO, MODE_FILE, MODE_MEMORY, MODE_MMAP, MODE_PARAM_IS_BUFFER constants
132
132
  end
133
133
  end
134
134
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maxmind-db-rust
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Gregory Oschwald
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-25 00:00:00.000000000 Z
11
+ date: 2026-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parallel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "<"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement