familia 2.0.0.pre14 → 2.0.0.pre16
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 +4 -4
- data/.github/workflows/code-quality.yml +138 -0
- data/.github/workflows/code-smellage.yml +145 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +1 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +48 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +66 -6
- data/CLAUDE.md +1 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +99 -23
- data/LICENSE.txt +1 -1
- data/README.md +285 -85
- data/changelog.d/README.md +2 -2
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
- data/docs/archive/FAMILIA_TECHNICAL.md +41 -41
- data/docs/archive/FAMILIA_UPDATE.md +3 -3
- data/docs/archive/README.md +3 -2
- data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
- data/docs/conf.py +29 -0
- data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
- data/docs/guides/feature-encrypted-fields.md +785 -0
- data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
- data/docs/guides/feature-external-identifiers.md +637 -0
- data/docs/guides/feature-object-identifiers.md +435 -0
- data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
- data/docs/guides/feature-relationships-methods.md +684 -0
- data/docs/guides/feature-relationships.md +200 -0
- data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
- data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
- data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
- data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
- data/docs/guides/index.md +176 -0
- data/docs/guides/{Security-Model.md → security-model.md} +1 -1
- data/docs/migrating/v2.0.0-pre.md +1 -1
- data/docs/migrating/v2.0.0-pre11.md +4 -4
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre13.md +1 -1
- data/docs/migrating/v2.0.0-pre5.md +33 -12
- data/docs/migrating/v2.0.0-pre6.md +2 -2
- data/docs/migrating/v2.0.0-pre7.md +8 -8
- data/docs/overview.md +623 -19
- data/docs/reference/api-technical.md +1365 -0
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
- data/examples/autoloader/mega_customer.rb +3 -1
- data/examples/encrypted_fields.rb +378 -0
- data/examples/json_usage_patterns.rb +144 -0
- data/examples/relationships.rb +13 -13
- data/examples/safe_dump.rb +6 -6
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +49 -10
- data/lib/familia/connection/handlers.rb +223 -0
- data/lib/familia/connection/individual_command_proxy.rb +64 -0
- data/lib/familia/connection/middleware.rb +75 -0
- data/lib/familia/connection/operation_core.rb +93 -0
- data/lib/familia/connection/operations.rb +277 -0
- data/lib/familia/connection/pipeline_core.rb +87 -0
- data/lib/familia/connection/transaction_core.rb +100 -0
- data/lib/familia/connection.rb +60 -186
- data/lib/familia/data_type/commands.rb +53 -51
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +13 -10
- data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
- data/lib/familia/data_type/types/lock.rb +3 -2
- data/lib/familia/data_type/types/sorted_set.rb +26 -15
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -5
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +75 -47
- data/lib/familia/distinguisher.rb +85 -0
- data/lib/familia/encryption/encrypted_data.rb +15 -24
- data/lib/familia/encryption/manager.rb +6 -4
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
- data/lib/familia/encryption/request_cache.rb +7 -7
- data/lib/familia/encryption.rb +2 -3
- data/lib/familia/errors.rb +9 -3
- data/lib/familia/{autoloader.rb → features/autoloader.rb} +49 -23
- data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
- data/lib/familia/features/encrypted_fields.rb +68 -66
- data/lib/familia/features/expiration/extensions.rb +61 -0
- data/lib/familia/features/expiration.rb +35 -87
- data/lib/familia/features/external_identifier.rb +11 -12
- data/lib/familia/features/object_identifier.rb +58 -20
- data/lib/familia/features/quantization.rb +17 -22
- data/lib/familia/features/relationships/README.md +97 -0
- data/lib/familia/features/relationships/collection_operations.rb +104 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +301 -0
- data/lib/familia/features/relationships/indexing.rb +176 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +160 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
- data/lib/familia/features/relationships/participation.rb +656 -0
- data/lib/familia/features/relationships/participation_relationship.rb +31 -0
- data/lib/familia/features/relationships/score_encoding.rb +20 -20
- data/lib/familia/features/relationships.rb +69 -271
- data/lib/familia/features/safe_dump.rb +127 -132
- data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
- data/lib/familia/features/transient_fields.rb +5 -5
- data/lib/familia/features.rb +21 -21
- data/lib/familia/field_type.rb +24 -4
- data/lib/familia/horreum/core/connection.rb +229 -26
- data/lib/familia/horreum/core/database_commands.rb +27 -17
- data/lib/familia/horreum/core/serialization.rb +40 -20
- data/lib/familia/horreum/core/utils.rb +2 -1
- data/lib/familia/horreum/shared/settings.rb +2 -1
- data/lib/familia/horreum/subclass/definition.rb +33 -45
- data/lib/familia/horreum/subclass/management.rb +72 -24
- data/lib/familia/horreum/subclass/related_fields_management.rb +82 -21
- data/lib/familia/horreum.rb +196 -114
- data/lib/familia/json_serializer.rb +0 -1
- data/lib/familia/logging.rb +11 -114
- data/lib/familia/refinements/dear_json.rb +122 -0
- data/lib/familia/refinements/logger_trace.rb +20 -17
- data/lib/familia/refinements/stylize_words.rb +65 -0
- data/lib/familia/refinements/time_literals.rb +60 -52
- data/lib/familia/refinements.rb +2 -1
- data/lib/familia/secure_identifier.rb +60 -28
- data/lib/familia/settings.rb +83 -7
- data/lib/familia/utils.rb +5 -87
- data/lib/familia/verifiable_identifier.rb +4 -4
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +72 -15
- data/lib/middleware/database_middleware.rb +56 -14
- data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
- data/try/configuration/scenarios_try.rb +1 -1
- data/try/connection/fiber_context_preservation_try.rb +250 -0
- data/try/connection/handler_constraints_try.rb +59 -0
- data/try/connection/operation_mode_guards_try.rb +208 -0
- data/try/connection/pipeline_fallback_integration_try.rb +128 -0
- data/try/connection/responsibility_chain_tracking_try.rb +72 -0
- data/try/connection/transaction_fallback_integration_try.rb +288 -0
- data/try/connection/transaction_mode_permissive_try.rb +153 -0
- data/try/connection/transaction_mode_strict_try.rb +98 -0
- data/try/connection/transaction_mode_warn_try.rb +131 -0
- data/try/connection/transaction_modes_try.rb +249 -0
- data/try/core/autoloader_try.rb +129 -11
- data/try/core/connection_try.rb +7 -7
- data/try/core/conventional_inheritance_try.rb +130 -0
- data/try/core/create_method_try.rb +15 -23
- data/try/core/database_consistency_try.rb +10 -10
- data/try/core/errors_try.rb +8 -11
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- data/try/core/isolated_dbclient_try.rb +165 -0
- data/try/core/middleware_try.rb +16 -16
- data/try/core/persistence_operations_try.rb +4 -4
- data/try/core/pools_try.rb +42 -26
- data/try/core/secure_identifier_try.rb +28 -24
- data/try/core/time_utils_try.rb +10 -10
- data/try/core/tools_try.rb +1 -1
- data/try/core/utils_try.rb +2 -2
- data/try/data_types/boolean_try.rb +4 -4
- data/try/data_types/datatype_base_try.rb +0 -2
- data/try/data_types/list_try.rb +10 -10
- data/try/data_types/sorted_set_try.rb +5 -5
- data/try/data_types/string_try.rb +12 -12
- data/try/data_types/unsortedset_try.rb +33 -0
- data/try/debugging/cache_behavior_tracer.rb +7 -7
- data/try/debugging/debug_aad_process.rb +1 -1
- data/try/debugging/debug_concealed_internal.rb +1 -1
- data/try/debugging/debug_cross_context.rb +1 -1
- data/try/debugging/debug_fresh_cross_context.rb +1 -1
- data/try/debugging/encryption_method_tracer.rb +10 -10
- data/try/edge_cases/hash_symbolization_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/encryption/config_persistence_try.rb +2 -2
- data/try/encryption/encryption_core_try.rb +19 -19
- data/try/encryption/instance_variable_scope_try.rb +1 -1
- data/try/encryption/module_loading_try.rb +2 -2
- data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
- data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/encryption/secure_memory_handling_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
- data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +3 -3
- data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
- data/try/features/object_identifier/object_identifier_try.rb +10 -0
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
- data/try/features/relationships/indexing_try.rb +433 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
- data/try/features/relationships/participation_commands_verification_try.rb +105 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
- data/try/features/relationships/participation_reverse_index_try.rb +196 -0
- data/try/features/relationships/relationships_api_changes_try.rb +72 -71
- data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
- data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
- data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
- data/try/features/relationships/relationships_performance_try.rb +20 -20
- data/try/features/relationships/relationships_try.rb +27 -38
- data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
- data/try/features/transient_fields/refresh_reset_try.rb +1 -1
- data/try/features/transient_fields/simple_refresh_test.rb +1 -1
- data/try/helpers/test_cleanup.rb +86 -0
- data/try/helpers/test_helpers.rb +3 -3
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +1 -1
- data/try/horreum/destroy_related_fields_cleanup_try.rb +330 -0
- data/try/horreum/initialization_try.rb +11 -7
- data/try/horreum/relations_try.rb +21 -13
- data/try/horreum/serialization_try.rb +12 -11
- data/try/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +1 -1
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +8 -10
- data/try/models/datatype_base_try.rb +3 -3
- data/try/models/familia_object_try.rb +9 -8
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
- data/try/prototypes/atomic_saves_v4.rb +1 -1
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
- data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
- data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
- data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
- data/try/prototypes/pooling/pool_siege.rb +11 -11
- data/try/prototypes/pooling/run_stress_tests.rb +7 -7
- data/try/refinements/dear_json_array_methods_try.rb +53 -0
- data/try/refinements/dear_json_hash_methods_try.rb +54 -0
- data/try/refinements/logger_trace_methods_try.rb +44 -0
- data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
- data/try/refinements/time_literals_string_methods_try.rb +80 -0
- metadata +77 -45
- data/.rubocop_todo.yml +0 -208
- data/docs/connection_pooling.md +0 -192
- data/docs/guides/Connection-Pooling-Guide.md +0 -437
- data/docs/guides/Encrypted-Fields-Overview.md +0 -101
- data/docs/guides/Feature-System-Autoloading.md +0 -228
- data/docs/guides/Home.md +0 -116
- data/docs/guides/Relationships-Guide.md +0 -737
- data/docs/guides/relationships-methods.md +0 -266
- data/docs/reference/auditing_database_commands.rb +0 -228
- data/examples/permissions.rb +0 -240
- data/lib/familia/features/autoloadable.rb +0 -113
- data/lib/familia/features/relationships/cascading.rb +0 -437
- data/lib/familia/features/relationships/membership.rb +0 -497
- data/lib/familia/features/relationships/permission_management.rb +0 -264
- data/lib/familia/features/relationships/querying.rb +0 -615
- data/lib/familia/features/relationships/redis_operations.rb +0 -274
- data/lib/familia/features/relationships/tracking.rb +0 -418
- data/lib/familia/refinements/snake_case.rb +0 -40
- data/lib/familia/validation/command_recorder.rb +0 -336
- data/lib/familia/validation/expectations.rb +0 -519
- data/lib/familia/validation/validation_helpers.rb +0 -443
- data/lib/familia/validation/validator.rb +0 -412
- data/lib/familia/validation.rb +0 -140
- data/try/data_types/set_try.rb +0 -33
- data/try/features/autoloadable/autoloadable_try.rb +0 -61
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -111
- data/try/validation/atomic_operations_try.rb.disabled +0 -320
- data/try/validation/command_validation_try.rb.disabled +0 -207
- data/try/validation/performance_validation_try.rb.disabled +0 -324
- data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -0,0 +1,435 @@
|
|
1
|
+
# Object Identifiers Guide
|
2
|
+
|
3
|
+
> **💡 Quick Reference**
|
4
|
+
>
|
5
|
+
> Enable automatic object ID generation with configurable strategies:
|
6
|
+
> ```ruby
|
7
|
+
> class Document < Familia::Horreum
|
8
|
+
> feature :object_identifier, generator: :uuid_v4
|
9
|
+
> field :title, :content
|
10
|
+
> end
|
11
|
+
> ```
|
12
|
+
|
13
|
+
## Overview
|
14
|
+
|
15
|
+
The Object Identifier feature provides automatic generation of unique identifiers for Familia objects. Instead of manually creating identifiers, you can configure different generation strategies that suit your application's needs - from globally unique UUIDs to compact hexadecimal strings.
|
16
|
+
|
17
|
+
## Why Use Object Identifiers?
|
18
|
+
|
19
|
+
**Consistency**: Ensures all objects have properly formatted, unique identifiers without manual management.
|
20
|
+
|
21
|
+
**Flexibility**: Different applications need different ID formats - UUIDs for distributed systems, short hex strings for internal tools, or custom formats for specific business requirements.
|
22
|
+
|
23
|
+
**Collision Avoidance**: Built-in collision detection and retry logic ensures identifier uniqueness even under high concurrency.
|
24
|
+
|
25
|
+
**Integration Ready**: Generated IDs work seamlessly with external APIs, logging systems, and database relationships.
|
26
|
+
|
27
|
+
## Quick Start
|
28
|
+
|
29
|
+
### Basic UUID Generation
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class User < Familia::Horreum
|
33
|
+
feature :object_identifier, generator: :uuid_v4
|
34
|
+
|
35
|
+
field :name, :email, :created_at
|
36
|
+
end
|
37
|
+
|
38
|
+
# Automatic ID generation on creation
|
39
|
+
user = User.create(name: "Alice", email: "alice@example.com")
|
40
|
+
puts user.objid # => "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
41
|
+
```
|
42
|
+
|
43
|
+
### Compact Hex Identifiers
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class Session < Familia::Horreum
|
47
|
+
feature :object_identifier, generator: :hex, length: 16
|
48
|
+
|
49
|
+
field :user_id, :data, :expires_at
|
50
|
+
end
|
51
|
+
|
52
|
+
session = Session.create(user_id: "user123")
|
53
|
+
puts session.objid # => "a1b2c3d4e5f67890"
|
54
|
+
```
|
55
|
+
|
56
|
+
## Generator Types
|
57
|
+
|
58
|
+
### UUID v4 Generator
|
59
|
+
|
60
|
+
Standard UUID format providing global uniqueness across distributed systems.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
class Document < Familia::Horreum
|
64
|
+
feature :object_identifier, generator: :uuid_v4
|
65
|
+
field :title, :content
|
66
|
+
end
|
67
|
+
|
68
|
+
doc = Document.create(title: "My Document")
|
69
|
+
doc.objid # => "550e8400-e29b-41d4-a716-446655440000"
|
70
|
+
```
|
71
|
+
|
72
|
+
**Characteristics:**
|
73
|
+
- **Format**: 36 characters (8-4-4-4-12 hex pattern)
|
74
|
+
- **Uniqueness**: Globally unique across time and space
|
75
|
+
- **Performance**: Good for distributed systems
|
76
|
+
- **Use Cases**: Public APIs, microservices, external integrations
|
77
|
+
|
78
|
+
> **💡 Best Practice**
|
79
|
+
>
|
80
|
+
> Use UUID v4 for objects that will be exposed externally or across service boundaries.
|
81
|
+
|
82
|
+
### Hex Generator
|
83
|
+
|
84
|
+
Compact hexadecimal strings ideal for internal use and high-volume scenarios.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class ApiKey < Familia::Horreum
|
88
|
+
feature :object_identifier, generator: :hex, length: 24
|
89
|
+
field :name, :permissions, :created_at
|
90
|
+
end
|
91
|
+
|
92
|
+
key = ApiKey.create(name: "Production API")
|
93
|
+
key.objid # => "1a2b3c4d5e6f7890abcdef12"
|
94
|
+
```
|
95
|
+
|
96
|
+
**Configuration Options:**
|
97
|
+
- `length`: Number of hex characters (default: 12)
|
98
|
+
- `prefix`: Optional prefix for the identifier
|
99
|
+
- `charset`: Custom character set (default: hex digits)
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class InternalToken < Familia::Horreum
|
103
|
+
feature :object_identifier,
|
104
|
+
generator: :hex,
|
105
|
+
length: 16,
|
106
|
+
prefix: "tk_"
|
107
|
+
|
108
|
+
field :scope, :issued_at
|
109
|
+
end
|
110
|
+
|
111
|
+
token = InternalToken.create(scope: "read:users")
|
112
|
+
token.objid # => "tk_a1b2c3d4e5f67890"
|
113
|
+
```
|
114
|
+
|
115
|
+
**Characteristics:**
|
116
|
+
- **Format**: Configurable length hexadecimal string
|
117
|
+
- **Performance**: Very fast generation
|
118
|
+
- **Storage**: Compact representation
|
119
|
+
- **Use Cases**: Internal IDs, tokens, session identifiers
|
120
|
+
|
121
|
+
> **⚠️ Important**
|
122
|
+
>
|
123
|
+
> Hex generators provide good uniqueness but aren't globally unique like UUIDs. Use appropriate length for your collision tolerance.
|
124
|
+
|
125
|
+
### Custom Generator
|
126
|
+
|
127
|
+
Define your own identifier generation logic for business-specific requirements.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class OrderNumber < Familia::Horreum
|
131
|
+
feature :object_identifier, generator: :custom
|
132
|
+
|
133
|
+
field :customer_id, :amount, :created_at
|
134
|
+
|
135
|
+
# Custom generator implementation
|
136
|
+
def self.generate_identifier
|
137
|
+
timestamp = Time.now.strftime('%Y%m%d')
|
138
|
+
sequence = Redis.current.incr("order_sequence:#{timestamp}")
|
139
|
+
"ORD-#{timestamp}-#{sequence.to_s.rjust(6, '0')}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
order = OrderNumber.create(customer_id: "cust123", amount: 99.99)
|
144
|
+
order.objid # => "ORD-20241215-000001"
|
145
|
+
```
|
146
|
+
|
147
|
+
**Implementation Requirements:**
|
148
|
+
- Must define `self.generate_identifier` class method
|
149
|
+
- Should return a string identifier
|
150
|
+
- Must handle uniqueness and collision scenarios
|
151
|
+
- Consider thread safety for concurrent access
|
152
|
+
|
153
|
+
> **🔧 Advanced Pattern**
|
154
|
+
>
|
155
|
+
> Custom generators can integrate with external services, database sequences, or business rules for sophisticated ID schemes.
|
156
|
+
|
157
|
+
## Advanced Configuration
|
158
|
+
|
159
|
+
### Collision Detection
|
160
|
+
|
161
|
+
Enable automatic collision detection and retry logic:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
class Product < Familia::Horreum
|
165
|
+
feature :object_identifier,
|
166
|
+
generator: :hex,
|
167
|
+
collision_check: true,
|
168
|
+
max_retries: 5
|
169
|
+
|
170
|
+
field :name, :price, :sku
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
**Configuration Options:**
|
175
|
+
- `collision_check`: Enable/disable collision detection (default: true)
|
176
|
+
- `max_retries`: Maximum retry attempts on collision (default: 3)
|
177
|
+
- `retry_delay`: Delay between retries in seconds (default: 0.001)
|
178
|
+
|
179
|
+
### Identifier Validation
|
180
|
+
|
181
|
+
Add custom validation logic for generated identifiers:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class SecureToken < Familia::Horreum
|
185
|
+
feature :object_identifier, generator: :custom
|
186
|
+
|
187
|
+
def self.generate_identifier
|
188
|
+
loop do
|
189
|
+
candidate = SecureRandom.alphanumeric(32)
|
190
|
+
# Ensure no ambiguous characters
|
191
|
+
next if candidate.match?(/[0O1lI]/)
|
192
|
+
return "st_#{candidate.downcase}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def self.valid_identifier?(id)
|
197
|
+
id.match?(/^st_[a-z0-9]{32}$/) && !id.match?(/[0O1lI]/)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
## Performance Considerations
|
203
|
+
|
204
|
+
### Generation Speed Benchmarks
|
205
|
+
|
206
|
+
Different generators have varying performance characteristics:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
# Benchmark different generators
|
210
|
+
require 'benchmark'
|
211
|
+
|
212
|
+
Benchmark.bm(10) do |x|
|
213
|
+
x.report("UUID v4:") { 10_000.times { SecureRandom.uuid } }
|
214
|
+
x.report("Hex 12:") { 10_000.times { SecureRandom.hex(6) } }
|
215
|
+
x.report("Hex 24:") { 10_000.times { SecureRandom.hex(12) } }
|
216
|
+
x.report("Custom:") { 10_000.times { MyClass.generate_identifier } }
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
### Memory Usage
|
221
|
+
|
222
|
+
- **UUID v4**: 36 bytes per identifier
|
223
|
+
- **Hex**: Variable based on length (2 bytes per hex character)
|
224
|
+
- **Custom**: Depends on implementation
|
225
|
+
|
226
|
+
### Collision Probability
|
227
|
+
|
228
|
+
For hex generators, collision probability depends on length and volume:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# Approximate collision probability for hex identifiers
|
232
|
+
def collision_probability(length, count)
|
233
|
+
total_space = 16 ** length
|
234
|
+
1 - Math.exp(-(count * (count - 1)) / (2.0 * total_space))
|
235
|
+
end
|
236
|
+
|
237
|
+
# Examples:
|
238
|
+
collision_probability(12, 1_000_000) # Very low
|
239
|
+
collision_probability(8, 100_000) # Consider longer length
|
240
|
+
```
|
241
|
+
|
242
|
+
> **📊 Sizing Guidance**
|
243
|
+
>
|
244
|
+
> - **8 hex chars**: Good for < 10K objects
|
245
|
+
> - **12 hex chars**: Good for < 1M objects
|
246
|
+
> - **16 hex chars**: Good for < 100M objects
|
247
|
+
> - **UUID v4**: Suitable for any scale
|
248
|
+
|
249
|
+
## Integration Patterns
|
250
|
+
|
251
|
+
### External API Integration
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
class ExternalReference < Familia::Horreum
|
255
|
+
feature :object_identifier, generator: :uuid_v4
|
256
|
+
field :external_id, :sync_status, :last_sync
|
257
|
+
|
258
|
+
def sync_to_external_api
|
259
|
+
response = ExternalAPI.create_record(
|
260
|
+
id: self.objid, # Use generated ID
|
261
|
+
data: self.to_h
|
262
|
+
)
|
263
|
+
|
264
|
+
self.external_id = response['id']
|
265
|
+
self.sync_status = 'synced'
|
266
|
+
self.last_sync = Familia.now.to_i
|
267
|
+
save
|
268
|
+
end
|
269
|
+
end
|
270
|
+
```
|
271
|
+
|
272
|
+
### Database Relationships
|
273
|
+
|
274
|
+
```ruby
|
275
|
+
class Order < Familia::Horreum
|
276
|
+
feature :object_identifier, generator: :custom
|
277
|
+
field :customer_id, :total_amount
|
278
|
+
|
279
|
+
def self.generate_identifier
|
280
|
+
"ORD-#{SecureRandom.hex(8).upcase}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class OrderItem < Familia::Horreum
|
285
|
+
feature :object_identifier, generator: :hex
|
286
|
+
field :order_id, :product_id, :quantity
|
287
|
+
|
288
|
+
def order
|
289
|
+
Order.load(order_id)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Usage
|
294
|
+
order = Order.create(customer_id: "cust123", total_amount: 299.99)
|
295
|
+
item = OrderItem.create(
|
296
|
+
order_id: order.objid, # Reference by generated ID
|
297
|
+
product_id: "prod456",
|
298
|
+
quantity: 2
|
299
|
+
)
|
300
|
+
```
|
301
|
+
|
302
|
+
### Logging and Debugging
|
303
|
+
|
304
|
+
Generated identifiers provide excellent debugging context:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
class UserSession < Familia::Horreum
|
308
|
+
feature :object_identifier, generator: :hex, length: 16
|
309
|
+
field :user_id, :ip_address, :user_agent
|
310
|
+
|
311
|
+
def log_activity(action)
|
312
|
+
logger.info(
|
313
|
+
"Session #{objid}: User #{user_id} performed #{action}",
|
314
|
+
session_id: objid,
|
315
|
+
user_id: user_id,
|
316
|
+
action: action,
|
317
|
+
timestamp: Familia.now.to_i
|
318
|
+
)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
323
|
+
## Testing Strategies
|
324
|
+
|
325
|
+
### Test Identifier Generation
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
# test/models/user_test.rb
|
329
|
+
require 'test_helper'
|
330
|
+
|
331
|
+
class UserTest < Minitest::Test
|
332
|
+
def test_uuid_generation
|
333
|
+
user = User.create(name: "Test User")
|
334
|
+
|
335
|
+
# Verify UUID format
|
336
|
+
assert_match(
|
337
|
+
/\A[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i,
|
338
|
+
user.objid
|
339
|
+
)
|
340
|
+
end
|
341
|
+
|
342
|
+
def test_hex_generation
|
343
|
+
session = Session.create(user_id: "123")
|
344
|
+
|
345
|
+
# Verify hex format and length
|
346
|
+
assert_match(/\A[0-9a-f]{16}\z/i, session.objid)
|
347
|
+
assert_equal 16, session.objid.length
|
348
|
+
end
|
349
|
+
|
350
|
+
def test_custom_identifier_format
|
351
|
+
order = OrderNumber.create(customer_id: "cust123")
|
352
|
+
|
353
|
+
# Verify custom format
|
354
|
+
assert_match(/\AORD-\d{8}-\d{6}\z/, order.objid)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
### Mock Generators for Testing
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
# test/test_helper.rb
|
363
|
+
class TestIdentifierGenerator
|
364
|
+
def self.generate_test_uuid
|
365
|
+
"test-#{Time.now.to_f}-#{rand(1000)}"
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# In tests
|
370
|
+
class User < Familia::Horreum
|
371
|
+
feature :object_identifier, generator: :custom
|
372
|
+
|
373
|
+
def self.generate_identifier
|
374
|
+
if Rails.env.test?
|
375
|
+
TestIdentifierGenerator.generate_test_uuid
|
376
|
+
else
|
377
|
+
SecureRandom.uuid
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
```
|
382
|
+
|
383
|
+
## Troubleshooting
|
384
|
+
|
385
|
+
### Common Issues
|
386
|
+
|
387
|
+
**Identifier Not Generated**
|
388
|
+
```ruby
|
389
|
+
# Ensure feature is enabled
|
390
|
+
class MyModel < Familia::Horreum
|
391
|
+
feature :object_identifier # This line is required!
|
392
|
+
field :name
|
393
|
+
end
|
394
|
+
```
|
395
|
+
|
396
|
+
**Custom Generator Not Called**
|
397
|
+
```ruby
|
398
|
+
# Verify method signature
|
399
|
+
def self.generate_identifier # Must be class method
|
400
|
+
# Implementation here
|
401
|
+
end
|
402
|
+
```
|
403
|
+
|
404
|
+
**Collision Detection Failing**
|
405
|
+
```ruby
|
406
|
+
# Check Valkey/Redis connectivity and permissions
|
407
|
+
begin
|
408
|
+
MyModel.create(name: "test")
|
409
|
+
rescue Familia::Problem => e
|
410
|
+
puts "Identifier collision: #{e.message}"
|
411
|
+
end
|
412
|
+
```
|
413
|
+
|
414
|
+
### Debug Identifier Generation
|
415
|
+
|
416
|
+
```ruby
|
417
|
+
# Enable debug logging
|
418
|
+
Familia.debug = true
|
419
|
+
|
420
|
+
# Check feature configuration
|
421
|
+
MyModel.feature_options(:object_identifier)
|
422
|
+
#=> {generator: :uuid_v4, collision_check: true, max_retries: 3}
|
423
|
+
|
424
|
+
# Verify generation manually
|
425
|
+
MyModel.generate_identifier # Should return new identifier
|
426
|
+
```
|
427
|
+
|
428
|
+
---
|
429
|
+
|
430
|
+
## See Also
|
431
|
+
|
432
|
+
- **[Technical Reference](../reference/api-technical.md#object-identifier-feature-v200-pre7)** - Implementation details and advanced patterns
|
433
|
+
- **[External Identifiers Guide](feature-external-identifiers.md)** - Integration with external systems
|
434
|
+
- **[Feature System Guide](feature-system.md)** - Understanding the feature architecture
|
435
|
+
- **[Implementation Guide](implementation.md)** - Advanced configuration patterns
|
@@ -1,19 +1,64 @@
|
|
1
1
|
# Quantization Feature Guide
|
2
2
|
|
3
|
-
|
3
|
+
The Quantization feature revolutionizes how you handle time-based data by providing automatic bucketing, consistent timestamps, and analytics-ready data organization. Instead of dealing with precise timestamps that make aggregation difficult, quantization rounds time values to predictable intervals, enabling efficient caching, analytics dashboards, and time-series data management.
|
4
4
|
|
5
|
-
|
5
|
+
> [!NOTE]
|
6
|
+
> **Perfect For:** Analytics dashboards, time-series metrics, cache keys, data retention policies, and any scenario where you need consistent time-based data grouping.
|
7
|
+
|
8
|
+
## Understanding Time Quantization
|
9
|
+
|
10
|
+
### The Problem with Precise Timestamps
|
11
|
+
|
12
|
+
Without quantization, time-based data becomes fragmented and difficult to aggregate:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Problem: Each request gets a unique timestamp
|
16
|
+
user_activity_14_30_23 = "login at 2023-06-15 14:30:23"
|
17
|
+
user_activity_14_30_45 = "login at 2023-06-15 14:30:45"
|
18
|
+
user_activity_14_31_12 = "login at 2023-06-15 14:31:12"
|
19
|
+
|
20
|
+
# Result: Three separate data points instead of aggregated hourly data
|
21
|
+
# Makes analytics queries complex and cache keys unpredictable
|
22
|
+
```
|
23
|
+
|
24
|
+
### The Quantization Solution
|
25
|
+
|
26
|
+
Quantization groups timestamps into consistent buckets:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
# Solution: All timestamps within an hour become the same bucket
|
30
|
+
qstamp(1.hour, time: Time.parse("2023-06-15 14:30:23")) # => 1687276800 (14:00:00)
|
31
|
+
qstamp(1.hour, time: Time.parse("2023-06-15 14:30:45")) # => 1687276800 (14:00:00)
|
32
|
+
qstamp(1.hour, time: Time.parse("2023-06-15 14:31:12")) # => 1687276800 (14:00:00)
|
33
|
+
|
34
|
+
# Result: Single aggregated data point for the entire hour
|
35
|
+
# Perfect for analytics and predictable cache keys!
|
36
|
+
```
|
37
|
+
|
38
|
+
> [!NOTE]
|
39
|
+
> **Key Benefits:**
|
40
|
+
> - **Consistent Buckets**: All timestamps in a period map to the same value
|
41
|
+
> - **Predictable Keys**: Cache keys and identifiers become deterministic
|
42
|
+
> - **Efficient Aggregation**: Reduce millions of data points to manageable buckets
|
43
|
+
> - **Analytics Ready**: Perfect for dashboards requiring time-series data
|
6
44
|
|
7
45
|
## Core Concepts
|
8
46
|
|
9
|
-
### Quantum Intervals
|
47
|
+
### Quantum Intervals Explained
|
10
48
|
|
11
|
-
A **quantum**
|
49
|
+
A **quantum** represents the time bucket size for grouping timestamps. Think of it as the "resolution" of your time-based data:
|
12
50
|
|
13
|
-
|
14
|
-
- **
|
15
|
-
- **
|
16
|
-
- **
|
51
|
+
**Common Quantum Patterns:**
|
52
|
+
- **High-Resolution**: `1.minute`, `5.minutes`, `15.minutes` - For real-time monitoring
|
53
|
+
- **Medium-Resolution**: `1.hour`, `6.hours`, `12.hours` - For hourly analytics
|
54
|
+
- **Low-Resolution**: `1.day`, `1.week`, `1.month` - For long-term trends
|
55
|
+
- **Custom Intervals**: Any number of seconds (e.g., `90` for 1.5 minutes, `300` for 5 minutes)
|
56
|
+
|
57
|
+
> [!TIP]
|
58
|
+
> **Choosing the Right Quantum:**
|
59
|
+
> - **Smaller quantums** = More granular data, more storage
|
60
|
+
> - **Larger quantums** = Less granular data, less storage
|
61
|
+
> - Consider your analytics needs and storage constraints
|
17
62
|
|
18
63
|
### Quantized Timestamps (qstamp)
|
19
64
|
|
@@ -33,21 +78,28 @@ qstamp(1.hour, pattern: '%H:%M:%S', time: Time.parse('14:05:12')) # => "14:00:0
|
|
33
78
|
qstamp(1.hour, pattern: '%H:%M:%S', time: Time.parse('14:55:33')) # => "14:00:00"
|
34
79
|
```
|
35
80
|
|
36
|
-
##
|
81
|
+
## Getting Started with Quantization
|
82
|
+
|
83
|
+
### Enabling the Feature
|
37
84
|
|
38
|
-
|
85
|
+
The quantization feature integrates seamlessly with your existing Familia models:
|
39
86
|
|
40
87
|
```ruby
|
41
88
|
class AnalyticsEvent < Familia::Horreum
|
42
89
|
feature :quantization
|
43
|
-
default_expiration 300 # 5 minutes (used as default quantum)
|
90
|
+
default_expiration 300 # 5 minutes (also used as default quantum)
|
44
91
|
|
45
92
|
identifier_field :event_id
|
46
93
|
field :event_id, :event_type, :user_id, :data, :timestamp
|
47
94
|
end
|
48
95
|
```
|
49
96
|
|
50
|
-
|
97
|
+
> [!TIP]
|
98
|
+
> If you set `default_expiration`, it automatically becomes your default quantum for `qstamp` calls without an explicit interval.
|
99
|
+
|
100
|
+
### Your First Quantized Timestamps
|
101
|
+
|
102
|
+
The `qstamp` method is your main tool for creating consistent time buckets:
|
51
103
|
|
52
104
|
```ruby
|
53
105
|
event = AnalyticsEvent.new
|
@@ -65,6 +117,9 @@ AnalyticsEvent.qstamp(1.hour)
|
|
65
117
|
# => 1687276800
|
66
118
|
```
|
67
119
|
|
120
|
+
> [!IMPORTANT]
|
121
|
+
> The `qstamp` method always rounds DOWN to the quantum boundary. A timestamp of 14:30:45 with a 1-hour quantum becomes 14:00:00, not 15:00:00.
|
122
|
+
|
68
123
|
### Formatted Timestamps
|
69
124
|
|
70
125
|
```ruby
|
@@ -145,7 +200,7 @@ class UserActivity < Familia::Horreum
|
|
145
200
|
bucket.user_count ||= 0
|
146
201
|
bucket.event_count ||= 0
|
147
202
|
|
148
|
-
# Use Redis sets/hashes for precise counting
|
203
|
+
# Use Valkey/Redis sets/hashes for precise counting
|
149
204
|
bucket_users = bucket.related_set("users")
|
150
205
|
bucket_users.add(user_id)
|
151
206
|
|
@@ -172,7 +227,7 @@ end
|
|
172
227
|
|
173
228
|
# Usage
|
174
229
|
UserActivity.record_activity('user_123', 'page_view')
|
175
|
-
hourly_buckets = UserActivity.activity_for_hour(
|
230
|
+
hourly_buckets = UserActivity.activity_for_hour(Familia.now)
|
176
231
|
```
|
177
232
|
|
178
233
|
### Time-Series Data Storage
|
@@ -217,14 +272,14 @@ end
|
|
217
272
|
|
218
273
|
# Usage - Record CPU usage every minute
|
219
274
|
TimeSeriesMetric.record_metric('cpu_usage', 75.5, 1.minute)
|
220
|
-
TimeSeriesMetric.record_metric('cpu_usage', 82.1, 1.minute,
|
275
|
+
TimeSeriesMetric.record_metric('cpu_usage', 82.1, 1.minute, Familia.now + 1.minute)
|
221
276
|
|
222
277
|
# Retrieve data for last hour
|
223
278
|
series_data = TimeSeriesMetric.get_series(
|
224
279
|
'cpu_usage',
|
225
280
|
1.minute,
|
226
|
-
|
227
|
-
|
281
|
+
Familia.now - 1.hour,
|
282
|
+
Familia.now
|
228
283
|
)
|
229
284
|
```
|
230
285
|
|
@@ -252,7 +307,7 @@ class LogAggregator < Familia::Horreum
|
|
252
307
|
)
|
253
308
|
|
254
309
|
aggregator.count += 1
|
255
|
-
aggregator.last_seen =
|
310
|
+
aggregator.last_seen = Familia.now.to_i
|
256
311
|
|
257
312
|
# Keep sample messages (up to 10)
|
258
313
|
sample_key = "sample_#{aggregator.count}"
|
@@ -265,8 +320,8 @@ class LogAggregator < Familia::Horreum
|
|
265
320
|
end
|
266
321
|
|
267
322
|
def self.error_summary(time_range = 1.hour)
|
268
|
-
start_time =
|
269
|
-
end_time =
|
323
|
+
start_time = Familia.now - time_range
|
324
|
+
end_time = Familia.now
|
270
325
|
|
271
326
|
# Find all error buckets in time range
|
272
327
|
buckets = []
|
@@ -300,7 +355,7 @@ class ApplicationMetrics
|
|
300
355
|
include Familia::Horreum
|
301
356
|
feature :quantization
|
302
357
|
|
303
|
-
#
|
358
|
+
# UnsortedSet up different quantum intervals for different metrics
|
304
359
|
QUANTUM_CONFIGS = {
|
305
360
|
real_time: 1.minute, # High frequency metrics
|
306
361
|
standard: 5.minutes, # Regular analytics
|
@@ -325,7 +380,7 @@ class MetricsCollector
|
|
325
380
|
# Increment counter
|
326
381
|
Familia.dbclient.incr(key)
|
327
382
|
|
328
|
-
#
|
383
|
+
# UnsortedSet expiration based on quantum type
|
329
384
|
ttl = case quantum_type
|
330
385
|
when :real_time then 2.hours
|
331
386
|
when :standard then 1.day
|
@@ -352,7 +407,7 @@ class QuantizedDataProcessor
|
|
352
407
|
process_bucket(current_bucket)
|
353
408
|
|
354
409
|
# Also process previous bucket in case of delayed data
|
355
|
-
previous_bucket = AnalyticsEvent.qstamp(5.minutes, time:
|
410
|
+
previous_bucket = AnalyticsEvent.qstamp(5.minutes, time: Familia.now - 5.minutes)
|
356
411
|
process_bucket(previous_bucket)
|
357
412
|
end
|
358
413
|
|
@@ -463,13 +518,13 @@ class GlobalMetrics < Familia::Horreum
|
|
463
518
|
|
464
519
|
def self.utc_hourly_key(time = nil)
|
465
520
|
# Always quantize in UTC for global consistency
|
466
|
-
utc_time = time&.utc ||
|
521
|
+
utc_time = time&.utc || Familia.now
|
467
522
|
qstamp(1.hour, pattern: '%Y%m%d%H', time: utc_time)
|
468
523
|
end
|
469
524
|
|
470
525
|
def self.local_daily_key(timezone, time = nil)
|
471
526
|
# Quantize in local timezone for regional reports
|
472
|
-
local_time = time ||
|
527
|
+
local_time = time || Familia.now
|
473
528
|
local_time = local_time.in_time_zone(timezone) if local_time.respond_to?(:in_time_zone)
|
474
529
|
qstamp(1.day, pattern: '%Y%m%d', time: local_time)
|
475
530
|
end
|
@@ -526,7 +581,7 @@ class CompactTimeSeriesStorage < Familia::Horreum
|
|
526
581
|
identifier_field :series_id
|
527
582
|
field :series_id, :metric_name, :quantum
|
528
583
|
|
529
|
-
# Store quantized data in Redis sorted sets for efficiency
|
584
|
+
# Store quantized data in Valkey/Redis sorted sets for efficiency
|
530
585
|
def record_value(value, time = nil)
|
531
586
|
bucket_timestamp = self.class.qstamp(quantum, time: time)
|
532
587
|
|
@@ -534,7 +589,7 @@ class CompactTimeSeriesStorage < Familia::Horreum
|
|
534
589
|
data_key = "#{series_id}:data"
|
535
590
|
Familia.dbclient.zadd(data_key, bucket_timestamp, value)
|
536
591
|
|
537
|
-
#
|
592
|
+
# UnsortedSet TTL based on quantum (longer quantum = longer retention)
|
538
593
|
ttl = calculate_retention_period
|
539
594
|
Familia.dbclient.expire(data_key, ttl)
|
540
595
|
end
|
@@ -696,8 +751,8 @@ end
|
|
696
751
|
class QuantizationMonitor
|
697
752
|
def self.analyze_bucket_distribution(metric_name, quantum, time_range = 24.hours)
|
698
753
|
buckets = {}
|
699
|
-
current_time =
|
700
|
-
end_time =
|
754
|
+
current_time = Familia.now - time_range
|
755
|
+
end_time = Familia.now
|
701
756
|
|
702
757
|
while current_time <= end_time
|
703
758
|
bucket = AnalyticsEvent.qstamp(quantum, time: current_time)
|
@@ -718,4 +773,14 @@ class QuantizationMonitor
|
|
718
773
|
end
|
719
774
|
```
|
720
775
|
|
776
|
+
---
|
777
|
+
|
778
|
+
## See Also
|
779
|
+
|
780
|
+
- **[Technical Reference](../reference/api-technical.md#quantization-feature-v200-pre7)** - Implementation details and advanced patterns
|
781
|
+
- **[Overview](../overview.md#time-based-quantization)** - Conceptual introduction to quantization
|
782
|
+
- **[Time Utilities Guide](time-utilities.md)** - Time manipulation and formatting utilities
|
783
|
+
- **[Feature System Guide](feature-system.md)** - Understanding Familia's feature architecture
|
784
|
+
- **[Implementation Guide](implementation.md)** - Production deployment and configuration patterns
|
785
|
+
|
721
786
|
The Quantization feature provides powerful time-based data organization capabilities, enabling efficient analytics, caching, and time-series data management in Familia applications.
|