java-topology/docs/tickets/rails-0011-time-zone-conversion-skip-list-array.md
russell@unturf.com 547a9f5738 ORM wave 2: 10 new defects — Active Record +3, Exposed +3, SeaORM +4 (167 sites, 64 ecosystems)
rails-0009: FilterAttributeHandler filter_parameters Array O(A×F) → Set (450×)
rails-0010: Encryption::AutoFilteredParameters two Array scans → Set (250×)
rails-0011: TimeZoneConversion skip_list Array O(M×C×S) → Set (20×)

exposed-0001: SchemaUtilityApi mapMissingColumnStatements O(N×M) → map (118×)
exposed-0002: IdentifierManagerApi isAKeyword O(K) linear → HashSet (144×)
exposed-0003: Table.clone consParams.map fresh List → hoisted HashSet (6×)

seaorm-0001: active_model establish_links leftover.any O(N²) → HashSet (501×)
seaorm-0002: rbac engine group_permissions .values().find() → HashMap by ID (502×)
seaorm-0003: schema builder sorted_tables Vec::contains → HashSet (500×)
seaorm-0004: TopologicalSort from_iter seen Vec O(N²) → BTreeSet (28×)

Unit tests: RailsTest 11/11, ExposedTest 3/3, SeaORMTest 4/4 PASS
Whitepaper: 157→167 sites, 62→64 ecosystems; §13.12 ORM Wave 2 added
2026-03-27 13:49:46 -04:00

65 lines
2.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# rails-0011: TimeZoneConversion — O(C×S+C×T) Array#include? per column during schema load
**Severity:** MEDIUM
**File:** activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
**Lines:** 85, 87
**Status:** PATCHED
## Description
`create_time_zone_conversion_attribute?` is called once per column per model class during
schema load (`hook_attribute_type``type_for_column``_default_attributes`). It
performs two Array `include?` checks per call:
**Line 85:**
```ruby
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
```
**Line 87:**
```ruby
time_zone_aware_types.include?(cast_type.type)
```
`skip_time_zone_conversion_for_attributes` defaults to `[]` and is set as a
`class_attribute`. `time_zone_aware_types` defaults to `[:datetime, :time]` (size 2, so
trivially fast), but `skip_time_zone_conversion_for_attributes` can grow large in
applications that skip many attributes — e.g., `self.skip_time_zone_conversion_for_attributes = all_columns`.
With C columns per model, S skip-list entries, and M model classes:
- Total cost: O(M × C × S)
On large apps with 100+ models of 50+ columns and skip lists of 20+ attributes,
this contributes measurably to boot time.
## Root Cause
`skip_time_zone_conversion_for_attributes` is a `class_attribute` initialized as `[]`
(Array). The `include?` check is O(S) per column per class.
## Fix
```ruby
# BEFORE (time_zone_conversion.rb:83-88)
def create_time_zone_conversion_attribute?(name, cast_type)
enabled_for_column = time_zone_aware_attributes &&
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
enabled_for_column && time_zone_aware_types.include?(cast_type.type)
end
# AFTER
def create_time_zone_conversion_attribute?(name, cast_type)
@skip_tz_set ||= skip_time_zone_conversion_for_attributes.to_set
@tz_aware_set ||= time_zone_aware_types.to_set
enabled_for_column = time_zone_aware_attributes &&
!@skip_tz_set.include?(name.to_sym)
enabled_for_column && @tz_aware_set.include?(cast_type.type)
end
```
Note: the cache must be invalidated when `skip_time_zone_conversion_for_attributes` is
reassigned — use `class_attribute` with a custom setter or reset on write.
## Speedup
~10x at M=100 models, C=50 columns, S=20 skip-list entries