Performance
OpaqueId is designed for efficient ID generation with optimized algorithms and memory usage. This guide covers performance characteristics, optimization strategies, and scalability considerations.
- TOC
Performance Characteristics
Generation Speed
OpaqueId is designed for efficient ID generation with optimized algorithms for different alphabet sizes and ID lengths.
Algorithm Performance
- Fast Path (64-character alphabets): Uses bitwise operations for efficient generation with no rejection sampling overhead
- Unbiased Path (Other alphabets): Uses rejection sampling for unbiased distribution with slight performance overhead
- Performance scales linearly with ID length and batch size
ID Length Impact
- Shorter IDs: Faster generation due to less computation
- Longer IDs: More secure but require more computation
- Memory usage: Scales linearly with ID length and batch size
Memory Usage
OpaqueId is memory-efficient with predictable memory consumption patterns.
Memory Characteristics
- Efficient memory usage: Minimal memory overhead per ID generation
- Linear scaling: Memory usage scales predictably with ID length and batch size
- Garbage collection: Minimal impact on garbage collection due to efficient string handling
Memory Optimization
# Efficient batch generation
def generate_batch_efficient(count, size, alphabet)
# Pre-allocate result array
results = Array.new(count)
# Generate IDs in batches to control memory
batch_size = 10000
count.times_slice(batch_size) do |batch|
batch.each_with_index do |_, i|
results[i] = OpaqueId.generate(size: size, alphabet: alphabet)
end
# Force garbage collection for large batches
GC.start if count > 100000
end
results
end
Collision Probability
OpaqueId provides extremely low collision probabilities for practical use cases.
Collision Analysis
The collision probability depends on:
- ID length: Longer IDs have exponentially lower collision probability
- Alphabet size: Larger alphabets provide more entropy per character
- Total IDs generated: Collision probability increases with the square of the number of IDs
Mathematical Foundation
- 21-character alphanumeric (62 chars): ~125 bits of entropy
- 21-character standard (64 chars): ~126 bits of entropy
- Collision probability: Follows the birthday paradox formula
- Practical safety: Collisions are extremely unlikely for typical use cases
Performance Characteristics
Algorithm Performance
Fast Path (64-character alphabets)
# Optimized for 64-character alphabets
# Uses bitwise operations for maximum speed
# No rejection sampling overhead
# Linear time complexity: O(n)
Unbiased Path (Other alphabets)
# Uses rejection sampling for unbiased distribution
# Slight performance overhead for uniformity
# Linear time complexity: O(n × rejection_rate)
Scalability Analysis
Linear Scaling
- ID length: Performance scales linearly with ID length
- Batch size: Performance remains consistent across different batch sizes
- Memory usage: Scales predictably with both ID length and batch size
Real-World Performance
ActiveRecord Integration
OpaqueId adds minimal overhead to ActiveRecord operations:
- Automatic generation: IDs are generated during model creation
- Minimal performance impact: Generation overhead is typically negligible
- Memory efficient: No significant memory overhead per record
Batch Operations
# Bulk insert with pre-generated IDs
ids = 100000.times.map { OpaqueId.generate }
users_data = ids.map.with_index do |id, index|
{ opaque_id: id, name: "User #{index + 1}" }
end
User.insert_all(users_data)
# Much faster than individual creates
API Performance
- Database indexing: Ensure proper indexes on opaque_id columns for optimal lookup performance
- Query performance: Lookups by opaque_id should be fast with proper indexing
- Response times: Performance depends on database configuration and indexing
Optimization Tips
1. Choose Optimal Alphabet Size
# For maximum performance, use 64-character alphabets
class User < ApplicationRecord
include OpaqueId::Model
# Fastest generation
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
end
# For specific requirements, consider performance impact
class Order < ApplicationRecord
include OpaqueId::Model
# Numeric-only (slower but meets requirements)
self.opaque_id_alphabet = "0123456789"
end
2. Optimize ID Length
# Shorter IDs for better performance
class ShortUrl < ApplicationRecord
include OpaqueId::Model
# 8 characters: faster generation
self.opaque_id_length = 8
end
# Longer IDs for better security
class ApiKey < ApplicationRecord
include OpaqueId::Model
# 32 characters: higher security
self.opaque_id_length = 32
end
3. Batch Generation
# Generate IDs in batches for better performance
def generate_batch_ids(count, size: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
# Pre-allocate array
results = Array.new(count)
# Generate all IDs
count.times do |i|
results[i] = OpaqueId.generate(size: size, alphabet: alphabet)
end
results
end
# Usage
ids = generate_batch_ids(10000)
# Much faster than individual calls
4. Memory Management
# For large-scale generation, manage memory carefully
def generate_large_batch(count, size: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
results = []
batch_size = 10000
(count / batch_size).times do
batch = batch_size.times.map { OpaqueId.generate(size: size, alphabet: alphabet) }
results.concat(batch)
# Force garbage collection for large batches
GC.start if count > 100000
end
results
end
5. Database Optimization
# Ensure proper database indexing
class AddOpaqueIdToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :opaque_id, :string
add_index :users, :opaque_id, unique: true
end
end
# Use database-specific optimizations
# PostgreSQL: Partial indexes for specific use cases
# MySQL: Use appropriate storage engine (InnoDB)
# SQLite: Consider WAL mode for better concurrency
Performance Testing
Benchmark Scripts
# Basic performance benchmark
require 'benchmark'
def benchmark_generation(count = 1000000)
puts "Generating #{count} opaque IDs..."
time = Benchmark.measure do
count.times { OpaqueId.generate }
end
puts "Time: #{time.real.round(2)} seconds"
puts "Rate: #{(count / time.real).round(0)} IDs/second"
puts "Memory: #{`ps -o rss= -p #{Process.pid}`.to_i / 1024}MB"
end
# Run benchmark
benchmark_generation(1000000)
Memory Profiling
# Memory usage analysis
require 'memory_profiler'
def profile_memory(count = 100000)
report = MemoryProfiler.report do
count.times { OpaqueId.generate }
end
puts "Total allocated: #{report.total_allocated_memsize / 1024 / 1024}MB"
puts "Total retained: #{report.total_retained_memsize / 1024 / 1024}MB"
puts "Objects allocated: #{report.total_allocated}"
puts "Objects retained: #{report.total_retained}"
end
# Run memory profile
profile_memory(100000)
Load Testing
# Concurrent generation testing
require 'concurrent'
def load_test(threads = 10, ids_per_thread = 100000)
puts "Testing #{threads} threads, #{ids_per_thread} IDs each..."
start_time = Time.now
# Create thread pool
pool = Concurrent::FixedThreadPool.new(threads)
# Submit tasks
futures = threads.times.map do
pool.post do
ids_per_thread.times.map { OpaqueId.generate }
end
end
# Wait for completion
results = futures.map(&:value!)
end_time = Time.now
total_ids = threads * ids_per_thread
duration = end_time - start_time
puts "Total IDs: #{total_ids}"
puts "Duration: #{duration.round(2)} seconds"
puts "Rate: #{(total_ids / duration).round(0)} IDs/second"
puts "Per thread: #{(ids_per_thread / duration).round(0)} IDs/second"
pool.shutdown
pool.wait_for_termination
end
# Run load test
load_test(10, 100000)
Performance Comparison
vs. Other ID Generation Methods
OpaqueId compares favorably to other ID generation methods:
- SecureRandom.hex: OpaqueId provides more customization and collision handling
- SecureRandom.uuid: OpaqueId offers configurable length and alphabet
- NanoID (gem): OpaqueId uses native Ruby methods without external dependencies
- Database auto-increment: OpaqueId provides security and URL-safety at minimal performance cost
vs. Database-Generated IDs
# Database auto-increment vs OpaqueId
# Database auto-increment
# Pros: Fast, sequential, small storage
# Cons: Predictable, not URL-safe, reveals count
# OpaqueId
# Pros: Unpredictable, URL-safe, configurable
# Cons: Slightly slower, larger storage
Production Considerations
1. Monitoring
# Add performance monitoring
class User < ApplicationRecord
include OpaqueId::Model
private
def set_opaque_id
start_time = Time.now
self.opaque_id = generate_opaque_id
duration = Time.now - start_time
# Log slow generation
Rails.logger.warn "Slow opaque_id generation: #{duration}ms" if duration > 0.001
end
end
2. Caching
# Cache generated IDs for frequently accessed records
class User < ApplicationRecord
include OpaqueId::Model
def opaque_id
@opaque_id ||= super
end
end
3. Background Generation
# Generate IDs in background for bulk operations
class BulkUserCreator
def self.create_users(count)
# Generate IDs in background
ids = Concurrent::Future.execute do
count.times.map { OpaqueId.generate }
end
# Create users with pre-generated IDs
users_data = ids.value.map.with_index do |id, index|
{ opaque_id: id, name: "User #{index + 1}" }
end
User.insert_all(users_data)
end
end
4. Error Handling
# Handle generation failures gracefully
class User < ApplicationRecord
include OpaqueId::Model
private
def set_opaque_id
retries = 0
max_retries = opaque_id_max_retry
begin
self.opaque_id = generate_opaque_id
rescue OpaqueId::GenerationError => e
retries += 1
if retries < max_retries
retry
else
Rails.logger.error "Failed to generate opaque_id after #{max_retries} retries: #{e.message}"
raise
end
end
end
end
Best Practices
1. Choose Appropriate Configuration
# High-performance applications
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
self.opaque_id_length = 21
# High-security applications
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
self.opaque_id_length = 32
# Human-readable applications
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
self.opaque_id_length = 15
2. Optimize for Your Use Case
# Short URLs - prioritize performance
self.opaque_id_length = 8
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
# API keys - prioritize security
self.opaque_id_length = 32
self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
# User IDs - balance performance and security
self.opaque_id_length = 21
self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
3. Monitor Performance
# Add performance monitoring
# Track generation time
# Monitor memory usage
# Alert on slow generation
# Log performance metrics
Next Steps
Now that you understand performance characteristics:
- Explore Algorithms for technical algorithm details
- Check out Security for security considerations
- Review Configuration for performance optimization
- Read API Reference for complete performance documentation