lex-llm-ledger 0.2.9 → 0.3.1
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/CHANGELOG.md +13 -0
- data/lib/legion/extensions/llm/ledger/actors/metering.rb +2 -0
- data/lib/legion/extensions/llm/ledger/actors/prompts.rb +2 -0
- data/lib/legion/extensions/llm/ledger/actors/tools.rb +2 -0
- data/lib/legion/extensions/llm/ledger/version.rb +1 -1
- data/lib/legion/extensions/llm/ledger/writers/official_record_writer.rb +195 -154
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a69d4ccf5975f45c795e6e54ab6ed59272a269855394bbef4d1149a48bd338e7
|
|
4
|
+
data.tar.gz: 60bfc3f268526d0cf427ccfa90f2e230f9e0491fd88584ff685604c93faf63c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e166ea0dc31b758dd45d5c1595f6a72c78629b7dfd69d09d9928a699ce14f475658d9bf77f08018608bc98f394532f4c19b5887c3b2649bb1761aecbe2b4b5c8
|
|
7
|
+
data.tar.gz: 226be9d0592d67fcca0ff956fe0c62db56a311cc3f6bde5a16523f21801d43079d976a6d6582e2a0f1022396089555495df37ccf81ccfd56ddfd3f2344f9d27f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.1] - 2026-05-13
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Recover cleanly when concurrent ledger consumers create the same conversation, request, response, metric, or identity rows.
|
|
7
|
+
- Keep duplicate insert recovery inside savepoints so PostgreSQL transactions remain usable after unique constraint races.
|
|
8
|
+
- Remove temporary prompt runner debug output while preserving single-message subscription prefetch behavior.
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-05-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Renamed all `portable_identity_*` table references to canonical identity table names (`identities`, `identity_principals`, `identity_providers`).
|
|
14
|
+
- Renamed internal methods: `resolve_portable_identity` → `resolve_identity`, `find_or_create_portable_identity` → `find_or_create_identity`.
|
|
15
|
+
|
|
3
16
|
## [0.2.9] - 2026-05-07
|
|
4
17
|
|
|
5
18
|
### Fixed
|
|
@@ -57,20 +57,26 @@ module Legion
|
|
|
57
57
|
existing = db[:llm_conversations].where(uuid: uuid).first
|
|
58
58
|
return existing if existing
|
|
59
59
|
|
|
60
|
-
id =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
id = insert_with_savepoint(db, :llm_conversations, {
|
|
61
|
+
uuid: uuid,
|
|
62
|
+
title: body[:title] || body[:conversation_title],
|
|
63
|
+
classification_level: classification_level(body),
|
|
64
|
+
contains_phi: contains_phi?(body),
|
|
65
|
+
contains_pii: contains_pii?(body),
|
|
66
|
+
retention_policy: body[:retention_policy] || 'default',
|
|
67
|
+
expires_at: body[:expires_at],
|
|
68
|
+
recorded_at: recorded_at(body),
|
|
69
|
+
inserted_at: Time.now.utc,
|
|
70
|
+
created_at: Time.now.utc,
|
|
71
|
+
updated_at: Time.now.utc
|
|
72
|
+
}, operation: 'official_record_writer.conversation')
|
|
73
73
|
db[:llm_conversations][id: id]
|
|
74
|
+
rescue Sequel::UniqueConstraintViolation => e
|
|
75
|
+
log.debug("[ledger] conversation collision resolved uuid=#{uuid} error=#{e.class}")
|
|
76
|
+
existing = db[:llm_conversations].where(uuid: uuid).first
|
|
77
|
+
return existing if existing
|
|
78
|
+
|
|
79
|
+
raise
|
|
74
80
|
end
|
|
75
81
|
|
|
76
82
|
def find_or_create_user_message(db, conversation, body)
|
|
@@ -80,20 +86,18 @@ module Legion
|
|
|
80
86
|
|
|
81
87
|
seq = body[:message_seq] ? integer(body[:message_seq]) : next_message_seq(db, conversation)
|
|
82
88
|
begin
|
|
83
|
-
id = db
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}, operation: 'official_record_writer.user_message')
|
|
96
|
-
end
|
|
89
|
+
id = insert_with_savepoint(db, :llm_messages, {
|
|
90
|
+
uuid: uuid,
|
|
91
|
+
conversation_id: conversation[:id],
|
|
92
|
+
seq: seq,
|
|
93
|
+
role: 'user',
|
|
94
|
+
content_type: 'text',
|
|
95
|
+
content: request_content(body),
|
|
96
|
+
input_tokens: tokens(body)[:input_tokens],
|
|
97
|
+
output_tokens: 0,
|
|
98
|
+
created_at: recorded_at(body),
|
|
99
|
+
inserted_at: Time.now.utc
|
|
100
|
+
}, operation: 'official_record_writer.user_message')
|
|
97
101
|
db[:llm_messages][id: id]
|
|
98
102
|
rescue Sequel::UniqueConstraintViolation => e
|
|
99
103
|
log.debug("[ledger] seq collision resolved uuid=#{uuid} conversation_id=#{conversation[:id]} error=#{e.class}")
|
|
@@ -112,31 +116,40 @@ module Legion
|
|
|
112
116
|
|
|
113
117
|
operation = operation(body)
|
|
114
118
|
caller_refs = caller_identity_refs(db, body)
|
|
115
|
-
id =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
119
|
+
id = insert_with_savepoint(db, :llm_message_inference_requests, {
|
|
120
|
+
uuid: stable_uuid(request_id),
|
|
121
|
+
conversation_id: conversation[:id],
|
|
122
|
+
latest_message_id: latest_message[:id],
|
|
123
|
+
caller_principal_id: caller_refs[:principal_id],
|
|
124
|
+
caller_identity_id: caller_refs[:identity_id],
|
|
125
|
+
runtime_caller_type: caller_type(body),
|
|
126
|
+
request_ref: request_id,
|
|
127
|
+
correlation_ref: correlation_id(body),
|
|
128
|
+
correlation_id: correlation_id(body),
|
|
129
|
+
exchange_ref: body[:exchange_id],
|
|
130
|
+
request_type: operation,
|
|
131
|
+
operation: operation,
|
|
132
|
+
idempotency_key: body[:idempotency_key] || request_id,
|
|
133
|
+
status: 'responded',
|
|
134
|
+
context_message_count: Array(body.dig(:request, :messages) || body[:messages]).size,
|
|
135
|
+
request_capture_mode: 'full',
|
|
136
|
+
request_json: json_dump(request_payload(body)),
|
|
137
|
+
classification_level: classification_level(body),
|
|
138
|
+
cost_center: billing(body)[:cost_center],
|
|
139
|
+
budget_key: billing(body)[:budget_id] || billing(body)[:budget_key],
|
|
140
|
+
requested_at: recorded_at(body),
|
|
141
|
+
inserted_at: Time.now.utc
|
|
142
|
+
}, operation: 'official_record_writer.inference_request')
|
|
139
143
|
db[:llm_message_inference_requests][id: id]
|
|
144
|
+
rescue Sequel::UniqueConstraintViolation => e
|
|
145
|
+
log.debug("[ledger] request collision resolved request_ref=#{request_id} error=#{e.class}")
|
|
146
|
+
existing = db[:llm_message_inference_requests].where(request_ref: request_id).first
|
|
147
|
+
if existing
|
|
148
|
+
enrich_request!(db, existing, body)
|
|
149
|
+
return existing
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
raise
|
|
140
153
|
end
|
|
141
154
|
|
|
142
155
|
def find_or_create_response_message(db, conversation, request, body)
|
|
@@ -147,22 +160,20 @@ module Legion
|
|
|
147
160
|
latest = db[:llm_messages][id: request[:latest_message_id]]
|
|
148
161
|
seq = (latest&.dig(:seq) || 1) + 1
|
|
149
162
|
begin
|
|
150
|
-
id = db
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}, operation: 'official_record_writer.response_message')
|
|
165
|
-
end
|
|
163
|
+
id = insert_with_savepoint(db, :llm_messages, {
|
|
164
|
+
uuid: uuid,
|
|
165
|
+
conversation_id: conversation[:id],
|
|
166
|
+
parent_message_id: latest&.dig(:id),
|
|
167
|
+
message_inference_request_id: request[:id],
|
|
168
|
+
seq: seq,
|
|
169
|
+
role: 'assistant',
|
|
170
|
+
content_type: 'text',
|
|
171
|
+
content: response_content(body),
|
|
172
|
+
input_tokens: 0,
|
|
173
|
+
output_tokens: tokens(body)[:output_tokens],
|
|
174
|
+
created_at: recorded_at(body),
|
|
175
|
+
inserted_at: Time.now.utc
|
|
176
|
+
}, operation: 'official_record_writer.response_message')
|
|
166
177
|
db[:llm_messages][id: id]
|
|
167
178
|
rescue Sequel::UniqueConstraintViolation => e
|
|
168
179
|
log.debug("[ledger] seq collision resolved uuid=#{uuid} conversation_id=#{conversation[:id]} error=#{e.class}")
|
|
@@ -179,28 +190,37 @@ module Legion
|
|
|
179
190
|
return existing
|
|
180
191
|
end
|
|
181
192
|
|
|
182
|
-
id =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
193
|
+
id = insert_with_savepoint(db, :llm_message_inference_responses, {
|
|
194
|
+
uuid: response_uuid,
|
|
195
|
+
message_inference_request_id: request[:id],
|
|
196
|
+
response_message_id: response_message&.dig(:id),
|
|
197
|
+
provider: provider(body),
|
|
198
|
+
provider_instance: provider_instance(body),
|
|
199
|
+
model_key: model_id(body),
|
|
200
|
+
tier: tier(body),
|
|
201
|
+
runner_ref: body[:worker_id] || body[:runner_ref],
|
|
202
|
+
provider_response_ref: body[:provider_response_ref],
|
|
203
|
+
status: body[:error] ? 'error' : 'success',
|
|
204
|
+
finish_reason: finish_reason(body),
|
|
205
|
+
latency_ms: integer(body[:latency_ms]),
|
|
206
|
+
wall_clock_ms: integer(body[:wall_clock_ms]),
|
|
207
|
+
response_capture_mode: 'full',
|
|
208
|
+
response_json: json_dump(visible_response(body)),
|
|
209
|
+
response_thinking_json: json_dump(thinking_response(body)),
|
|
210
|
+
dispatch_path: body[:dispatch_path] || body[:tier],
|
|
211
|
+
responded_at: recorded_at(body),
|
|
212
|
+
inserted_at: Time.now.utc
|
|
213
|
+
}, operation: 'official_record_writer.inference_response')
|
|
203
214
|
db[:llm_message_inference_responses][id: id]
|
|
215
|
+
rescue Sequel::UniqueConstraintViolation => e
|
|
216
|
+
log.debug("[ledger] response collision resolved uuid=#{response_uuid} error=#{e.class}")
|
|
217
|
+
existing = db[:llm_message_inference_responses].where(uuid: response_uuid).first
|
|
218
|
+
if existing
|
|
219
|
+
enrich_response!(db, existing, response_message, body)
|
|
220
|
+
return existing
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
raise
|
|
204
224
|
end
|
|
205
225
|
|
|
206
226
|
def enrich_response!(db, existing, response_message, body)
|
|
@@ -237,33 +257,45 @@ module Legion
|
|
|
237
257
|
return existing if existing
|
|
238
258
|
|
|
239
259
|
token_values = tokens(body)
|
|
240
|
-
id =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
id = insert_with_savepoint(db, :llm_message_inference_metrics, {
|
|
261
|
+
uuid: metric_uuid,
|
|
262
|
+
message_inference_request_id: request[:id],
|
|
263
|
+
message_inference_response_id: response[:id],
|
|
264
|
+
provider: provider(body),
|
|
265
|
+
model_key: model_id(body),
|
|
266
|
+
tier: tier(body),
|
|
267
|
+
input_tokens: token_values[:input_tokens],
|
|
268
|
+
output_tokens: token_values[:output_tokens],
|
|
269
|
+
thinking_tokens: token_values[:thinking_tokens],
|
|
270
|
+
total_tokens: token_values[:total_tokens],
|
|
271
|
+
latency_ms: integer(body[:latency_ms]),
|
|
272
|
+
wall_clock_ms: integer(body[:wall_clock_ms]),
|
|
273
|
+
cost_usd: cost_usd(body),
|
|
274
|
+
currency: body[:currency] || 'USD',
|
|
275
|
+
cost_center: billing(body)[:cost_center],
|
|
276
|
+
budget_key: billing(body)[:budget_id] || billing(body)[:budget_key],
|
|
277
|
+
recorded_at: recorded_at(body),
|
|
278
|
+
inserted_at: Time.now.utc
|
|
279
|
+
}, operation: 'official_record_writer.inference_metric')
|
|
260
280
|
db[:llm_message_inference_metrics][id: id]
|
|
281
|
+
rescue Sequel::UniqueConstraintViolation => e
|
|
282
|
+
log.debug("[ledger] metric collision resolved uuid=#{metric_uuid} error=#{e.class}")
|
|
283
|
+
existing = db[:llm_message_inference_metrics].where(uuid: metric_uuid).first
|
|
284
|
+
return existing if existing
|
|
285
|
+
|
|
286
|
+
raise
|
|
261
287
|
end
|
|
262
288
|
|
|
263
289
|
def insert_row(db, table, attributes, operation:)
|
|
264
290
|
Helpers::PersistenceLogging.insert_row(db, table, attributes, operation: operation)
|
|
265
291
|
end
|
|
266
292
|
|
|
293
|
+
def insert_with_savepoint(db, table, attributes, operation:)
|
|
294
|
+
db.transaction(savepoint: true) do
|
|
295
|
+
insert_row(db, table, attributes, operation: operation)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
267
299
|
def request_ref(body)
|
|
268
300
|
body[:__ledger_request_ref] ||= reference(body, :request_id, :request_ref) ||
|
|
269
301
|
correlation_id(body) ||
|
|
@@ -322,11 +354,11 @@ module Legion
|
|
|
322
354
|
refs = { principal_id: explicit_principal_id, identity_id: explicit_identity_id }.compact
|
|
323
355
|
unless refs[:principal_id] && refs[:identity_id]
|
|
324
356
|
if explicit_identity_id && !explicit_principal_id && identity_tables_available?(db)
|
|
325
|
-
row = db[:
|
|
357
|
+
row = db[:identities].where(id: explicit_identity_id).first
|
|
326
358
|
refs[:principal_id] = row[:principal_id] if row
|
|
327
359
|
end
|
|
328
360
|
|
|
329
|
-
resolved =
|
|
361
|
+
resolved = resolve_identity(db, body)
|
|
330
362
|
refs[:principal_id] ||= resolved[:principal_id]
|
|
331
363
|
refs[:identity_id] ||= resolved[:identity_id]
|
|
332
364
|
end
|
|
@@ -334,7 +366,7 @@ module Legion
|
|
|
334
366
|
end
|
|
335
367
|
end
|
|
336
368
|
|
|
337
|
-
def
|
|
369
|
+
def resolve_identity(db, body)
|
|
338
370
|
return {} unless identity_tables_available?(db)
|
|
339
371
|
|
|
340
372
|
descriptor = parsed_identity_descriptor(body)
|
|
@@ -342,7 +374,7 @@ module Legion
|
|
|
342
374
|
|
|
343
375
|
provider = find_or_create_identity_provider(db, descriptor[:provider_name])
|
|
344
376
|
principal = find_or_create_identity_principal(db, descriptor)
|
|
345
|
-
identity =
|
|
377
|
+
identity = find_or_create_identity(db, principal, provider, descriptor)
|
|
346
378
|
|
|
347
379
|
{ principal_id: principal[:id], identity_id: identity[:id] }
|
|
348
380
|
rescue StandardError => e
|
|
@@ -392,47 +424,53 @@ module Legion
|
|
|
392
424
|
end
|
|
393
425
|
|
|
394
426
|
def find_or_create_identity_provider(db, provider_name)
|
|
395
|
-
table = db[:
|
|
427
|
+
table = db[:identity_providers]
|
|
396
428
|
existing = table.where(name: provider_name).first
|
|
397
429
|
return existing if existing
|
|
398
430
|
|
|
399
|
-
id =
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
431
|
+
id = insert_with_savepoint(db, :identity_providers, {
|
|
432
|
+
uuid: deterministic_uuid("identity_provider:#{provider_name}"),
|
|
433
|
+
name: provider_name,
|
|
434
|
+
provider_type: provider_name == 'local' ? 'local' : 'external',
|
|
435
|
+
facing: 'internal',
|
|
436
|
+
source: 'ledger',
|
|
437
|
+
created_at: Time.now.utc,
|
|
438
|
+
updated_at: Time.now.utc
|
|
439
|
+
}, operation: 'official_record_writer.identity_provider')
|
|
408
440
|
table[id: id]
|
|
409
441
|
rescue Sequel::UniqueConstraintViolation => e
|
|
410
442
|
handle_exception(e, level: :debug, handled: true, operation: 'official_record_writer.identity_provider_race')
|
|
411
|
-
table.where(name: provider_name).first
|
|
443
|
+
existing = table.where(name: provider_name).first
|
|
444
|
+
return existing if existing
|
|
445
|
+
|
|
446
|
+
raise
|
|
412
447
|
end
|
|
413
448
|
|
|
414
449
|
def find_or_create_identity_principal(db, descriptor)
|
|
415
|
-
table = db[:
|
|
450
|
+
table = db[:identity_principals]
|
|
416
451
|
existing = table.where(canonical_name: descriptor[:canonical_name], kind: descriptor[:kind]).first
|
|
417
452
|
return existing if existing
|
|
418
453
|
|
|
419
|
-
id =
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
454
|
+
id = insert_with_savepoint(db, :identity_principals, {
|
|
455
|
+
uuid: deterministic_uuid("identity_principal:#{descriptor[:kind]}:#{descriptor[:canonical_name]}"),
|
|
456
|
+
canonical_name: descriptor[:canonical_name],
|
|
457
|
+
kind: descriptor[:kind],
|
|
458
|
+
display_name: descriptor[:canonical_name],
|
|
459
|
+
last_seen_at: Time.now.utc,
|
|
460
|
+
created_at: Time.now.utc,
|
|
461
|
+
updated_at: Time.now.utc
|
|
462
|
+
}, operation: 'official_record_writer.identity_principal')
|
|
428
463
|
table[id: id]
|
|
429
464
|
rescue Sequel::UniqueConstraintViolation => e
|
|
430
465
|
handle_exception(e, level: :debug, handled: true, operation: 'official_record_writer.identity_principal_race')
|
|
431
|
-
table.where(canonical_name: descriptor[:canonical_name], kind: descriptor[:kind]).first
|
|
466
|
+
existing = table.where(canonical_name: descriptor[:canonical_name], kind: descriptor[:kind]).first
|
|
467
|
+
return existing if existing
|
|
468
|
+
|
|
469
|
+
raise
|
|
432
470
|
end
|
|
433
471
|
|
|
434
|
-
def
|
|
435
|
-
table = db[:
|
|
472
|
+
def find_or_create_identity(db, principal, provider, descriptor)
|
|
473
|
+
table = db[:identities]
|
|
436
474
|
existing = table.where(
|
|
437
475
|
principal_id: principal[:id],
|
|
438
476
|
provider_id: provider[:id],
|
|
@@ -440,29 +478,32 @@ module Legion
|
|
|
440
478
|
).first
|
|
441
479
|
return existing if existing
|
|
442
480
|
|
|
443
|
-
uuid_key = "
|
|
444
|
-
id =
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
481
|
+
uuid_key = "identity:#{principal[:id]}:#{provider[:id]}:#{descriptor[:provider_identity_key]}"
|
|
482
|
+
id = insert_with_savepoint(db, :identities, {
|
|
483
|
+
uuid: deterministic_uuid(uuid_key),
|
|
484
|
+
principal_id: principal[:id],
|
|
485
|
+
provider_id: provider[:id],
|
|
486
|
+
provider_identity_key: descriptor[:provider_identity_key],
|
|
487
|
+
last_authenticated_at: Time.now.utc,
|
|
488
|
+
account_type: 'primary',
|
|
489
|
+
is_default: true,
|
|
490
|
+
created_at: Time.now.utc,
|
|
491
|
+
updated_at: Time.now.utc
|
|
492
|
+
}, operation: 'official_record_writer.identity')
|
|
455
493
|
table[id: id]
|
|
456
494
|
rescue Sequel::UniqueConstraintViolation => e
|
|
457
|
-
handle_exception(e, level: :debug, handled: true, operation: 'official_record_writer.
|
|
458
|
-
table.where(principal_id: principal[:id], provider_id: provider[:id],
|
|
459
|
-
|
|
495
|
+
handle_exception(e, level: :debug, handled: true, operation: 'official_record_writer.identity_race')
|
|
496
|
+
existing = table.where(principal_id: principal[:id], provider_id: provider[:id],
|
|
497
|
+
provider_identity_key: descriptor[:provider_identity_key]).first
|
|
498
|
+
return existing if existing
|
|
499
|
+
|
|
500
|
+
raise
|
|
460
501
|
end
|
|
461
502
|
|
|
462
503
|
def identity_tables_available?(db)
|
|
463
|
-
db.table_exists?(:
|
|
464
|
-
db.table_exists?(:
|
|
465
|
-
db.table_exists?(:
|
|
504
|
+
db.table_exists?(:identity_providers) &&
|
|
505
|
+
db.table_exists?(:identity_principals) &&
|
|
506
|
+
db.table_exists?(:identities)
|
|
466
507
|
end
|
|
467
508
|
|
|
468
509
|
def normalize_caller_type(value)
|