|
159 | 159 | ReportingRowStorageV1, |
160 | 160 | ReportingRowStoreError, |
161 | 161 | ) |
| 162 | +from adcp_reporting_storage.migration import schema_ahead_error |
162 | 163 |
|
163 | 164 | if TYPE_CHECKING: |
164 | 165 | from psycopg_pool import AsyncConnectionPool |
@@ -1028,40 +1029,46 @@ async def put_configuration(self, configuration: ReportingConfiguration) -> None |
1028 | 1029 | digest = _fingerprint(payload) |
1029 | 1030 | async with self._connection() as connection, connection.transaction(): |
1030 | 1031 | await self._lock_account(connection, key.account_id) |
1031 | | - inserted_cursor = await connection.execute( |
1032 | | - "INSERT INTO reporting_configurations" |
1033 | | - " (delivery_config_id, delivery_config_version, account_id," |
1034 | | - " report_definition_id, reporting_profile, feed_purpose, required_finality," |
1035 | | - " account_timezone, schedule, media_buy_ids, activated_at, deactivated_at," |
1036 | | - " automated_recovery_seconds, status_retention_days, definition," |
1037 | | - " authoritative_party, content_sha256, consumer_id, quarantined)" |
1038 | | - " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb, %s::jsonb, %s, %s, %s, %s," |
1039 | | - " %s::jsonb, %s, %s, %s, %s)" |
1040 | | - " ON CONFLICT (account_id, consumer_id, delivery_config_id, delive" |
1041 | | - "ry_config_version) DO NOTHING" |
1042 | | - " RETURNING delivery_config_id", |
1043 | | - ( |
1044 | | - key.delivery_config_id, |
1045 | | - key.delivery_config_version, |
1046 | | - key.account_id, |
1047 | | - configuration.report_definition_id, |
1048 | | - configuration.reporting_profile, |
1049 | | - configuration.feed_purpose, |
1050 | | - configuration.required_finality, |
1051 | | - configuration.account_timezone, |
1052 | | - _json(payload["schedule"]), |
1053 | | - _json(sorted(configuration.media_buy_ids)), |
1054 | | - configuration.activated_at, |
1055 | | - configuration.deactivated_at, |
1056 | | - configuration.automated_recovery_window.total_seconds(), |
1057 | | - configuration.status_retention_days, |
1058 | | - _json(payload["definition"]) if payload["definition"] else None, |
1059 | | - configuration.authoritative_party, |
1060 | | - digest, |
1061 | | - configuration.consumer_id, |
1062 | | - configuration.quarantined, |
1063 | | - ), |
1064 | | - ) |
| 1032 | + try: |
| 1033 | + inserted_cursor = await connection.execute( |
| 1034 | + "INSERT INTO reporting_configurations" |
| 1035 | + " (delivery_config_id, delivery_config_version, account_id," |
| 1036 | + " report_definition_id, reporting_profile, feed_purpose, required_finality," |
| 1037 | + " account_timezone, schedule, media_buy_ids, activated_at, deactivated_at," |
| 1038 | + " automated_recovery_seconds, status_retention_days, definition," |
| 1039 | + " authoritative_party, content_sha256, consumer_id, quarantined)" |
| 1040 | + " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb, %s::jsonb, %s, %s, %s, %s," |
| 1041 | + " %s::jsonb, %s, %s, %s, %s)" |
| 1042 | + " ON CONFLICT (account_id, consumer_id, delivery_config_id, delive" |
| 1043 | + "ry_config_version) DO NOTHING" |
| 1044 | + " RETURNING delivery_config_id", |
| 1045 | + ( |
| 1046 | + key.delivery_config_id, |
| 1047 | + key.delivery_config_version, |
| 1048 | + key.account_id, |
| 1049 | + configuration.report_definition_id, |
| 1050 | + configuration.reporting_profile, |
| 1051 | + configuration.feed_purpose, |
| 1052 | + configuration.required_finality, |
| 1053 | + configuration.account_timezone, |
| 1054 | + _json(payload["schedule"]), |
| 1055 | + _json(sorted(configuration.media_buy_ids)), |
| 1056 | + configuration.activated_at, |
| 1057 | + configuration.deactivated_at, |
| 1058 | + configuration.automated_recovery_window.total_seconds(), |
| 1059 | + configuration.status_retention_days, |
| 1060 | + _json(payload["definition"]) if payload["definition"] else None, |
| 1061 | + configuration.authoritative_party, |
| 1062 | + digest, |
| 1063 | + configuration.consumer_id, |
| 1064 | + configuration.quarantined, |
| 1065 | + ), |
| 1066 | + ) |
| 1067 | + except Exception as error: |
| 1068 | + ahead = schema_ahead_error(error) |
| 1069 | + if ahead is not None: |
| 1070 | + raise ahead from error |
| 1071 | + raise |
1065 | 1072 | inserted = await inserted_cursor.fetchone() |
1066 | 1073 | # Check *after* the insert. ON CONFLICT waits for a concurrent |
1067 | 1074 | # winner; a fresh READ COMMITTED statement sees its retained |
@@ -3493,6 +3500,9 @@ def _translate_integrity_error(error: Exception) -> LedgerConflictError: |
3493 | 3500 | Matching the index name rather than the driver's prose: the message text |
3494 | 3501 | varies by Postgres version and locale, the index name does not. |
3495 | 3502 | """ |
| 3503 | + ahead = schema_ahead_error(error) |
| 3504 | + if ahead is not None: |
| 3505 | + return LedgerConflictError("REPORTING_SCHEMA_AHEAD", str(ahead)) |
3496 | 3506 | text = str(error) |
3497 | 3507 | if "reporting_revisions_one_official" in text: |
3498 | 3508 | return LedgerConflictError( |
|
0 commit comments