A Ruby gem for reading MaxMind DB files, implemented in Rust.
It keeps the API close to the official maxmind-db gem while adding Rust-backed performance.
Note: This is an unofficial library and is not endorsed by MaxMind. For the official Ruby library, see maxmind-db.
- Rust implementation focused on fast lookups
- API modeled after the official
maxmind-dbgem - Thread-safe lookups
- Supports file-backed, MMAP, in-memory, and buffer-backed modes
- Includes network iteration support
- Accepts both
StringandIPAddrinputs - Includes selective path lookup and batch lookup extensions
Add this line to your application's Gemfile:
gem 'maxmind-db-rust'And then execute:
bundle installOr install it yourself as:
gem install maxmind-db-rust- Ruby 3.2 or higher
- Rust toolchain (for building from source)
require 'maxmind/db/rust'
# Open database
reader = MaxMind::DB::Rust::Reader.new(
'GeoIP2-City.mmdb',
mode: MaxMind::DB::Rust::MODE_MEMORY
)
# Lookup an IP address
record = reader.get('8.8.8.8')
if record
puts record['country']['iso_code']
puts record['country']['names']['en']
puts record['city']['names']['en']
end
# Close the database
reader.closerequire 'maxmind/db/rust'
reader = MaxMind::DB::Rust::Reader.new('GeoIP2-City.mmdb')
record, prefix_length = reader.get_with_prefix_length('8.8.8.8')
puts "Record: #{record}"
puts "Prefix length: #{prefix_length}"
reader.closerequire 'maxmind/db/rust'
reader = MaxMind::DB::Rust::Reader.new('GeoIP2-City.mmdb')
# Decode one field without materializing the full record.
iso_code = reader.get_path('8.8.8.8', ['country', 'iso_code'])
# Batch full-record lookups.
ips = ['8.8.8.8', '1.1.1.1', '208.67.222.222']
records = reader.get_many(ips)
# Batch one-field lookups.
iso_codes = reader.get_many_path(ips, ['country', 'iso_code'])
reader.closeDecoded MMDB strings, including hash keys, are frozen. Call dup before
modifying a decoded string.
require 'maxmind/db/rust'
require 'ipaddr'
reader = MaxMind::DB::Rust::Reader.new('GeoIP2-City.mmdb')
ip = IPAddr.new('8.8.8.8')
record = reader.get(ip)
reader.closerequire 'maxmind/db/rust'
# MODE_AUTO: Uses memory-mapped files (default, best performance)
reader = MaxMind::DB::Rust::Reader.new(
'GeoIP2-City.mmdb',
mode: MaxMind::DB::Rust::MODE_AUTO
)
# MODE_MMAP: Explicitly use memory-mapped files (recommended)
reader = MaxMind::DB::Rust::Reader.new(
'GeoIP2-City.mmdb',
mode: MaxMind::DB::Rust::MODE_MMAP
)
# MODE_FILE: Official-gem compatibility alias for path-backed MMAP
reader = MaxMind::DB::Rust::Reader.new(
'GeoIP2-City.mmdb',
mode: MaxMind::DB::Rust::MODE_FILE
)
# MODE_MEMORY: Load entire database into memory
reader = MaxMind::DB::Rust::Reader.new(
'GeoIP2-City.mmdb',
mode: MaxMind::DB::Rust::MODE_MEMORY
)
# MODE_PARAM_IS_BUFFER: Read from a String containing database bytes
buffer = File.binread('GeoIP2-City.mmdb')
reader = MaxMind::DB::Rust::Reader.new(
buffer,
mode: MaxMind::DB::Rust::MODE_PARAM_IS_BUFFER
)MODE_AUTO, MODE_FILE, and MODE_MMAP require the underlying file not to be
modified or truncated while the reader is alive. To refresh a database, write
the replacement to a new file and atomically rename it over the old path. Use
MODE_MEMORY if the file lifecycle cannot be controlled this way.
require 'maxmind/db/rust'
reader = MaxMind::DB::Rust::Reader.new('GeoIP2-City.mmdb')
metadata = reader.metadata
puts "Database type: #{metadata.database_type}"
puts "Node count: #{metadata.node_count}"
puts "Record size: #{metadata.record_size}"
puts "IP version: #{metadata.ip_version}"
puts "Build epoch: #{metadata.build_epoch}"
puts "Languages: #{metadata.languages.join(', ')}"
puts "Description: #{metadata.description}"
reader.closeIterate over all networks in the database:
require 'maxmind/db/rust'
reader = MaxMind::DB::Rust::Reader.new('GeoLite2-Country.mmdb')
# Iterate over all networks
reader.each do |network, data|
puts "#{network}: #{data['country']['iso_code']}"
break # Remove this to see all networks
end
# Iterate over networks within a specific subnet (String CIDR notation)
reader.each('192.168.0.0/16') do |network, data|
puts "#{network}: #{data['city']['names']['en']}"
end
# Iterate over networks within a specific subnet (IPAddr object)
require 'ipaddr'
subnet = IPAddr.new('10.0.0.0/8')
reader.each(subnet) do |network, data|
puts "#{network}: #{data['country']['iso_code']}"
end
# Use Enumerable methods
countries = reader.map { |network, data| data['country']['iso_code'] }.uniq
puts "Unique countries: #{countries.size}"
reader.closeCreate a new Reader instance.
Parameters:
database(String): Path to the MaxMind DB file, or database bytes when using:MODE_PARAM_IS_BUFFERoptions(Hash): Optional configuration:mode(Symbol): One of:MODE_AUTO,:MODE_FILE,:MODE_MEMORY,:MODE_MMAP, or:MODE_PARAM_IS_BUFFER
Returns: Reader instance
Raises:
Errno::ENOENT: If the database file does not existMaxMind::DB::Rust::InvalidDatabaseError: If the file is not a valid MaxMind DB
Look up an IP address in the database.
Parameters:
ip_address(String or IPAddr): The IP address to look up
Returns: Hash with the record data, or nil if not found
Raises:
ArgumentError: If looking up IPv6 in an IPv4-only databaseMaxMind::DB::Rust::InvalidDatabaseError: If the database is corrupt
Look up an IP address and return only the value at path.
Parameters:
ip_address(String or IPAddr): The IP address to look uppath(Array): String map keys and Integer array indexes. Negative indexes count from the end.
Returns: The value at the path, or nil if the record or path is not found
Look up an IP address and return the prefix length.
Parameters:
ip_address(String or IPAddr): The IP address to look up
Returns: Array [record, prefix_length] where record is a Hash or nil
Look up multiple IP addresses.
Parameters:
ip_addresses(Array or Enumerable): IP address strings or IPAddr objects
Returns: Array of record values in input order
Look up one path for multiple IP addresses.
Parameters:
ip_addresses(Array or Enumerable): IP address strings or IPAddr objectspath(Array): String map keys and Integer array indexes
Returns: Array of path values in input order
Get metadata about the database.
Returns: MaxMind::DB::Rust::Metadata instance
Perform a comprehensive validation of the database metadata, search tree, data section separator, and referenced data records. Verification traverses the entire database and may use memory proportional to the number of distinct referenced values, so it is intended for explicit integrity checks rather than the lookup hot path.
Returns: true when the database passes verification
Raises: MaxMind::DB::Rust::InvalidDatabaseError when verification fails
Close the database and release resources.
Check if the database has been closed.
Returns: Boolean
Iterate over networks in the database.
Parameters:
network(String or IPAddr, optional): Network CIDR to iterate within (e.g., "192.168.0.0/16"). If omitted, iterates over all networks in the database.
Yields: IPAddr network and Hash data for each entry
Returns: Enumerator if no block given
Raises:
ArgumentError: If network CIDR is invalid or IPv6 network specified for IPv4-only database
Metadata attributes:
binary_format_major_version- Major version of the binary formatbinary_format_minor_version- Minor version of the binary formatbuild_epoch- Unix timestamp when the database was builtdatabase_type- Type of database (e.g., "GeoIP2-City")description- Hash of locale codes to descriptionsip_version- 4 for IPv4-only, 6 for IPv4/IPv6 supportlanguages- Array of supported locale codesnode_count- Number of nodes in the search treerecord_size- Record size in bits (24, 28, or 32)node_byte_size- Size of a node in bytessearch_tree_size- Size of the search tree in bytes
MaxMind::DB::Rust::MODE_AUTO- Automatically choose the best mode (uses MMAP)MaxMind::DB::Rust::MODE_FILE- Official-gem compatibility alias for path-backed MMAPMaxMind::DB::Rust::MODE_MEMORY- Load entire database into memoryMaxMind::DB::Rust::MODE_MMAP- Use memory-mapped file I/O (recommended)MaxMind::DB::Rust::MODE_PARAM_IS_BUFFER- Read database bytes from a Ruby String
MaxMind::DB::Rust::InvalidDatabaseError- Raised when the database file is corrupt or invalid
| Feature | maxmind-db (official) | maxmind-db-rust (this gem) |
|---|---|---|
| Implementation | Pure Ruby | Rust with Ruby bindings |
| Performance | Baseline | Faster lookup throughput in our benchmarks |
| API | MaxMind::DB | MaxMind::DB::Rust |
| MODE_FILE | ✓ | ✓ |
| MODE_MEMORY | ✓ | ✓ |
| MODE_AUTO | ✓ | ✓ |
| MODE_PARAM_IS_BUFFER | ✓ | ✓ |
| MODE_MMAP | ✗ | ✓ |
| Iterator support | ✗ | ✓ |
| Thread-safe | ✓ | ✓ |
Lookup performance depends on hardware, Ruby version, database, and workload.
- In this project’s random-lookup benchmarks, this gem is consistently faster than the official Ruby implementation.
MODE_MMAPandMODE_MEMORYboth perform well; which is faster can vary by environment.- Prefer
get_pathorget_many_pathwhen only a small part of a record is needed. Selective decoding avoids constructing the rest of the Ruby object graph and can be substantially faster than a full-record lookup. - Textual IP address strings use the fastest input path.
IPAddrobjects are supported but require Ruby method calls to extract their address family and integer value. - For current, reproducible numbers on your own data and Ruby version, run
benchmark/compare_lookups.rbagainst your database. - Readers are safe to share across Ruby threads. Ruby-facing lookup and decode work currently runs under MRI's global VM lock, so additional Ruby threads do not make one process perform lookups in parallel.
See CONTRIBUTING.md for developer documentation, including:
- Development setup and prerequisites
- Building and testing the extension
- Code quality guidelines
- Project structure
- Submitting changes
git clone https://github.com/oschwald/maxmind-db-rust-ruby.git
cd maxmind-db-rust-ruby
git submodule update --init --recursive
bundle install
bundle exec rake compile
bundle exec rake test- Fork the repository
- Create a feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Describe your change') - Push to the branch (
git push origin my-new-feature) - Open a Pull Request
This software is licensed under the ISC License. See the LICENSE file for details.
- Issues: https://github.com/oschwald/maxmind-db-rust-ruby/issues
- Documentation: https://www.rubydoc.info/gems/maxmind-db-rust
This gem uses the maxminddb Rust crate for the core MaxMind DB reading functionality.