legion-data 1.6.18 → 1.6.20

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/Gemfile +1 -0
  4. data/legion-data.gemspec +2 -2
  5. data/lib/legion/data/archival/policy.rb +7 -1
  6. data/lib/legion/data/archival.rb +27 -4
  7. data/lib/legion/data/archiver.rb +103 -51
  8. data/lib/legion/data/audit_record.rb +8 -5
  9. data/lib/legion/data/connection.rb +88 -17
  10. data/lib/legion/data/encryption/key_provider.rb +9 -2
  11. data/lib/legion/data/encryption/sequel_plugin.rb +126 -12
  12. data/lib/legion/data/event_store.rb +29 -10
  13. data/lib/legion/data/extract/handlers/base.rb +7 -1
  14. data/lib/legion/data/extract/handlers/csv.rb +1 -0
  15. data/lib/legion/data/extract/handlers/docx.rb +3 -1
  16. data/lib/legion/data/extract/handlers/html.rb +3 -1
  17. data/lib/legion/data/extract/handlers/json.rb +1 -0
  18. data/lib/legion/data/extract/handlers/jsonl.rb +1 -0
  19. data/lib/legion/data/extract/handlers/markdown.rb +1 -0
  20. data/lib/legion/data/extract/handlers/pdf.rb +3 -1
  21. data/lib/legion/data/extract/handlers/pptx.rb +3 -1
  22. data/lib/legion/data/extract/handlers/text.rb +1 -0
  23. data/lib/legion/data/extract/handlers/vtt.rb +1 -0
  24. data/lib/legion/data/extract/handlers/xlsx.rb +3 -1
  25. data/lib/legion/data/extract.rb +7 -0
  26. data/lib/legion/data/helper.rb +16 -6
  27. data/lib/legion/data/local.rb +62 -5
  28. data/lib/legion/data/migration.rb +6 -1
  29. data/lib/legion/data/migrations/044_expand_memory_traces.rb +4 -1
  30. data/lib/legion/data/model.rb +8 -4
  31. data/lib/legion/data/models/audit_log.rb +5 -1
  32. data/lib/legion/data/models/audit_record.rb +5 -1
  33. data/lib/legion/data/models/function.rb +5 -1
  34. data/lib/legion/data/models/node.rb +6 -2
  35. data/lib/legion/data/partition_manager.rb +15 -19
  36. data/lib/legion/data/retention.rb +31 -2
  37. data/lib/legion/data/rls.rb +8 -2
  38. data/lib/legion/data/settings.rb +5 -1
  39. data/lib/legion/data/spool.rb +69 -6
  40. data/lib/legion/data/storage_tiers.rb +16 -3
  41. data/lib/legion/data/vector.rb +9 -5
  42. data/lib/legion/data/version.rb +1 -1
  43. data/lib/legion/data.rb +39 -12
  44. metadata +5 -5
@@ -32,6 +32,7 @@ module Legion
32
32
  }
33
33
  }
34
34
  rescue StandardError => e
35
+ handle_exception(e, level: :warn, handled: true, operation: :extract_vtt)
35
36
  { text: nil, error: e.message }
36
37
  end
37
38
 
@@ -25,9 +25,11 @@ module Legion
25
25
  end
26
26
  text = sheets.join("\n\n")
27
27
  { text: text, metadata: { sheets: workbook.worksheets.size } }
28
- rescue LoadError
28
+ rescue LoadError => e
29
+ handle_exception(e, level: :warn, handled: true, operation: :extract_xlsx, gem: gem_name)
29
30
  { text: nil, error: :gem_not_installed, gem: gem_name }
30
31
  rescue StandardError => e
32
+ handle_exception(e, level: :warn, handled: true, operation: :extract_xlsx)
31
33
  { text: nil, error: e.message }
32
34
  end
33
35
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
3
4
  require_relative 'extract/type_detector'
4
5
  require_relative 'extract/handlers/base'
5
6
 
@@ -7,6 +8,8 @@ module Legion
7
8
  module Data
8
9
  module Extract
9
10
  class << self
11
+ include Legion::Logging::Helper
12
+
10
13
  def extract(source, type: :auto)
11
14
  detected_type = type == :auto ? TypeDetector.detect(source) : type&.to_sym
12
15
  return { success: false, text: nil, error: :unknown_type } unless detected_type
@@ -19,13 +22,17 @@ module Legion
19
22
  gem: handler.gem_name, type: detected_type }
20
23
  end
21
24
 
25
+ log.info "Extract starting type=#{detected_type} handler=#{handler.name}"
22
26
  result = handler.extract(source)
23
27
  if result[:text]
28
+ log.info "Extract succeeded type=#{detected_type}"
24
29
  { success: true, text: result[:text], metadata: result[:metadata], type: detected_type }
25
30
  else
31
+ log.warn "Extract failed type=#{detected_type} error=#{result[:error]}"
26
32
  { success: false, text: nil, error: result[:error], type: detected_type }
27
33
  end
28
34
  rescue StandardError => e
35
+ handle_exception(e, level: :error, handled: true, operation: :extract, type: detected_type)
29
36
  { success: false, text: nil, error: e.message, type: detected_type }
30
37
  end
31
38
 
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Helper
8
+ include Legion::Logging::Helper
9
+
6
10
  def data_path
7
11
  @data_path ||= "#{full_path}/data"
8
12
  end
@@ -39,7 +43,8 @@ module Legion
39
43
 
40
44
  def data_adapter
41
45
  Legion::Data::Connection.adapter
42
- rescue StandardError
46
+ rescue StandardError => e
47
+ handle_exception(e, level: :warn, handled: true, operation: :data_adapter)
43
48
  :unknown
44
49
  end
45
50
 
@@ -47,7 +52,8 @@ module Legion
47
52
  return {} unless data_connected?
48
53
 
49
54
  Legion::Data::Connection.pool_stats
50
- rescue StandardError
55
+ rescue StandardError => e
56
+ handle_exception(e, level: :warn, handled: true, operation: :data_pool_stats)
51
57
  {}
52
58
  end
53
59
 
@@ -55,7 +61,8 @@ module Legion
55
61
  return {} unless data_connected?
56
62
 
57
63
  Legion::Data.stats
58
- rescue StandardError
64
+ rescue StandardError => e
65
+ handle_exception(e, level: :warn, handled: true, operation: :data_stats)
59
66
  {}
60
67
  end
61
68
 
@@ -63,7 +70,8 @@ module Legion
63
70
  return {} unless local_data_connected?
64
71
 
65
72
  Legion::Data::Local.stats
66
- rescue StandardError
73
+ rescue StandardError => e
74
+ handle_exception(e, level: :warn, handled: true, operation: :local_data_stats)
67
75
  {}
68
76
  end
69
77
 
@@ -71,13 +79,15 @@ module Legion
71
79
 
72
80
  def data_can_read?(table_name)
73
81
  Legion::Data.can_read?(table_name)
74
- rescue StandardError
82
+ rescue StandardError => e
83
+ handle_exception(e, level: :warn, handled: true, operation: :data_can_read, table: table_name)
75
84
  false
76
85
  end
77
86
 
78
87
  def data_can_write?(table_name)
79
88
  Legion::Data.can_write?(table_name)
80
- rescue StandardError
89
+ rescue StandardError => e
90
+ handle_exception(e, level: :warn, handled: true, operation: :data_can_write, table: table_name)
81
91
  false
82
92
  end
83
93
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
4
+ require 'legion/logging/helper'
5
+
3
6
  require 'sequel'
4
7
  require 'sequel/extensions/migration'
5
8
 
@@ -7,12 +10,19 @@ module Legion
7
10
  module Data
8
11
  module Local
9
12
  class << self
13
+ include Legion::Logging::Helper
14
+
10
15
  attr_reader :connection, :db_path
11
16
 
12
17
  def setup(database: nil, **)
13
18
  return if @connected
14
19
 
15
20
  db_file = database || local_settings[:database] || 'legionio_local.db'
21
+ unless File.absolute_path?(db_file)
22
+ base_dir = File.expand_path('~/.legionio')
23
+ FileUtils.mkdir_p(base_dir)
24
+ db_file = File.join(base_dir, db_file)
25
+ end
16
26
  @db_path = db_file
17
27
 
18
28
  sqlite_defaults = Legion::Data::Connection::ADAPTER_DEFAULTS.fetch(:sqlite, {})
@@ -26,14 +36,24 @@ module Legion
26
36
  if local_settings[:query_log]
27
37
  log_path = File.join(Legion::Data::Connection::QUERY_LOG_DIR, 'data-local-query.log')
28
38
  @query_file_logger = Legion::Data::Connection::QueryFileLogger.new(log_path)
29
- opts[:logger] = @query_file_logger
30
- opts[:sql_log_level] = :debug
39
+ opts[:logger] = @query_file_logger
40
+ opts[:sql_log_level] = :debug
41
+ elsif data[:log] && defined?(Legion::Logging)
42
+ opts[:logger] = build_local_logger
43
+ opts[:sql_log_level] = resolved_sql_log_level
44
+ opts[:log_warn_duration] = resolved_log_warn_duration
31
45
  end
32
46
 
33
47
  @connection = ::Sequel.connect(opts)
48
+ @connection.run('PRAGMA journal_mode=WAL')
49
+ @connection.run('PRAGMA busy_timeout=30000')
50
+ @connection.run('PRAGMA synchronous=NORMAL')
34
51
  @connected = true
35
52
  run_migrations
36
- Legion::Logging.info "Legion::Data::Local connected to #{db_file}" if defined?(Legion::Logging)
53
+ log.info "Legion::Data::Local connected to #{db_file} (WAL mode, 30s busy_timeout)"
54
+ rescue StandardError => e
55
+ handle_exception(e, level: :error, handled: false, operation: :local_setup, database: db_file)
56
+ raise
37
57
  end
38
58
 
39
59
  def shutdown
@@ -82,7 +102,8 @@ module Legion
82
102
  wal_autocheckpoint cache_size busy_timeout].each do |pragma|
83
103
  val = begin
84
104
  @connection.fetch("PRAGMA #{pragma}").single_value
85
- rescue StandardError
105
+ rescue StandardError => e
106
+ handle_exception(e, level: :warn, handled: true, operation: :local_stats_pragma, pragma: pragma)
86
107
  nil
87
108
  end
88
109
  stats[pragma.to_sym] = val unless val.nil?
@@ -92,6 +113,7 @@ module Legion
92
113
 
93
114
  stats
94
115
  rescue StandardError => e
116
+ handle_exception(e, level: :warn, handled: true, operation: :local_stats, database: @db_path)
95
117
  { connected: connected?, error: e.message }
96
118
  end
97
119
 
@@ -119,7 +141,7 @@ module Legion
119
141
  table = :"schema_migrations_#{name}"
120
142
  ::Sequel::TimestampMigrator.new(@connection, path, table: table).run
121
143
  rescue StandardError => e
122
- Legion::Logging.warn "Local migration failed for #{path}: #{e.message}" if defined?(Legion::Logging)
144
+ handle_exception(e, level: :warn, handled: true, operation: :local_migration, name: name, path: path)
123
145
  end
124
146
 
125
147
  def local_settings
@@ -127,6 +149,41 @@ module Legion
127
149
 
128
150
  Legion::Settings[:data]&.dig(:local) || {}
129
151
  end
152
+
153
+ def build_local_logger
154
+ tagged = if defined?(Legion::Logging::TaggedLogger) && respond_to?(:tagged_logger_settings, true)
155
+ Legion::Logging::TaggedLogger.new(
156
+ segments: %w[data local],
157
+ **send(:tagged_logger_settings)
158
+ )
159
+ else
160
+ Legion::Data::Connection::SegmentedTaggedLogger.new(segments: %w[data local])
161
+ end
162
+ Legion::Data::Connection::SlowQueryLogger.new(tagged)
163
+ rescue StandardError => e
164
+ if respond_to?(:handle_exception, true)
165
+ handle_exception(e, level: :warn, handled: true, operation: :build_local_logger)
166
+ else
167
+ log.warn("build_local_logger failed: #{e.class}: #{e.message}")
168
+ end
169
+ Legion::Data::Connection::SlowQueryLogger.new(
170
+ Legion::Data::Connection::SegmentedTaggedLogger.new(segments: %w[data local], logger: log)
171
+ )
172
+ end
173
+
174
+ def resolved_sql_log_level
175
+ (local_settings[:sql_log_level] || Legion::Settings[:data][:sql_log_level] || 'debug').to_sym
176
+ rescue StandardError => e
177
+ handle_exception(e, level: :warn, handled: true, operation: :resolved_sql_log_level)
178
+ :debug
179
+ end
180
+
181
+ def resolved_log_warn_duration
182
+ local_settings[:log_warn_duration] || Legion::Settings[:data][:log_warn_duration]
183
+ rescue StandardError => e
184
+ handle_exception(e, level: :warn, handled: true, operation: :resolved_log_warn_duration)
185
+ nil
186
+ end
130
187
  end
131
188
  end
132
189
  end
@@ -1,16 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  require 'sequel/extensions/migration'
4
6
 
5
7
  module Legion
6
8
  module Data
7
9
  module Migration
8
10
  class << self
11
+ include Legion::Logging::Helper
12
+
9
13
  def migrate(connection = Legion::Data.connection, path = "#{__dir__}/migrations", **)
10
14
  Legion::Settings[:data][:migrations][:version] = Sequel::Migrator.run(connection, path, **)
11
- Legion::Logging.info("Legion::Data::Migration ran successfully to version #{Legion::Settings[:data][:migrations][:version]}")
15
+ log.info("Legion::Data::Migration ran successfully to version #{Legion::Settings[:data][:migrations][:version]}")
12
16
  Legion::Settings[:data][:migrations][:ran] = true
13
17
  rescue Sequel::DatabaseError => e
18
+ handle_exception(e, level: :error, handled: false, operation: :migrate, path: path)
14
19
  if e.message.include?('InsufficientPrivilege') || e.message.include?('permission denied')
15
20
  raise Sequel::DatabaseError,
16
21
  "#{e.message}\n Hint: the database user lacks CREATE on schema public " \
@@ -29,7 +29,10 @@ Sequel.migration do
29
29
 
30
30
  indexes = begin
31
31
  db.indexes(:memory_traces).keys
32
- rescue StandardError
32
+ rescue StandardError => e
33
+ if defined?(Legion::Data) && Legion::Data.respond_to?(:handle_exception)
34
+ Legion::Data.handle_exception(e, level: :warn, handled: true, operation: :migration_044_indexes)
35
+ end
33
36
  []
34
37
  end
35
38
 
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Models
6
8
  class << self
9
+ include Legion::Logging::Helper
10
+
7
11
  attr_reader :loaded_models
8
12
 
9
13
  def models
@@ -13,7 +17,7 @@ module Legion
13
17
  end
14
18
 
15
19
  def load
16
- Legion::Logging.info 'Loading Legion::Data::Models'
20
+ log.info 'Loading Legion::Data::Models'
17
21
  @loaded_models ||= []
18
22
  require_sequel_models(models)
19
23
  Legion::Settings[:data][:models][:loaded] = true
@@ -25,13 +29,13 @@ module Legion
25
29
  end
26
30
 
27
31
  def load_sequel_model(model)
28
- Legion::Logging.debug("Trying to load #{model}.rb")
32
+ log.debug("Trying to load #{model}.rb")
29
33
  require_relative "models/#{model}"
30
34
  @loaded_models << model
31
- Legion::Logging.debug("Successfully loaded #{model}")
35
+ log.debug("Successfully loaded #{model}")
32
36
  model
33
37
  rescue LoadError => e
34
- Legion::Logging.fatal("Failed to load #{model}")
38
+ handle_exception(e, level: :fatal, operation: :load_sequel_model, model: model)
35
39
  raise e unless Legion::Settings[:data][:models][:continue_on_fail]
36
40
  end
37
41
  end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Model
6
8
  class AuditLog < Sequel::Model(:audit_log)
9
+ include Legion::Logging::Helper
10
+
7
11
  VALID_EVENT_TYPES = %w[runner_execution lifecycle_transition].freeze
8
12
  VALID_STATUSES = %w[success failure denied].freeze
9
13
 
@@ -18,7 +22,7 @@ module Legion
18
22
 
19
23
  Legion::JSON.load(detail)
20
24
  rescue StandardError => e
21
- Legion::Logging.warn("AuditLog#parsed_detail JSON parse failed: #{e.message}") if defined?(Legion::Logging)
25
+ handle_exception(e, level: :warn, handled: true, operation: :parsed_detail, id: self[:id])
22
26
  nil
23
27
  end
24
28
 
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Model
6
8
  class AuditRecord < Sequel::Model(:audit_records)
9
+ include Legion::Logging::Helper
10
+
7
11
  # Enforce append-only semantics at the application layer.
8
12
  # PostgreSQL enforces this at the DB layer via rules (migration 058);
9
13
  # the application guard covers SQLite and MySQL.
@@ -21,7 +25,7 @@ module Legion
21
25
 
22
26
  Legion::JSON.load(metadata)
23
27
  rescue StandardError => e
24
- Legion::Logging.warn "AuditRecord#parsed_metadata failed: #{e.message}" if defined?(Legion::Logging)
28
+ handle_exception(e, level: :warn, handled: true, operation: :parsed_metadata, id: self[:id])
25
29
  {}
26
30
  end
27
31
  end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Model
6
8
  class Function < Sequel::Model
9
+ include Legion::Logging::Helper
10
+
7
11
  many_to_one :runner
8
12
  one_to_many :trigger_relationships, class: 'Legion::Data::Model::Relationship', key: :trigger_id
9
13
  one_to_many :action_relationships, class: 'Legion::Data::Model::Relationship', key: :action_id
@@ -13,7 +17,7 @@ module Legion
13
17
 
14
18
  ::JSON.parse(embedding)
15
19
  rescue ::JSON::ParserError => e
16
- Legion::Logging.debug("Function#embedding_vector JSON parse failed: #{e.message}") if defined?(Legion::Logging)
20
+ handle_exception(e, level: :debug, handled: true, operation: :embedding_vector, id: self[:id])
17
21
  nil
18
22
  end
19
23
 
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Model
6
8
  class Node < Sequel::Model
9
+ include Legion::Logging::Helper
10
+
7
11
  # one_to_many :task_log
8
12
 
9
13
  def parsed_metrics
@@ -11,7 +15,7 @@ module Legion
11
15
 
12
16
  Legion::JSON.load(metrics)
13
17
  rescue StandardError => e
14
- Legion::Logging.debug("Node#parsed_metrics JSON parse failed: #{e.message}") if defined?(Legion::Logging)
18
+ handle_exception(e, level: :debug, handled: true, operation: :parsed_metrics, id: self[:id])
15
19
  nil
16
20
  end
17
21
 
@@ -20,7 +24,7 @@ module Legion
20
24
 
21
25
  Legion::JSON.load(hosted_worker_ids)
22
26
  rescue StandardError => e
23
- Legion::Logging.debug("Node#parsed_hosted_worker_ids JSON parse failed: #{e.message}") if defined?(Legion::Logging)
27
+ handle_exception(e, level: :debug, handled: true, operation: :parsed_hosted_worker_ids, id: self[:id])
24
28
  []
25
29
  end
26
30
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module PartitionManager
6
8
  NOT_POSTGRES = { skipped: true, reason: 'not_postgres' }.freeze
7
9
 
8
10
  class << self
11
+ include Legion::Logging::Helper
12
+
9
13
  def ensure_partitions(table:, months_ahead: 3)
10
14
  return NOT_POSTGRES unless postgres?
11
15
 
@@ -28,16 +32,17 @@ module Legion
28
32
  after_count = partition_names_for(table).size
29
33
 
30
34
  if after_count > before_count
31
- log_info("Created partition #{partition}") if logging?
35
+ log.info("Created partition #{partition}")
32
36
  created << partition
33
37
  else
34
38
  existing << partition
35
39
  end
36
40
  end
37
41
 
42
+ log.info "PartitionManager ensure_partitions table=#{table} created=#{created.size} existing=#{existing.size}"
38
43
  { created: created, existing: existing }
39
44
  rescue StandardError => e
40
- log_warn("ensure_partitions failed for #{table}: #{e.message}") if logging?
45
+ handle_exception(e, level: :warn, handled: true, operation: :ensure_partitions, table: table, months_ahead: months_ahead)
41
46
  { created: [], existing: [], error: e.message }
42
47
  end
43
48
 
@@ -54,16 +59,17 @@ module Legion
54
59
 
55
60
  if part_date < cutoff
56
61
  Legion::Data.connection.run("DROP TABLE #{part}")
57
- log_info("Dropped partition #{part}") if logging?
62
+ log.info("Dropped partition #{part}")
58
63
  dropped << part
59
64
  else
60
65
  retained << part
61
66
  end
62
67
  end
63
68
 
69
+ log.info "PartitionManager drop_old_partitions table=#{table} dropped=#{dropped.size} retained=#{retained.size}"
64
70
  { dropped: dropped, retained: retained }
65
71
  rescue StandardError => e
66
- log_warn("drop_old_partitions failed for #{table}: #{e.message}") if logging?
72
+ handle_exception(e, level: :warn, handled: true, operation: :drop_old_partitions, table: table, retention_months: retention_months)
67
73
  { dropped: [], retained: [], error: e.message }
68
74
  end
69
75
 
@@ -80,12 +86,14 @@ module Legion
80
86
  ORDER BY c.relname
81
87
  SQL
82
88
 
83
- Legion::Data.connection.fetch(sql).map do |row|
89
+ partitions = Legion::Data.connection.fetch(sql).map do |row|
84
90
  from_val, to_val = parse_bound(row[:bound])
85
91
  { name: row[:name], from: from_val, to: to_val }
86
92
  end
93
+ log.info "PartitionManager list_partitions table=#{table} count=#{partitions.size}"
94
+ partitions
87
95
  rescue StandardError => e
88
- log_warn("list_partitions failed for #{table}: #{e.message}") if logging?
96
+ handle_exception(e, level: :warn, handled: true, operation: :list_partitions, table: table)
89
97
  []
90
98
  end
91
99
 
@@ -95,18 +103,6 @@ module Legion
95
103
  Legion::Data::Connection.adapter == :postgres
96
104
  end
97
105
 
98
- def logging?
99
- defined?(Legion::Logging)
100
- end
101
-
102
- def log_info(msg)
103
- Legion::Logging.info(msg)
104
- end
105
-
106
- def log_warn(msg)
107
- Legion::Logging.warn(msg)
108
- end
109
-
110
106
  def partition_name(table, date)
111
107
  "#{table}_y#{date.strftime('%Y')}m#{date.strftime('%m')}"
112
108
  end
@@ -136,7 +132,7 @@ module Legion
136
132
 
137
133
  Legion::Data.connection.fetch(sql).map { |row| row[:name] }
138
134
  rescue StandardError => e
139
- log_warn("partition_names_for #{table} failed: #{e.message}") if logging?
135
+ handle_exception(e, level: :warn, handled: true, operation: :partition_names_for, table: table)
140
136
  []
141
137
  end
142
138
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
3
4
  require_relative 'archival/policy'
4
5
 
5
6
  module Legion
@@ -9,6 +10,8 @@ module Legion
9
10
  DEFAULT_ARCHIVE_AFTER_DAYS = 90
10
11
 
11
12
  class << self
13
+ include Legion::Logging::Helper
14
+
12
15
  def archive_old_records(table:, date_column: nil, archive_after_days: DEFAULT_ARCHIVE_AFTER_DAYS)
13
16
  db = Legion::Data.connection
14
17
  return { archived: 0, table: table } unless db
@@ -29,8 +32,19 @@ module Legion
29
32
  end
30
33
  end
31
34
 
32
- Legion::Logging.info "Archived #{count} row(s) from #{table}" if defined?(Legion::Logging) && count.positive?
35
+ log.info "Archived #{count} row(s) from #{table}" if count.positive?
33
36
  { archived: count, table: table }
37
+ rescue StandardError => e
38
+ handle_exception(
39
+ e,
40
+ level: :error,
41
+ handled: false,
42
+ operation: :archive_old_records,
43
+ table: table,
44
+ date_column: date_column,
45
+ archive_after_days: archive_after_days
46
+ )
47
+ raise
34
48
  end
35
49
 
36
50
  def purge_expired_records(table:, date_column: nil, retention_years: DEFAULT_RETENTION_YEARS)
@@ -43,9 +57,20 @@ module Legion
43
57
  expired = db[archive_table].where(Sequel.identifier(date_column) < cutoff)
44
58
  count = expired.count
45
59
  expired.delete if count.positive?
46
- Legion::Logging.info "Purged #{count} expired row(s) from #{archive_table}" if defined?(Legion::Logging) && count.positive?
60
+ log.info "Purged #{count} expired row(s) from #{archive_table}" if count.positive?
47
61
 
48
62
  { purged: count, table: table }
63
+ rescue StandardError => e
64
+ handle_exception(
65
+ e,
66
+ level: :error,
67
+ handled: false,
68
+ operation: :purge_expired_records,
69
+ table: table,
70
+ date_column: date_column,
71
+ retention_years: retention_years
72
+ )
73
+ raise
49
74
  end
50
75
 
51
76
  def retention_status(table:, date_column: nil)
@@ -67,6 +92,9 @@ module Legion
67
92
  oldest_active: oldest_active,
68
93
  oldest_archived: oldest_archived
69
94
  }
95
+ rescue StandardError => e
96
+ handle_exception(e, level: :warn, handled: false, operation: :retention_status, table: table, date_column: date_column)
97
+ raise
70
98
  end
71
99
 
72
100
  def archive_table_name(table)
@@ -90,6 +118,7 @@ module Legion
90
118
 
91
119
  source_schema = db.schema(source_table).to_h
92
120
 
121
+ log.info "Creating archive table #{archive_table} from #{source_table}"
93
122
  db.create_table(archive_table) do
94
123
  source_schema.each do |col_name, col_info|
95
124
  column col_name, col_info[:db_type]
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Rls
8
+ extend Legion::Logging::Helper
9
+
6
10
  RLS_TABLES = %i[
7
11
  tasks digital_workers audit_log memory_traces extensions
8
12
  functions runners nodes settings value_metrics
@@ -14,7 +18,8 @@ module Legion
14
18
  return false unless Legion::Settings[:data][:connected]
15
19
 
16
20
  Legion::Data.connection.adapter_scheme == :postgres
17
- rescue StandardError
21
+ rescue StandardError => e
22
+ handle_exception(e, level: :warn, handled: true, operation: :rls_enabled)
18
23
  false
19
24
  end
20
25
 
@@ -30,7 +35,8 @@ module Legion
30
35
  return nil unless rls_enabled?
31
36
 
32
37
  Legion::Data.connection.fetch('SHOW app.current_tenant').first&.values&.first
33
- rescue Sequel::DatabaseError
38
+ rescue Sequel::DatabaseError => e
39
+ handle_exception(e, level: :warn, handled: true, operation: :current_tenant)
34
40
  nil
35
41
  end
36
42
 
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'legion/logging/helper'
4
+
3
5
  module Legion
4
6
  module Data
5
7
  module Settings
8
+ extend Legion::Logging::Helper
9
+
6
10
  CREDS = {
7
11
  sqlite: {
8
12
  database: 'legionio.db'
@@ -130,5 +134,5 @@ end
130
134
  begin
131
135
  Legion::Settings.merge_settings('data', Legion::Data::Settings.default) if Legion.const_defined?('Settings', false)
132
136
  rescue StandardError => e
133
- Legion::Logging.fatal(e.message) if Legion::Logging.method_defined?(:fatal)
137
+ Legion::Data::Settings.handle_exception(e, level: :fatal, operation: :merge_settings)
134
138
  end