legion-data 1.4.12 → 1.4.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32985bb416df4b3bf1d135609260830412479d6176d4503aa82e1ff4934382a3
4
- data.tar.gz: eb1d09e394c743f3334eeb0f3aee9584889a7a4863f190062f6b127dba246ad5
3
+ metadata.gz: c43d3a8453436e0a8b9293c6cd8f23686f1e73550ed62d27958ecc6a4e55ba02
4
+ data.tar.gz: 48da4423bb162af7d1e95ba854bce106131b8b1d6cc2e98f6d9205bfd32eb5e1
5
5
  SHA512:
6
- metadata.gz: 3c23bc39ed0649ed989e484947a33ca85f93b7a5f2b7ea487e862c8aa944c15b60f21c83a162a9ce45ea93b0f5221b66a2ca98f43e016ea1f2b7031ef9e3eee3
7
- data.tar.gz: b68e6b6f32262123983356bdfb2a26d243c2e96fbabc3057c285b65eb59f78578e0137147ad82c18f8af8d000f7a25fde9618c54fb9c060e42286a71f8465dea
6
+ metadata.gz: 96884671abf0ef7ed9fdef58c85e332a186043d35d9bd29210d158fb92a3f7bc7d2e60e8ec5a759f7d854fe89ef7892773b4c81cb841ad8655a103da3401fed4
7
+ data.tar.gz: 875a80126a77632a4af0604bbb447ddf54646ae37e072f3018a789270b8fb258cf214fc02aa3a639c6c31bcba504c7e639ee4141d5906524eaa5561c9b418a2a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Legion::Data Changelog
2
2
 
3
+ ## [1.4.13] - 2026-03-22
4
+
5
+ ### Added
6
+ - Comprehensive logging across data operations: connection lifecycle, archival, retention, storage tiers, event store, encryption key provider, spool drain, and vector search
7
+ - `Connection.setup`: `.info` on successful connect (adapter://host:port/db or SQLite path)
8
+ - `Connection.shutdown`: `.info` on disconnect
9
+ - `Connection.connect_with_replicas`: `.debug` with replica count
10
+ - `Data.setup`: `.info` on setup completion
11
+ - `Data.shutdown`: `.info` on shutdown
12
+ - `Archiver.archive_table`: `.info` on start and completion with table name and row count; `.warn` before re-raising S3/Azure upload failures
13
+ - `Archival.archive!`: `.info` with table, destination, cutoff, and dry_run flag; `.info` on restore with row count
14
+ - `Retention.archive_old_records`: `.info` with table name and archived row count
15
+ - `Retention.purge_expired_records`: `.info` with archive table name and purged row count
16
+ - `StorageTiers.archive_to_warm`: `.info` with table name and row count
17
+ - `StorageTiers.export_to_cold`: `.info` with exported row count
18
+ - `EventStore.append`: `.debug` with stream, event type, and sequence number
19
+ - `EventStore.verify_chain`: `.warn` when hash chain is broken, with stream and sequence number
20
+ - `Encryption::KeyProvider`: `.warn` on dev key fallback; `.debug` on Vault key derivation
21
+ - `Encryption::SequelPlugin`: `.warn` on decrypt failure before re-raise
22
+ - `Spool#write`: `.debug` with sub-namespace and filename
23
+ - `Spool#flush`: `.info` with sub-namespace and drained item count
24
+ - `Vector.ensure_extension!`: `.info` on successful pgvector setup
25
+ - `Vector.cosine_search` / `Vector.l2_search`: `.debug` with table, column, and limit
26
+
3
27
  ## [1.4.12] - 2026-03-21
4
28
 
5
29
  ### Added
data/CODEOWNERS CHANGED
@@ -1 +1,41 @@
1
+ # Default owner — all files
1
2
  * @Esity
3
+
4
+ # Core library code
5
+ # lib/ @Esity @future-core-team
6
+
7
+ # Database connection
8
+ # lib/legion/data/connection.rb @Esity @future-infra-team
9
+
10
+ # Migrations
11
+ # lib/legion/data/migrations/ @Esity @future-core-team
12
+
13
+ # Models
14
+ # lib/legion/data/models/ @Esity @future-core-team
15
+
16
+ # Local SQLite (agentic cognitive state)
17
+ # lib/legion/data/local.rb @Esity @future-ai-team
18
+ # lib/legion/data/local/ @Esity @future-ai-team
19
+
20
+ # Encryption at rest
21
+ # lib/legion/data/encryption/ @Esity @future-security-team
22
+
23
+ # Event store (governance)
24
+ # lib/legion/data/event_store/ @Esity @future-security-team
25
+ # lib/legion/data/event_store.rb @Esity @future-security-team
26
+
27
+ # Vector helpers (pgvector / Apollo)
28
+ # lib/legion/data/vector.rb @Esity @future-ai-team
29
+
30
+ # Storage tiers and archival
31
+ # lib/legion/data/storage_tiers.rb @Esity @future-infra-team
32
+ # lib/legion/data/archival/ @Esity @future-infra-team
33
+
34
+ # Specs
35
+ # spec/ @Esity @future-contributors
36
+
37
+ # Documentation
38
+ # *.md @Esity @future-docs-team
39
+
40
+ # CI/CD
41
+ # .github/ @Esity
@@ -18,6 +18,7 @@ module Legion
18
18
  archive_table = ARCHIVE_TABLE_MAP[table]
19
19
  next unless archive_table && db_ready?(table) && db_ready?(archive_table)
20
20
 
21
+ Legion::Logging.info "Archiving #{table} -> #{archive_table} (cutoff: #{policy.warm_cutoff}, dry_run: #{dry_run})" if defined?(Legion::Logging)
21
22
  count = archive_table!(
22
23
  source: table, destination: archive_table,
23
24
  cutoff: policy.warm_cutoff, batch_size: policy.batch_size, dry_run: dry_run
@@ -45,6 +46,7 @@ module Legion
45
46
  end
46
47
  conn[archive_table].where(original_id: ids).delete
47
48
  end
49
+ Legion::Logging.info "Restored #{restored} row(s) from #{archive_table} -> #{source_table}" if defined?(Legion::Logging)
48
50
  restored
49
51
  end
50
52
 
@@ -16,6 +16,8 @@ module Legion
16
16
  def archive_table(table:, retention_days: 90, batch_size: 1000, storage_backend: nil)
17
17
  return { skipped: true, reason: 'not_postgres' } unless postgres?
18
18
 
19
+ Legion::Logging.info "Archiving table #{table} (retention: #{retention_days}d)" if defined?(Legion::Logging)
20
+
19
21
  conn = Legion::Data.connection
20
22
  cutoff = Time.now - (retention_days * 86_400)
21
23
  now = Time.now.utc
@@ -62,6 +64,7 @@ module Legion
62
64
  paths << path
63
65
  end
64
66
 
67
+ Legion::Logging.info "Archived #{total_rows} rows from #{table} in #{batches} batch(es)" if defined?(Legion::Logging)
65
68
  { batches: batches, total_rows: total_rows, paths: paths }
66
69
  end
67
70
 
@@ -134,6 +137,7 @@ module Legion
134
137
  rescue UploadError
135
138
  raise
136
139
  rescue StandardError => e
140
+ Legion::Logging.warn "S3 upload failed: #{e.message}" if defined?(Legion::Logging)
137
141
  raise UploadError, "S3 upload failed: #{e.message}"
138
142
  end
139
143
 
@@ -148,6 +152,7 @@ module Legion
148
152
  rescue UploadError
149
153
  raise
150
154
  rescue StandardError => e
155
+ Legion::Logging.warn "Azure upload failed: #{e.message}" if defined?(Legion::Logging)
151
156
  raise UploadError, "Azure upload failed: #{e.message}"
152
157
  end
153
158
 
@@ -33,6 +33,14 @@ module Legion
33
33
  end
34
34
  end
35
35
  Legion::Settings[:data][:connected] = true
36
+ if defined?(Legion::Logging)
37
+ if adapter == :sqlite
38
+ Legion::Logging.info "Connected to SQLite at #{sqlite_path}"
39
+ else
40
+ creds = Legion::Data::Settings.creds(adapter)
41
+ Legion::Logging.info "Connected to #{adapter}://#{creds[:host]}:#{creds[:port]}/#{creds[:database] || creds[:db]}"
42
+ end
43
+ end
36
44
  configure_logging
37
45
  connect_with_replicas
38
46
  end
@@ -40,6 +48,7 @@ module Legion
40
48
  def shutdown
41
49
  @sequel&.disconnect
42
50
  Legion::Settings[:data][:connected] = false
51
+ Legion::Logging.info 'Legion::Data connection closed' if defined?(Legion::Logging)
43
52
  end
44
53
 
45
54
  def connect_with_replicas
@@ -61,6 +70,7 @@ module Legion
61
70
  end
62
71
 
63
72
  @replica_servers = replica_list.each_with_index.map { |_, idx| :"read_#{idx}" }
73
+ Legion::Logging.debug "Registered #{@replica_servers.size} read replica(s)" if defined?(Legion::Logging)
64
74
  end
65
75
 
66
76
  def read_server
@@ -24,10 +24,12 @@ module Legion
24
24
 
25
25
  def derive_key(tenant_id)
26
26
  if tenant_id && crypt_available?
27
+ Legion::Logging.debug "Deriving Vault key for tenant #{tenant_id}" if defined?(Legion::Logging)
27
28
  Legion::Crypt::PartitionKeys.derive(tenant_id: tenant_id)
28
29
  elsif crypt_available?
29
30
  Legion::Crypt.default_encryption_key
30
31
  else
32
+ Legion::Logging.warn 'Legion::Crypt unavailable, falling back to dev encryption key' if defined?(Legion::Logging)
31
33
  local_key
32
34
  end
33
35
  end
@@ -24,7 +24,12 @@ module Legion
24
24
  tenant = col_scope == :tenant ? self[:tenant_id] : nil
25
25
  key = provider.key_for(tenant_id: tenant)
26
26
  aad = "#{self.class.table_name}:#{pk}:#{name}"
27
- Legion::Data::Encryption::Cipher.decrypt(raw.b, key: key, aad: aad)
27
+ begin
28
+ Legion::Data::Encryption::Cipher.decrypt(raw.b, key: key, aad: aad)
29
+ rescue StandardError => e
30
+ Legion::Logging.warn "Decrypt failed for #{self.class.table_name}##{pk} column #{name}: #{e.message}" if defined?(Legion::Logging)
31
+ raise
32
+ end
28
33
  end
29
34
 
30
35
  define_method(:"#{name}=") do |value|
@@ -42,6 +42,7 @@ module Legion
42
42
  created_at: Time.now
43
43
  )
44
44
 
45
+ Legion::Logging.debug "EventStore append: stream=#{stream} type=#{type} seq=#{seq}" if defined?(Legion::Logging)
45
46
  { stream: stream, sequence: seq, hash: event_hash }
46
47
  end
47
48
  end
@@ -73,8 +74,14 @@ module Legion
73
74
  prev_hash = '0' * 64
74
75
  events.each do |e|
75
76
  expected = compute_hash(stream, e[:sequence_number], e[:event_type], e[:data_json], prev_hash)
76
- return { valid: false, broken_at: e[:sequence_number] } unless e[:event_hash] == expected
77
- return { valid: false, broken_at: e[:sequence_number] } unless e[:previous_hash] == prev_hash
77
+ unless e[:event_hash] == expected
78
+ Legion::Logging.warn "EventStore chain broken: stream=#{stream} seq=#{e[:sequence_number]}" if defined?(Legion::Logging)
79
+ return { valid: false, broken_at: e[:sequence_number] }
80
+ end
81
+ unless e[:previous_hash] == prev_hash
82
+ Legion::Logging.warn "EventStore chain broken: stream=#{stream} seq=#{e[:sequence_number]}" if defined?(Legion::Logging)
83
+ return { valid: false, broken_at: e[:sequence_number] }
84
+ end
78
85
 
79
86
  prev_hash = e[:event_hash]
80
87
  end
@@ -26,6 +26,7 @@ module Legion
26
26
  end
27
27
  end
28
28
 
29
+ Legion::Logging.info "Archived #{count} row(s) from #{table}" if defined?(Legion::Logging) && count.positive?
29
30
  { archived: count, table: table }
30
31
  end
31
32
 
@@ -38,6 +39,7 @@ module Legion
38
39
  expired = db[archive_table].where(Sequel.lit("#{date_column} < ?", cutoff))
39
40
  count = expired.count
40
41
  expired.delete if count.positive?
42
+ Legion::Logging.info "Purged #{count} expired row(s) from #{archive_table}" if defined?(Legion::Logging) && count.positive?
41
43
 
42
44
  { purged: count, table: table }
43
45
  end
@@ -41,6 +41,7 @@ module Legion
41
41
  filename = "#{Time.now.strftime('%s%9N')}-#{SecureRandom.uuid}.json"
42
42
  path = File.join(dir, filename)
43
43
  File.write(path, ::JSON.generate(payload))
44
+ Legion::Logging.debug "Spool write: #{sub_namespace} -> #{filename}" if defined?(Legion::Logging)
44
45
  path
45
46
  end
46
47
 
@@ -56,6 +57,7 @@ module Legion
56
57
  File.delete(path)
57
58
  count += 1
58
59
  end
60
+ Legion::Logging.info "Spool drained #{count} item(s) from #{sub_namespace}" if defined?(Legion::Logging) && count.positive?
59
61
  count
60
62
  end
61
63
 
@@ -28,6 +28,7 @@ module Legion
28
28
  Legion::Data.connection[table].where(id: ids).delete
29
29
  end
30
30
 
31
+ Legion::Logging.info "Archived #{records.size} row(s) from #{table} to warm tier" if defined?(Legion::Logging)
31
32
  { archived: records.size, table: table.to_s }
32
33
  end
33
34
 
@@ -43,6 +44,7 @@ module Legion
43
44
 
44
45
  ids = records.map { |r| r[:id] }
45
46
  Legion::Data.connection[:data_archive].where(id: ids).update(tier: TIERS[:cold])
47
+ Legion::Logging.info "Exported #{records.size} row(s) to cold tier" if defined?(Legion::Logging)
46
48
  { exported: records.size, data: records }
47
49
  end
48
50
 
@@ -17,6 +17,7 @@ module Legion
17
17
  return false unless Legion::Data.connection&.adapter_scheme == :postgres
18
18
 
19
19
  Legion::Data.connection.run('CREATE EXTENSION IF NOT EXISTS vector')
20
+ Legion::Logging.info 'pgvector extension enabled' if defined?(Legion::Logging)
20
21
  true
21
22
  rescue StandardError => e
22
23
  Legion::Logging.warn("pgvector extension creation failed: #{e.message}") if defined?(Legion::Logging)
@@ -26,6 +27,7 @@ module Legion
26
27
  def cosine_search(table:, column:, query_vector:, limit: 10, min_similarity: 0.0)
27
28
  return [] unless available?
28
29
 
30
+ Legion::Logging.debug "Vector cosine_search: table=#{table} column=#{column} limit=#{limit}" if defined?(Legion::Logging)
29
31
  vec_literal = vector_literal(query_vector)
30
32
  ds = Legion::Data.connection[table]
31
33
  .select_all
@@ -40,6 +42,7 @@ module Legion
40
42
  def l2_search(table:, column:, query_vector:, limit: 10)
41
43
  return [] unless available?
42
44
 
45
+ Legion::Logging.debug "Vector l2_search: table=#{table} column=#{column} limit=#{limit}" if defined?(Legion::Logging)
43
46
  vec_literal = vector_literal(query_vector)
44
47
  Legion::Data.connection[table]
45
48
  .select_all
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Data
5
- VERSION = '1.4.12'
5
+ VERSION = '1.4.13'
6
6
  end
7
7
  end
data/lib/legion/data.rb CHANGED
@@ -21,6 +21,7 @@ module Legion
21
21
  load_models
22
22
  setup_cache
23
23
  setup_local
24
+ Legion::Logging.info 'Legion::Data setup complete' if defined?(Legion::Logging)
24
25
  end
25
26
 
26
27
  def connection_setup
@@ -66,6 +67,7 @@ module Legion
66
67
  def shutdown
67
68
  Legion::Data::Local.shutdown if defined?(Legion::Data::Local) && Legion::Data::Local.connected?
68
69
  Legion::Data::Connection.shutdown
70
+ Legion::Logging.info 'Legion::Data shutdown complete' if defined?(Legion::Logging)
69
71
  end
70
72
 
71
73
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-data
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.12
4
+ version: 1.4.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity