Module: Familia::Horreum::AuditMethods
- Included in:
- ManagementMethods
- Defined in:
- lib/familia/horreum/management/audit.rb
Overview
AuditMethods provides proactive consistency detection for Horreum models.
Included in ManagementMethods so every Horreum subclass gets these as class methods (e.g. Customer.audit_instances, Customer.health_check).
Instance Method Summary collapse
-
#audit_instances(batch_size: 100) {|Hash| ... } ⇒ Hash
Compares the instances timeline against actual DB keys via SCAN.
-
#audit_multi_indexes ⇒ Array<Hash>
Audits all multi indexes.
-
#audit_participations(sample_size: nil) ⇒ Array<Hash>
Audits participation collections for stale members.
-
#audit_unique_indexes ⇒ Array<Hash>
Audits all unique indexes (class-level only, where within is nil).
-
#health_check(batch_size: 100, sample_size: nil) {|Hash| ... } ⇒ AuditReport
Runs all four audits and wraps results in an AuditReport.
Instance Method Details
#audit_instances(batch_size: 100) {|Hash| ... } ⇒ Hash
Compares the instances timeline against actual DB keys via SCAN.
Detects:
- Phantoms: identifiers in timeline but no corresponding hash key
- Missing: hash keys in DB but not in timeline
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/familia/horreum/management/audit.rb', line 25 def audit_instances(batch_size: 100, &progress) # Phase 1: Collect identifiers from timeline timeline_ids = Set.new(instances.members) progress&.call(phase: :timeline_collected, current: timeline_ids.size, total: nil) # Phase 2: SCAN keys and extract identifiers (source of truth) scan_ids = scan_identifiers(batch_size: batch_size, &progress) # Phase 3: Set differences phantoms = (timeline_ids - scan_ids).to_a missing = (scan_ids - timeline_ids).to_a { phantoms: phantoms, missing: missing, count_timeline: timeline_ids.size, count_scan: scan_ids.size, } end |
#audit_multi_indexes ⇒ Array<Hash>
Audits all multi indexes.
For each multi index:
- SCANs for per-value set keys
- Checks that each member exists and field value matches
- Detects orphaned set keys (sets for values no object has)
71 72 73 74 75 76 77 |
# File 'lib/familia/horreum/management/audit.rb', line 71 def audit_multi_indexes return [] unless respond_to?(:indexing_relationships) indexing_relationships.select { |r| r.cardinality == :multi }.map { |rel| audit_single_multi_index(rel) } end |
#audit_participations(sample_size: nil) ⇒ Array<Hash>
Audits participation collections for stale members.
For each participation relationship defined on this class:
- Class-level: checks the single class collection directly
- Instance-level: SCANs for collection keys on the target class
- Enumerates raw members of each collection
- Verifies each referenced participant object still exists
90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/familia/horreum/management/audit.rb', line 90 def audit_participations(sample_size: nil) return [] unless respond_to?(:participation_relationships) participation_relationships.flat_map { |rel| if rel.target_class == self # Class-level participation (class_participates_in) [audit_class_participation(rel, sample_size: sample_size)] else # Instance-level participation (participates_in TargetClass, :collection) audit_instance_participations(rel, sample_size: sample_size) end } end |
#audit_unique_indexes ⇒ Array<Hash>
Audits all unique indexes (class-level only, where within is nil).
For each unique index:
- Reads all entries from the index HashKey
- Checks that each indexed object exists and its field value matches
- Checks for objects that should be indexed but aren't
54 55 56 57 58 59 60 |
# File 'lib/familia/horreum/management/audit.rb', line 54 def audit_unique_indexes return [] unless respond_to?(:indexing_relationships) indexing_relationships.select { |r| r.cardinality == :unique && r.within.nil? }.map { |rel| audit_single_unique_index(rel) } end |
#health_check(batch_size: 100, sample_size: nil) {|Hash| ... } ⇒ AuditReport
Runs all four audits and wraps results in an AuditReport.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/familia/horreum/management/audit.rb', line 111 def health_check(batch_size: 100, sample_size: nil, &progress) start_time = Familia.now inst = audit_instances(batch_size: batch_size, &progress) uniq = audit_unique_indexes multi = audit_multi_indexes parts = audit_participations(sample_size: sample_size) duration = Familia.now - start_time AuditReport.new( model_class: name, audited_at: start_time, instances: inst, unique_indexes: uniq, multi_indexes: multi, participations: parts, duration: duration ) end |