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