opaque_id 1.2.0 → 1.4.0

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.
@@ -0,0 +1,491 @@
1
+ ---
2
+ layout: default
3
+ title: Performance
4
+ nav_order: 8
5
+ description: "Performance benchmarks, optimization tips, and scalability considerations"
6
+ permalink: /performance/
7
+ ---
8
+
9
+ # Performance
10
+
11
+ OpaqueId is designed for high performance with optimized algorithms and efficient memory usage. This guide covers benchmarks, optimization strategies, and scalability considerations.
12
+
13
+ - TOC
14
+ {:toc}
15
+
16
+ ## Performance Characteristics
17
+
18
+ ### Generation Speed
19
+
20
+ OpaqueId is designed for high performance with optimized algorithms for different alphabet sizes and ID lengths.
21
+
22
+ #### Algorithm Performance
23
+
24
+ - **Fast Path (64-character alphabets)**: Uses bitwise operations for maximum speed with no rejection sampling overhead
25
+ - **Unbiased Path (Other alphabets)**: Uses rejection sampling for unbiased distribution with slight performance overhead
26
+ - **Performance scales linearly** with ID length and batch size
27
+
28
+ #### ID Length Impact
29
+
30
+ - **Shorter IDs**: Faster generation due to less computation
31
+ - **Longer IDs**: More secure but require more computation
32
+ - **Memory usage**: Scales linearly with ID length and batch size
33
+
34
+ ### Memory Usage
35
+
36
+ OpaqueId is memory-efficient with predictable memory consumption patterns.
37
+
38
+ #### Memory Characteristics
39
+
40
+ - **Efficient memory usage**: Minimal memory overhead per ID generation
41
+ - **Linear scaling**: Memory usage scales predictably with ID length and batch size
42
+ - **Garbage collection**: Minimal impact on garbage collection due to efficient string handling
43
+
44
+ #### Memory Optimization
45
+
46
+ ```ruby
47
+ # Efficient batch generation
48
+ def generate_batch_efficient(count, size, alphabet)
49
+ # Pre-allocate result array
50
+ results = Array.new(count)
51
+
52
+ # Generate IDs in batches to control memory
53
+ batch_size = 10000
54
+ count.times_slice(batch_size) do |batch|
55
+ batch.each_with_index do |_, i|
56
+ results[i] = OpaqueId.generate(size: size, alphabet: alphabet)
57
+ end
58
+
59
+ # Force garbage collection for large batches
60
+ GC.start if count > 100000
61
+ end
62
+
63
+ results
64
+ end
65
+ ```
66
+
67
+ ### Collision Probability
68
+
69
+ OpaqueId provides extremely low collision probabilities for practical use cases.
70
+
71
+ #### Collision Analysis
72
+
73
+ The collision probability depends on:
74
+
75
+ - **ID length**: Longer IDs have exponentially lower collision probability
76
+ - **Alphabet size**: Larger alphabets provide more entropy per character
77
+ - **Total IDs generated**: Collision probability increases with the square of the number of IDs
78
+
79
+ #### Mathematical Foundation
80
+
81
+ - **21-character alphanumeric (62 chars)**: ~125 bits of entropy
82
+ - **21-character standard (64 chars)**: ~126 bits of entropy
83
+ - **Collision probability**: Follows the birthday paradox formula
84
+ - **Practical safety**: Collisions are extremely unlikely for typical use cases
85
+
86
+ ## Performance Characteristics
87
+
88
+ ### Algorithm Performance
89
+
90
+ #### Fast Path (64-character alphabets)
91
+
92
+ ```ruby
93
+ # Optimized for 64-character alphabets
94
+ # Uses bitwise operations for maximum speed
95
+ # No rejection sampling overhead
96
+ # Linear time complexity: O(n)
97
+ ```
98
+
99
+ #### Unbiased Path (Other alphabets)
100
+
101
+ ```ruby
102
+ # Uses rejection sampling for unbiased distribution
103
+ # Slight performance overhead for uniformity
104
+ # Linear time complexity: O(n × rejection_rate)
105
+ ```
106
+
107
+ ### Scalability Analysis
108
+
109
+ #### Linear Scaling
110
+
111
+ - **ID length**: Performance scales linearly with ID length
112
+ - **Batch size**: Performance remains consistent across different batch sizes
113
+ - **Memory usage**: Scales predictably with both ID length and batch size
114
+
115
+ ## Real-World Performance
116
+
117
+ ### ActiveRecord Integration
118
+
119
+ OpaqueId adds minimal overhead to ActiveRecord operations:
120
+
121
+ - **Automatic generation**: IDs are generated during model creation
122
+ - **Minimal performance impact**: Generation overhead is typically negligible
123
+ - **Memory efficient**: No significant memory overhead per record
124
+
125
+ ### Batch Operations
126
+
127
+ ```ruby
128
+ # Bulk insert with pre-generated IDs
129
+ ids = 100000.times.map { OpaqueId.generate }
130
+
131
+ users_data = ids.map.with_index do |id, index|
132
+ { opaque_id: id, name: "User #{index + 1}" }
133
+ end
134
+
135
+ User.insert_all(users_data)
136
+ # Much faster than individual creates
137
+ ```
138
+
139
+ ### API Performance
140
+
141
+ - **Database indexing**: Ensure proper indexes on opaque_id columns for optimal lookup performance
142
+ - **Query performance**: Lookups by opaque_id should be fast with proper indexing
143
+ - **Response times**: Performance depends on database configuration and indexing
144
+
145
+ ## Optimization Tips
146
+
147
+ ### 1. Choose Optimal Alphabet Size
148
+
149
+ ```ruby
150
+ # For maximum performance, use 64-character alphabets
151
+ class User < ApplicationRecord
152
+ include OpaqueId::Model
153
+
154
+ # Fastest generation
155
+ self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
156
+ end
157
+
158
+ # For specific requirements, consider performance impact
159
+ class Order < ApplicationRecord
160
+ include OpaqueId::Model
161
+
162
+ # Numeric-only (slower but meets requirements)
163
+ self.opaque_id_alphabet = "0123456789"
164
+ end
165
+ ```
166
+
167
+ ### 2. Optimize ID Length
168
+
169
+ ```ruby
170
+ # Shorter IDs for better performance
171
+ class ShortUrl < ApplicationRecord
172
+ include OpaqueId::Model
173
+
174
+ # 8 characters: faster generation
175
+ self.opaque_id_length = 8
176
+ end
177
+
178
+ # Longer IDs for better security
179
+ class ApiKey < ApplicationRecord
180
+ include OpaqueId::Model
181
+
182
+ # 32 characters: higher security
183
+ self.opaque_id_length = 32
184
+ end
185
+ ```
186
+
187
+ ### 3. Batch Generation
188
+
189
+ ```ruby
190
+ # Generate IDs in batches for better performance
191
+ def generate_batch_ids(count, size: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
192
+ # Pre-allocate array
193
+ results = Array.new(count)
194
+
195
+ # Generate all IDs
196
+ count.times do |i|
197
+ results[i] = OpaqueId.generate(size: size, alphabet: alphabet)
198
+ end
199
+
200
+ results
201
+ end
202
+
203
+ # Usage
204
+ ids = generate_batch_ids(10000)
205
+ # Much faster than individual calls
206
+ ```
207
+
208
+ ### 4. Memory Management
209
+
210
+ ```ruby
211
+ # For large-scale generation, manage memory carefully
212
+ def generate_large_batch(count, size: 21, alphabet: OpaqueId::ALPHANUMERIC_ALPHABET)
213
+ results = []
214
+ batch_size = 10000
215
+
216
+ (count / batch_size).times do
217
+ batch = batch_size.times.map { OpaqueId.generate(size: size, alphabet: alphabet) }
218
+ results.concat(batch)
219
+
220
+ # Force garbage collection for large batches
221
+ GC.start if count > 100000
222
+ end
223
+
224
+ results
225
+ end
226
+ ```
227
+
228
+ ### 5. Database Optimization
229
+
230
+ ```ruby
231
+ # Ensure proper database indexing
232
+ class AddOpaqueIdToUsers < ActiveRecord::Migration[8.0]
233
+ def change
234
+ add_column :users, :opaque_id, :string
235
+ add_index :users, :opaque_id, unique: true
236
+ end
237
+ end
238
+
239
+ # Use database-specific optimizations
240
+ # PostgreSQL: Partial indexes for specific use cases
241
+ # MySQL: Use appropriate storage engine (InnoDB)
242
+ # SQLite: Consider WAL mode for better concurrency
243
+ ```
244
+
245
+ ## Performance Testing
246
+
247
+ ### Benchmark Scripts
248
+
249
+ ```ruby
250
+ # Basic performance benchmark
251
+ require 'benchmark'
252
+
253
+ def benchmark_generation(count = 1000000)
254
+ puts "Generating #{count} opaque IDs..."
255
+
256
+ time = Benchmark.measure do
257
+ count.times { OpaqueId.generate }
258
+ end
259
+
260
+ puts "Time: #{time.real.round(2)} seconds"
261
+ puts "Rate: #{(count / time.real).round(0)} IDs/second"
262
+ puts "Memory: #{`ps -o rss= -p #{Process.pid}`.to_i / 1024}MB"
263
+ end
264
+
265
+ # Run benchmark
266
+ benchmark_generation(1000000)
267
+ ```
268
+
269
+ ### Memory Profiling
270
+
271
+ ```ruby
272
+ # Memory usage analysis
273
+ require 'memory_profiler'
274
+
275
+ def profile_memory(count = 100000)
276
+ report = MemoryProfiler.report do
277
+ count.times { OpaqueId.generate }
278
+ end
279
+
280
+ puts "Total allocated: #{report.total_allocated_memsize / 1024 / 1024}MB"
281
+ puts "Total retained: #{report.total_retained_memsize / 1024 / 1024}MB"
282
+ puts "Objects allocated: #{report.total_allocated}"
283
+ puts "Objects retained: #{report.total_retained}"
284
+ end
285
+
286
+ # Run memory profile
287
+ profile_memory(100000)
288
+ ```
289
+
290
+ ### Load Testing
291
+
292
+ ```ruby
293
+ # Concurrent generation testing
294
+ require 'concurrent'
295
+
296
+ def load_test(threads = 10, ids_per_thread = 100000)
297
+ puts "Testing #{threads} threads, #{ids_per_thread} IDs each..."
298
+
299
+ start_time = Time.now
300
+
301
+ # Create thread pool
302
+ pool = Concurrent::FixedThreadPool.new(threads)
303
+
304
+ # Submit tasks
305
+ futures = threads.times.map do
306
+ pool.post do
307
+ ids_per_thread.times.map { OpaqueId.generate }
308
+ end
309
+ end
310
+
311
+ # Wait for completion
312
+ results = futures.map(&:value!)
313
+
314
+ end_time = Time.now
315
+ total_ids = threads * ids_per_thread
316
+ duration = end_time - start_time
317
+
318
+ puts "Total IDs: #{total_ids}"
319
+ puts "Duration: #{duration.round(2)} seconds"
320
+ puts "Rate: #{(total_ids / duration).round(0)} IDs/second"
321
+ puts "Per thread: #{(ids_per_thread / duration).round(0)} IDs/second"
322
+
323
+ pool.shutdown
324
+ pool.wait_for_termination
325
+ end
326
+
327
+ # Run load test
328
+ load_test(10, 100000)
329
+ ```
330
+
331
+ ## Performance Comparison
332
+
333
+ ### vs. Other ID Generation Methods
334
+
335
+ OpaqueId compares favorably to other ID generation methods:
336
+
337
+ - **SecureRandom.hex**: OpaqueId provides more customization and collision handling
338
+ - **SecureRandom.uuid**: OpaqueId offers configurable length and alphabet
339
+ - **NanoID (gem)**: OpaqueId uses native Ruby methods without external dependencies
340
+ - **Database auto-increment**: OpaqueId provides security and URL-safety at minimal performance cost
341
+
342
+ ### vs. Database-Generated IDs
343
+
344
+ ```ruby
345
+ # Database auto-increment vs OpaqueId
346
+
347
+ # Database auto-increment
348
+ # Pros: Fast, sequential, small storage
349
+ # Cons: Predictable, not URL-safe, reveals count
350
+
351
+ # OpaqueId
352
+ # Pros: Unpredictable, URL-safe, configurable
353
+ # Cons: Slightly slower, larger storage
354
+ ```
355
+
356
+ ## Production Considerations
357
+
358
+ ### 1. Monitoring
359
+
360
+ ```ruby
361
+ # Add performance monitoring
362
+ class User < ApplicationRecord
363
+ include OpaqueId::Model
364
+
365
+ private
366
+
367
+ def set_opaque_id
368
+ start_time = Time.now
369
+ self.opaque_id = generate_opaque_id
370
+ duration = Time.now - start_time
371
+
372
+ # Log slow generation
373
+ Rails.logger.warn "Slow opaque_id generation: #{duration}ms" if duration > 0.001
374
+ end
375
+ end
376
+ ```
377
+
378
+ ### 2. Caching
379
+
380
+ ```ruby
381
+ # Cache generated IDs for frequently accessed records
382
+ class User < ApplicationRecord
383
+ include OpaqueId::Model
384
+
385
+ def opaque_id
386
+ @opaque_id ||= super
387
+ end
388
+ end
389
+ ```
390
+
391
+ ### 3. Background Generation
392
+
393
+ ```ruby
394
+ # Generate IDs in background for bulk operations
395
+ class BulkUserCreator
396
+ def self.create_users(count)
397
+ # Generate IDs in background
398
+ ids = Concurrent::Future.execute do
399
+ count.times.map { OpaqueId.generate }
400
+ end
401
+
402
+ # Create users with pre-generated IDs
403
+ users_data = ids.value.map.with_index do |id, index|
404
+ { opaque_id: id, name: "User #{index + 1}" }
405
+ end
406
+
407
+ User.insert_all(users_data)
408
+ end
409
+ end
410
+ ```
411
+
412
+ ### 4. Error Handling
413
+
414
+ ```ruby
415
+ # Handle generation failures gracefully
416
+ class User < ApplicationRecord
417
+ include OpaqueId::Model
418
+
419
+ private
420
+
421
+ def set_opaque_id
422
+ retries = 0
423
+ max_retries = opaque_id_max_retry
424
+
425
+ begin
426
+ self.opaque_id = generate_opaque_id
427
+ rescue OpaqueId::GenerationError => e
428
+ retries += 1
429
+ if retries < max_retries
430
+ retry
431
+ else
432
+ Rails.logger.error "Failed to generate opaque_id after #{max_retries} retries: #{e.message}"
433
+ raise
434
+ end
435
+ end
436
+ end
437
+ end
438
+ ```
439
+
440
+ ## Best Practices
441
+
442
+ ### 1. Choose Appropriate Configuration
443
+
444
+ ```ruby
445
+ # High-performance applications
446
+ self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
447
+ self.opaque_id_length = 21
448
+
449
+ # High-security applications
450
+ self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
451
+ self.opaque_id_length = 32
452
+
453
+ # Human-readable applications
454
+ self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
455
+ self.opaque_id_length = 15
456
+ ```
457
+
458
+ ### 2. Optimize for Your Use Case
459
+
460
+ ```ruby
461
+ # Short URLs - prioritize performance
462
+ self.opaque_id_length = 8
463
+ self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
464
+
465
+ # API keys - prioritize security
466
+ self.opaque_id_length = 32
467
+ self.opaque_id_alphabet = OpaqueId::ALPHANUMERIC_ALPHABET
468
+
469
+ # User IDs - balance performance and security
470
+ self.opaque_id_length = 21
471
+ self.opaque_id_alphabet = OpaqueId::STANDARD_ALPHABET
472
+ ```
473
+
474
+ ### 3. Monitor Performance
475
+
476
+ ```ruby
477
+ # Add performance monitoring
478
+ # Track generation time
479
+ # Monitor memory usage
480
+ # Alert on slow generation
481
+ # Log performance metrics
482
+ ```
483
+
484
+ ## Next Steps
485
+
486
+ Now that you understand performance characteristics:
487
+
488
+ 1. **Explore [Algorithms](algorithms.md)** for technical algorithm details
489
+ 2. **Check out [Security](security.md)** for security considerations
490
+ 3. **Review [Configuration](configuration.md)** for performance optimization
491
+ 4. **Read [API Reference](api-reference.md)** for complete performance documentation
data/docs/robots.txt ADDED
@@ -0,0 +1,11 @@
1
+ User-agent: *
2
+ Allow: /
3
+
4
+ # Sitemap
5
+ Sitemap: https://nyaggah.github.io/opaque_id/sitemap.xml
6
+
7
+ # Disallow certain paths
8
+ Disallow: /assets/
9
+ Disallow: /_site/
10
+ Disallow: /vendor/
11
+ Disallow: /node_modules/