From 513c73bc5060fa5489c199c1bdb0e8ccd824dd2d Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Thu, 4 Jun 2026 12:47:39 -0400 Subject: [PATCH 1/4] fix: confine apalis's objects to the apalis schema (#86) Move the objects apalis creates out of `public` - generate_ulid() becomes apalis.generate_ulid() and no longer depends on pgcrypto -- its 10 random bytes come from core gen_random_uuid() instead of pgcrypto's gen_random_bytes(). A new forward migration creates it, repoints the legacy (driver-unused) apalis.push_job at it, and drops public.generate_ulid. - The sqlx migrations table moves from public._sqlx_migrations to apalis._sqlx_migrations via a new sqlx.toml (requires sqlx 0.9). This also isolates apalis's migration history from a user's own sqlx migrations, which previously collided over the shared default table name. - pgcrypto is no longer used; it is left where an earlier version installed it and documented as droppable. The upgrade is automatic and non-breaking. PostgresStorage::setup() relocates an existing apalis-owned public._sqlx_migrations into the apalis schema and re-stamps checksums (read from the embedded migrator) before running migrations, so existing deployments migrate with no manual steps and nothing is re-run. The relocation is guarded so it never adopts a user's own public._sqlx_migrations sharing the default name. The only edited migration is the first one (CREATE SCHEMA -> CREATE SCHEMA IF NOT EXISTS, so create-schemas can pre-create the schema on fresh installs); its checksum is healed by the same transition. Also bumps sqlx 0.8 -> 0.9 (remapping the runtime/TLS cargo features that 0.9 split apart) and updates deny.toml / cargo-vet for the new dep tree. --- ...961105f4df7c023b50b0da3acae8881d4d010.json | 112 ++- ...c076581cba29b6513d45172cf23ad2a234479.json | 12 +- ...0518adf4d213b30c7f0a0848dc54e9e3f6852.json | 24 +- ...7434375c562b9a88ed32895ec39793459268b.json | 48 +- ...d9065d452490dc7dde0d6f0590838f386f39c.json | 112 ++- ...80b62a46384d8c75433cbcee1746e26934f14.json | 48 +- ...8864a459891ff9b5447882bcd6a43856b91f4.json | 112 ++- ...6b4a361a6cc85c26c56ea82cdd2c37a123dac.json | 112 ++- ...66f69d94231d3564da84daeeac720eed22570.json | 12 +- ...321f8e515bf2b0d1ae1c1d8ca8067bd3bfbe3.json | 12 +- ...6d733992fd5b98269ad25c25dc9e9d7a1db0a.json | 42 +- ...61500f5e81da8d38ffd1e03db0148eb889c78.json | 112 ++- CHANGELOG.md | 7 + Cargo.lock | 820 ++++++------------ Cargo.toml | 12 +- deny.toml | 6 + migrations/20220530084123_jobs_workers.sql | 2 +- ...000000_confine_generate_ulid_to_apalis.sql | 125 +++ sqlx.toml | 4 + src/lib.rs | 59 ++ supply-chain/config.toml | 288 ++---- 21 files changed, 1197 insertions(+), 884 deletions(-) create mode 100644 migrations/20260605000000_confine_generate_ulid_to_apalis.sql create mode 100644 sqlx.toml diff --git a/.sqlx/query-1989cc0be6f389d4211ebd23cfa961105f4df7c023b50b0da3acae8881d4d010.json b/.sqlx/query-1989cc0be6f389d4211ebd23cfa961105f4df7c023b50b0da3acae8881d4d010.json index d3e59a1..33472b0 100644 --- a/.sqlx/query-1989cc0be6f389d4211ebd23cfa961105f4df7c023b50b0da3acae8881d4d010.json +++ b/.sqlx/query-1989cc0be6f389d4211ebd23cfa961105f4df7c023b50b0da3acae8881d4d010.json @@ -6,72 +6,156 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job" + } + } }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job_type" + } + } }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "attempts" + } + } }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "max_attempts" + } + } }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "run_at" + } + } }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_at" + } + } }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_by" + } + } }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "done_at" + } + } }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "priority" + } + } }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "metadata" + } + } }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "idempotency_key" + } + } } ], "parameters": { diff --git a/.sqlx/query-2081bdcf70787cdab55f2df7bbbc076581cba29b6513d45172cf23ad2a234479.json b/.sqlx/query-2081bdcf70787cdab55f2df7bbbc076581cba29b6513d45172cf23ad2a234479.json index 1f294a2..84bfd8c 100644 --- a/.sqlx/query-2081bdcf70787cdab55f2df7bbbc076581cba29b6513d45172cf23ad2a234479.json +++ b/.sqlx/query-2081bdcf70787cdab55f2df7bbbc076581cba29b6513d45172cf23ad2a234479.json @@ -6,22 +6,26 @@ { "ordinal": 0, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": "Expression" }, { "ordinal": 1, "name": "type", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 2, "name": "statistic", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 3, "name": "value", - "type_info": "Float4" + "type_info": "Float4", + "origin": "Expression" } ], "parameters": { diff --git a/.sqlx/query-37cf19d29005b40bb20786f9bfc0518adf4d213b30c7f0a0848dc54e9e3f6852.json b/.sqlx/query-37cf19d29005b40bb20786f9bfc0518adf4d213b30c7f0a0848dc54e9e3f6852.json index 9e28ae7..d56571b 100644 --- a/.sqlx/query-37cf19d29005b40bb20786f9bfc0518adf4d213b30c7f0a0848dc54e9e3f6852.json +++ b/.sqlx/query-37cf19d29005b40bb20786f9bfc0518adf4d213b30c7f0a0848dc54e9e3f6852.json @@ -6,17 +6,35 @@ { "ordinal": 0, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 1, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 2, "name": "result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } } ], "parameters": { diff --git a/.sqlx/query-54c019f6453cd767b36f76e31d27434375c562b9a88ed32895ec39793459268b.json b/.sqlx/query-54c019f6453cd767b36f76e31d27434375c562b9a88ed32895ec39793459268b.json index e06f2fd..358b4da 100644 --- a/.sqlx/query-54c019f6453cd767b36f76e31d27434375c562b9a88ed32895ec39793459268b.json +++ b/.sqlx/query-54c019f6453cd767b36f76e31d27434375c562b9a88ed32895ec39793459268b.json @@ -6,32 +6,68 @@ { "ordinal": 0, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "id" + } + } }, { "ordinal": 1, "name": "worker_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "worker_type" + } + } }, { "ordinal": 2, "name": "storage_name", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "storage_name" + } + } }, { "ordinal": 3, "name": "layers", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "layers" + } + } }, { "ordinal": 4, "name": "last_seen", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "last_seen" + } + } }, { "ordinal": 5, "name": "started_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "started_at" + } + } } ], "parameters": { diff --git a/.sqlx/query-6ce4a9f7891abb2452136bef3a9d9065d452490dc7dde0d6f0590838f386f39c.json b/.sqlx/query-6ce4a9f7891abb2452136bef3a9d9065d452490dc7dde0d6f0590838f386f39c.json index 17bd9bb..679b8ee 100644 --- a/.sqlx/query-6ce4a9f7891abb2452136bef3a9d9065d452490dc7dde0d6f0590838f386f39c.json +++ b/.sqlx/query-6ce4a9f7891abb2452136bef3a9d9065d452490dc7dde0d6f0590838f386f39c.json @@ -6,72 +6,156 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job" + } + } }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job_type" + } + } }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "attempts" + } + } }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "max_attempts" + } + } }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "run_at" + } + } }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_at" + } + } }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_by" + } + } }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "done_at" + } + } }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "priority" + } + } }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "metadata" + } + } }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "idempotency_key" + } + } } ], "parameters": { diff --git a/.sqlx/query-7084948e4ad9e6ce5238a11cd3a80b62a46384d8c75433cbcee1746e26934f14.json b/.sqlx/query-7084948e4ad9e6ce5238a11cd3a80b62a46384d8c75433cbcee1746e26934f14.json index 7599759..f8b78be 100644 --- a/.sqlx/query-7084948e4ad9e6ce5238a11cd3a80b62a46384d8c75433cbcee1746e26934f14.json +++ b/.sqlx/query-7084948e4ad9e6ce5238a11cd3a80b62a46384d8c75433cbcee1746e26934f14.json @@ -6,32 +6,68 @@ { "ordinal": 0, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "id" + } + } }, { "ordinal": 1, "name": "worker_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "worker_type" + } + } }, { "ordinal": 2, "name": "storage_name", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "storage_name" + } + } }, { "ordinal": 3, "name": "layers", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "layers" + } + } }, { "ordinal": 4, "name": "last_seen", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "last_seen" + } + } }, { "ordinal": 5, "name": "started_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.workers", + "name": "started_at" + } + } } ], "parameters": { diff --git a/.sqlx/query-a0dcac1deb02eb19959aedcde6e8864a459891ff9b5447882bcd6a43856b91f4.json b/.sqlx/query-a0dcac1deb02eb19959aedcde6e8864a459891ff9b5447882bcd6a43856b91f4.json index dbbc532..65ebc0a 100644 --- a/.sqlx/query-a0dcac1deb02eb19959aedcde6e8864a459891ff9b5447882bcd6a43856b91f4.json +++ b/.sqlx/query-a0dcac1deb02eb19959aedcde6e8864a459891ff9b5447882bcd6a43856b91f4.json @@ -6,72 +6,156 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job" + } + } }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job_type" + } + } }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "attempts" + } + } }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "max_attempts" + } + } }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "run_at" + } + } }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_at" + } + } }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_by" + } + } }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "done_at" + } + } }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "priority" + } + } }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "metadata" + } + } }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "idempotency_key" + } + } } ], "parameters": { diff --git a/.sqlx/query-aec15451aa407010d95e6014b0e6b4a361a6cc85c26c56ea82cdd2c37a123dac.json b/.sqlx/query-aec15451aa407010d95e6014b0e6b4a361a6cc85c26c56ea82cdd2c37a123dac.json index f2e478f..cd6d983 100644 --- a/.sqlx/query-aec15451aa407010d95e6014b0e6b4a361a6cc85c26c56ea82cdd2c37a123dac.json +++ b/.sqlx/query-aec15451aa407010d95e6014b0e6b4a361a6cc85c26c56ea82cdd2c37a123dac.json @@ -6,72 +6,156 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job" + } + } }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job_type" + } + } }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "attempts" + } + } }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "max_attempts" + } + } }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "run_at" + } + } }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_at" + } + } }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_by" + } + } }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "done_at" + } + } }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "priority" + } + } }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "metadata" + } + } }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "idempotency_key" + } + } } ], "parameters": { diff --git a/.sqlx/query-fa9eb268af4a8630fca6604989866f69d94231d3564da84daeeac720eed22570.json b/.sqlx/query-fa9eb268af4a8630fca6604989866f69d94231d3564da84daeeac720eed22570.json index 3ea4892..d39f607 100644 --- a/.sqlx/query-fa9eb268af4a8630fca6604989866f69d94231d3564da84daeeac720eed22570.json +++ b/.sqlx/query-fa9eb268af4a8630fca6604989866f69d94231d3564da84daeeac720eed22570.json @@ -6,22 +6,26 @@ { "ordinal": 0, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": "Expression" }, { "ordinal": 1, "name": "type", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 2, "name": "statistic", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 3, "name": "value", - "type_info": "Float4" + "type_info": "Float4", + "origin": "Expression" } ], "parameters": { diff --git a/.sqlx/query-fb557b3bd6802a07dd75fab3c7f321f8e515bf2b0d1ae1c1d8ca8067bd3bfbe3.json b/.sqlx/query-fb557b3bd6802a07dd75fab3c7f321f8e515bf2b0d1ae1c1d8ca8067bd3bfbe3.json index 8332fb6..726830d 100644 --- a/.sqlx/query-fb557b3bd6802a07dd75fab3c7f321f8e515bf2b0d1ae1c1d8ca8067bd3bfbe3.json +++ b/.sqlx/query-fb557b3bd6802a07dd75fab3c7f321f8e515bf2b0d1ae1c1d8ca8067bd3bfbe3.json @@ -6,22 +6,26 @@ { "ordinal": 0, "name": "name", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 1, "name": "stats", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": "Expression" }, { "ordinal": 2, "name": "workers", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": "Expression" }, { "ordinal": 3, "name": "activity", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": "Expression" } ], "parameters": { diff --git a/.sqlx/query-fc3801ddf6016402eb1ab46da4e6d733992fd5b98269ad25c25dc9e9d7a1db0a.json b/.sqlx/query-fc3801ddf6016402eb1ab46da4e6d733992fd5b98269ad25c25dc9e9d7a1db0a.json index 4d0cc06..69ee75d 100644 --- a/.sqlx/query-fc3801ddf6016402eb1ab46da4e6d733992fd5b98269ad25c25dc9e9d7a1db0a.json +++ b/.sqlx/query-fc3801ddf6016402eb1ab46da4e6d733992fd5b98269ad25c25dc9e9d7a1db0a.json @@ -6,72 +6,86 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": "Expression" }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": "Expression" }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": "Expression" }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": "Expression" }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": "Expression" }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": "Expression" }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": "Expression" }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": "Expression" }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": "Expression" }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": "Expression" } ], "parameters": { diff --git a/.sqlx/query-fe90494e81b507098f8b8704e8d61500f5e81da8d38ffd1e03db0148eb889c78.json b/.sqlx/query-fe90494e81b507098f8b8704e8d61500f5e81da8d38ffd1e03db0148eb889c78.json index e44d383..5e91f18 100644 --- a/.sqlx/query-fe90494e81b507098f8b8704e8d61500f5e81da8d38ffd1e03db0148eb889c78.json +++ b/.sqlx/query-fe90494e81b507098f8b8704e8d61500f5e81da8d38ffd1e03db0148eb889c78.json @@ -6,72 +6,156 @@ { "ordinal": 0, "name": "job", - "type_info": "Bytea" + "type_info": "Bytea", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job" + } + } }, { "ordinal": 1, "name": "id", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "id" + } + } }, { "ordinal": 2, "name": "job_type", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "job_type" + } + } }, { "ordinal": 3, "name": "status", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "status" + } + } }, { "ordinal": 4, "name": "attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "attempts" + } + } }, { "ordinal": 5, "name": "max_attempts", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "max_attempts" + } + } }, { "ordinal": 6, "name": "run_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "run_at" + } + } }, { "ordinal": 7, "name": "last_result", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "last_result" + } + } }, { "ordinal": 8, "name": "lock_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_at" + } + } }, { "ordinal": 9, "name": "lock_by", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "lock_by" + } + } }, { "ordinal": 10, "name": "done_at", - "type_info": "Timestamptz" + "type_info": "Timestamptz", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "done_at" + } + } }, { "ordinal": 11, "name": "priority", - "type_info": "Int4" + "type_info": "Int4", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "priority" + } + } }, { "ordinal": 12, "name": "metadata", - "type_info": "Jsonb" + "type_info": "Jsonb", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "metadata" + } + } }, { "ordinal": 13, "name": "idempotency_key", - "type_info": "Text" + "type_info": "Text", + "origin": { + "Table": { + "table": "apalis.jobs", + "name": "idempotency_key" + } + } } ], "parameters": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 446a67a..d67ac18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +- fix: confine apalis's objects to the `apalis` schema (#86): + - `generate_ulid` is now `apalis.generate_ulid` and no longer depends on `pgcrypto` — its random bytes come from core `gen_random_uuid()`. The sole caller (`apalis.push_job`) is repointed and the `public.generate_ulid` copy is dropped (via a new forward migration; existing migrations are not rewritten). + - The sqlx migrations table is tracked in `apalis._sqlx_migrations` instead of `public._sqlx_migrations` (configured in a new `sqlx.toml`). This also isolates apalis's migration history from a user's own sqlx migrations on the same database, which previously collided over the shared default table name. +- bump: upgrade `sqlx` 0.8 → 0.9 (required for `sqlx.toml`); remap the runtime/TLS cargo features since 0.9 removed the combined `runtime-*-tls` flags. +- **Upgrade is automatic.** `PostgresStorage::setup()` relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums on first run, so existing deployments migrate with no manual steps and nothing is re-run. (The only edited migration is the first one — `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS` — whose checksum is healed automatically.) Deployments that apply migrations only via the sqlx CLI rather than `setup()` should run `ALTER TABLE public._sqlx_migrations SET SCHEMA apalis;` once before upgrading. +- note: `pgcrypto` is no longer used by apalis but is left where an earlier version installed it (usually `public`). If nothing else needs it, you can `DROP EXTENSION pgcrypto;`. + ## [1.0.0-rc.8] - 2026-05-08 - feat: idempotency for tasks (#81) diff --git a/Cargo.lock b/Cargo.lock index b93b455..4f31bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,12 +150,23 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.4.1", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + [[package]] name = "async-global-executor" version = "2.4.1" @@ -164,33 +175,13 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.5.0", "async-executor", - "async-io 2.6.0", - "async-lock 3.4.2", + "async-io", + "async-lock", "blocking", - "futures-lite 2.6.1", + "futures-lite", "once_cell", ] -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", -] - [[package]] name = "async-io" version = "2.6.0" @@ -201,23 +192,14 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", - "polling 3.11.0", - "rustix 1.1.4", + "polling", + "rustix", "slab", "windows-sys 0.61.2", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.4.2" @@ -237,13 +219,13 @@ checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 2.6.0", - "async-lock 3.4.2", + "async-io", + "async-lock", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -288,18 +270,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.11.1" @@ -318,6 +288,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "blocking" version = "1.6.2" @@ -327,7 +306,7 @@ dependencies = [ "async-channel 2.5.0", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] @@ -365,6 +344,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.44" @@ -379,6 +369,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -388,12 +384,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "core-foundation" version = "0.10.1" @@ -419,6 +409,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -460,14 +459,21 @@ dependencies = [ ] [[package]] -name = "der" -version = "0.7.10" +name = "crypto-common" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", ] [[package]] @@ -486,10 +492,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", + "ctutils", ] [[package]] @@ -536,13 +551,12 @@ dependencies = [ [[package]] name = "etcetera" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" dependencies = [ "cfg-if", - "home", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -572,15 +586,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -601,9 +606,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flume" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" dependencies = [ "futures-core", "futures-sink", @@ -616,6 +621,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -699,28 +710,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.4.1", + "fastrand", "futures-core", "futures-io", "parking", @@ -815,6 +811,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -836,10 +833,19 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -850,11 +856,11 @@ checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "hashlink" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -863,12 +869,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -883,29 +883,29 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest", + "digest 0.11.3", ] [[package]] -name = "home" -version = "0.5.12" +name = "hybrid-array" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ - "windows-sys 0.61.2", + "typenum", ] [[package]] @@ -1053,26 +1053,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "itoa" version = "1.0.18" @@ -1100,15 +1080,6 @@ dependencies = [ "log", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - [[package]] name = "leb128fmt" version = "0.1.0" @@ -1121,24 +1092,6 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" -dependencies = [ - "bitflags 2.11.1", - "libc", - "plain", - "redox_syscall 0.7.5", -] - [[package]] name = "libsqlite3-sys" version = "0.30.1" @@ -1149,12 +1102,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1187,12 +1134,12 @@ dependencies = [ [[package]] name = "md-5" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" dependencies = [ "cfg-if", - "digest", + "digest 0.11.3", ] [[package]] @@ -1229,48 +1176,12 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.6", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1278,7 +1189,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -1293,7 +1203,7 @@ version = "0.10.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -1354,20 +1264,11 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -1426,59 +1327,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.4.1", + "fastrand", "futures-io", ] -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "pkg-config" version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.11.0" @@ -1487,9 +1345,9 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -1557,35 +1415,25 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -1598,15 +1446,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -1617,21 +1456,18 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.5.18" +name = "rand_core" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.1", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "redox_syscall" -version = "0.7.5" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -1648,50 +1484,16 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustix" -version = "0.37.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -1735,12 +1537,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "schannel" version = "0.1.29" @@ -1762,7 +1558,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.1", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -1829,26 +1625,23 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", ] [[package]] name = "sha1" -version = "0.10.6" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -1858,25 +1651,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "sha2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] [[package]] -name = "signature" -version = "2.2.0" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" @@ -1893,16 +1687,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.6.3" @@ -1922,21 +1706,11 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "sqlx" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" dependencies = [ "sqlx-core", "sqlx-macros", @@ -1947,14 +1721,16 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" dependencies = [ - "async-io 1.13.0", + "async-fs", + "async-io", "async-std", "base64", "bytes", + "cfg-if", "chrono", "crc", "crossbeam-queue", @@ -1964,33 +1740,33 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "hashlink", "indexmap", "log", "memchr", "native-tls", - "once_cell", "percent-encoding", "rustls", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "thiserror", "time", "tokio", "tokio-stream", + "toml", "tracing", "url", - "webpki-roots 0.26.11", + "webpki-roots", ] [[package]] name = "sqlx-macros" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" dependencies = [ "proc-macro2", "quote", @@ -2001,83 +1777,68 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" dependencies = [ "async-std", + "cfg-if", "dotenvy", "either", "heck", "hex", - "once_cell", "proc-macro2", "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "syn", + "thiserror", "tokio", "url", ] [[package]] name = "sqlx-mysql" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" dependencies = [ - "atoi", - "base64", - "bitflags 2.11.1", + "bitflags", "byteorder", "bytes", "chrono", "crc", - "digest", + "digest 0.11.3", "dotenvy", "either", - "futures-channel", "futures-core", - "futures-io", "futures-util", "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", "log", - "md-5", - "memchr", - "once_cell", "percent-encoding", - "rand 0.8.6", - "rsa", "serde", "sha1", - "sha2", - "smallvec", + "sha2 0.11.0", "sqlx-core", - "stringprep", "thiserror", "time", "tracing", - "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" dependencies = [ "atoi", "base64", - "bitflags 2.11.1", + "bitflags", "byteorder", "chrono", "crc", @@ -2089,16 +1850,14 @@ dependencies = [ "hex", "hkdf", "hmac", - "home", "itoa", "log", "md-5", "memchr", - "once_cell", - "rand 0.8.6", + "rand 0.10.1", "serde", "serde_json", - "sha2", + "sha2 0.11.0", "smallvec", "sqlx-core", "stringprep", @@ -2110,13 +1869,14 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" dependencies = [ "atoi", "chrono", "flume", + "form_urlencoded", "futures-channel", "futures-core", "futures-executor", @@ -2126,7 +1886,6 @@ dependencies = [ "log", "percent-encoding", "serde", - "serde_urlencoded", "sqlx-core", "thiserror", "time", @@ -2191,10 +1950,10 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.4.2", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -2284,7 +2043,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", - "socket2 0.6.3", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -2324,6 +2083,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.3" @@ -2477,12 +2277,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -2507,12 +2301,6 @@ dependencies = [ "wit-bindgen 0.51.0", ] -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - [[package]] name = "wasm-bindgen" version = "0.2.121" @@ -2596,7 +2384,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", + "bitflags", "hashbrown 0.15.5", "indexmap", "semver", @@ -2612,15 +2400,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.7", -] - [[package]] name = "webpki-roots" version = "1.0.7" @@ -2632,35 +2411,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" -dependencies = [ - "libredox", - "wasite", -] - -[[package]] -name = "winapi" -version = "0.3.9" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" [[package]] name = "windows-core" @@ -2721,22 +2474,13 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2748,67 +2492,34 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2821,36 +2532,18 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -2859,15 +2552,18 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "winnow" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -2933,7 +2629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags", "indexmap", "log", "serde", diff --git a/Cargo.toml b/Cargo.toml index d145cfb..9d9a668 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,10 @@ publish = true [features] default = ["migrate", "tokio-comp", "chrono"] migrate = ["sqlx/migrate", "sqlx/macros"] -async-std-comp = ["async-std", "sqlx/runtime-async-std-rustls"] -async-std-comp-native-tls = ["async-std", "sqlx/runtime-async-std-native-tls"] -tokio-comp = ["tokio", "sqlx/runtime-tokio-rustls"] -tokio-comp-native-tls = ["tokio", "sqlx/runtime-tokio-native-tls"] +async-std-comp = ["async-std", "sqlx/runtime-async-std", "sqlx/tls-rustls"] +async-std-comp-native-tls = ["async-std", "sqlx/runtime-async-std", "sqlx/tls-native-tls"] +tokio-comp = ["tokio", "sqlx/runtime-tokio", "sqlx/tls-rustls"] +tokio-comp-native-tls = ["tokio", "sqlx/runtime-tokio", "sqlx/tls-native-tls"] chrono = ["apalis-sql/chrono", "sqlx/chrono"] time = ["apalis-sql/time", "sqlx/time"] @@ -43,9 +43,9 @@ ulid = { version = "1", features = ["serde"] } [dependencies.sqlx] -version = "0.8.1" +version = "0.9" default-features = false -features = ["postgres", "json"] +features = ["postgres", "json", "sqlx-toml"] [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } diff --git a/deny.toml b/deny.toml index 132b797..75e1cc7 100644 --- a/deny.toml +++ b/deny.toml @@ -38,6 +38,12 @@ skip-tree = [ { name = "rand_chacha" }, { name = "rand_core" }, { name = "redox_syscall" }, + # sqlx 0.9 pulls two incompatible digest stacks internally: sqlx-core uses + # digest 0.10 / sha2 0.10, while sqlx-postgres (hmac/md-5/hkdf) uses 0.11. + { name = "block-buffer" }, + { name = "crypto-common" }, + { name = "digest" }, + { name = "sha2" }, ] [sources] diff --git a/migrations/20220530084123_jobs_workers.sql b/migrations/20220530084123_jobs_workers.sql index 133a89b..c78e164 100644 --- a/migrations/20220530084123_jobs_workers.sql +++ b/migrations/20220530084123_jobs_workers.sql @@ -1,4 +1,4 @@ - CREATE SCHEMA apalis; + CREATE SCHEMA IF NOT EXISTS apalis; CREATE TABLE IF NOT EXISTS apalis.workers ( id TEXT NOT NULL, diff --git a/migrations/20260605000000_confine_generate_ulid_to_apalis.sql b/migrations/20260605000000_confine_generate_ulid_to_apalis.sql new file mode 100644 index 0000000..a5c4134 --- /dev/null +++ b/migrations/20260605000000_confine_generate_ulid_to_apalis.sql @@ -0,0 +1,125 @@ +-- Confine generate_ulid() to the apalis schema and drop its pgcrypto dependency. +-- +-- The ULID's 10 random bytes now come from core gen_random_uuid() (pg_catalog, +-- available since PostgreSQL 13) instead of pgcrypto's gen_random_bytes(). This +-- means the function no longer needs the pgcrypto extension or a pinned +-- search_path, and nothing apalis creates from here on touches `public`. +-- +-- pgcrypto is intentionally left untouched: apalis no longer uses it, but an +-- earlier apalis version installed it in `public` and it may be relied upon +-- elsewhere. If nothing else in your database needs it you can remove it with +-- `DROP EXTENSION pgcrypto;`. + +CREATE OR REPLACE FUNCTION apalis.generate_ulid() +RETURNS TEXT +AS $$ +DECLARE + -- Crockford's Base32 + encoding BYTEA = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; + timestamp BYTEA = E'\\000\\000\\000\\000\\000\\000'; + output TEXT = ''; + + unix_time BIGINT; + ulid BYTEA; + rand BYTEA; +BEGIN + -- 6 timestamp bytes + unix_time = (EXTRACT(EPOCH FROM CLOCK_TIMESTAMP()) * 1000)::BIGINT; + timestamp = SET_BYTE(timestamp, 0, (unix_time >> 40)::BIT(8)::INTEGER); + timestamp = SET_BYTE(timestamp, 1, (unix_time >> 32)::BIT(8)::INTEGER); + timestamp = SET_BYTE(timestamp, 2, (unix_time >> 24)::BIT(8)::INTEGER); + timestamp = SET_BYTE(timestamp, 3, (unix_time >> 16)::BIT(8)::INTEGER); + timestamp = SET_BYTE(timestamp, 4, (unix_time >> 8)::BIT(8)::INTEGER); + timestamp = SET_BYTE(timestamp, 5, unix_time::BIT(8)::INTEGER); + + -- 10 entropy bytes, taken from a core gen_random_uuid(). Bytes 1-6 and 11-14 + -- of the UUID are fully random (we skip the version/variant bytes at offsets + -- 7 and 9), preserving the full 80 bits of ULID entropy. + rand = uuid_send(gen_random_uuid()); + ulid = timestamp || substr(rand, 1, 6) || substr(rand, 11, 4); + + -- Encode the timestamp + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 224) >> 5)); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 0) & 31))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 1) & 248) >> 3)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 1) & 7) << 2) | ((GET_BYTE(ulid, 2) & 192) >> 6))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 2) & 62) >> 1)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 2) & 1) << 4) | ((GET_BYTE(ulid, 3) & 240) >> 4))); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 3) & 15) << 1) | ((GET_BYTE(ulid, 4) & 128) >> 7))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 4) & 124) >> 2)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 4) & 3) << 3) | ((GET_BYTE(ulid, 5) & 224) >> 5))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 5) & 31))); + + -- Encode the entropy + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 6) & 248) >> 3)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 6) & 7) << 2) | ((GET_BYTE(ulid, 7) & 192) >> 6))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 7) & 62) >> 1)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 7) & 1) << 4) | ((GET_BYTE(ulid, 8) & 240) >> 4))); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 8) & 15) << 1) | ((GET_BYTE(ulid, 9) & 128) >> 7))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 9) & 124) >> 2)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 9) & 3) << 3) | ((GET_BYTE(ulid, 10) & 224) >> 5))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 10) & 31))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 11) & 248) >> 3)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 11) & 7) << 2) | ((GET_BYTE(ulid, 12) & 192) >> 6))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 12) & 62) >> 1)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 12) & 1) << 4) | ((GET_BYTE(ulid, 13) & 240) >> 4))); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 13) & 15) << 1) | ((GET_BYTE(ulid, 14) & 128) >> 7))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 14) & 124) >> 2)); + output = output || CHR(GET_BYTE(encoding, ((GET_BYTE(ulid, 14) & 3) << 3) | ((GET_BYTE(ulid, 15) & 224) >> 5))); + output = output || CHR(GET_BYTE(encoding, (GET_BYTE(ulid, 15) & 31))); + + RETURN output; +END +$$ +LANGUAGE plpgsql +VOLATILE; + +-- Repoint the (legacy) push_job helper at the confined function. push_job is not +-- used by the driver itself (tasks are inserted via queries/task/sink.sql); this +-- only updates its generate_ulid() reference and otherwise leaves it unchanged. +CREATE OR REPLACE FUNCTION apalis.push_job( + job_type text, + job json DEFAULT NULL :: json, + status text DEFAULT 'Pending' :: text, + run_at timestamptz DEFAULT NOW() :: timestamptz, + max_attempts integer DEFAULT 25 :: integer, + priority integer DEFAULT 0 :: integer +) RETURNS apalis.jobs AS $$ + + DECLARE + v_job_row apalis.jobs; + v_job_id text; + + BEGIN + IF job_type is not NULL and length(job_type) > 512 THEN raise exception 'Job_type is too long (max length: 512).' USING errcode = 'APAJT'; + END IF; + + IF max_attempts < 1 THEN raise exception 'Job maximum attempts must be at least 1.' USING errcode = 'APAMA'; + end IF; + + SELECT + apalis.generate_ulid() INTO v_job_id; + INSERT INTO + apalis.jobs + VALUES + ( + job, + v_job_id, + job_type, + status, + 0, + max_attempts, + run_at, + NULL, + NULL, + NULL, + NULL, + priority + ) + returning * INTO v_job_row; + RETURN v_job_row; +END; +$$ LANGUAGE plpgsql volatile; + +-- Remove the public copy now that nothing references it. +DROP FUNCTION IF EXISTS public.generate_ulid(); diff --git a/sqlx.toml b/sqlx.toml new file mode 100644 index 0000000..5554716 --- /dev/null +++ b/sqlx.toml @@ -0,0 +1,4 @@ +[migrate] +create-schemas = ["apalis"] +table-name = "apalis._sqlx_migrations" + diff --git a/src/lib.rs b/src/lib.rs index 5855a4c..e42af4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,10 +114,69 @@ impl PostgresStorage<(), (), ()> { /// Perform migrations for storage #[cfg(feature = "migrate")] pub async fn setup(pool: &PgPool) -> Result<(), sqlx::Error> { + Self::relocate_legacy_migrations_table(pool).await?; Self::migrations().run(pool).await?; Ok(()) } + /// One-time transition for deployments created before the migrations table + /// was confined to the `apalis` schema. + /// + /// Up to `1.0.0-rc.8`, sqlx tracked applied migrations in the default + /// `public._sqlx_migrations`; the migrations table now lives in + /// `apalis._sqlx_migrations` (configured in `sqlx.toml`). If we find an + /// apalis-owned `public._sqlx_migrations` and no `apalis._sqlx_migrations`, + /// we move it into `apalis` and re-stamp the checksum of any migration whose + /// content changed in `1.0` (only the first migration did, gaining + /// `IF NOT EXISTS`), so the embedded migrator recognizes the existing history + /// instead of re-running every migration on a populated database. + /// + /// The move is guarded so it never adopts a `public._sqlx_migrations` that + /// belongs to a user's own sqlx migrations sharing the default name. + #[cfg(feature = "migrate")] + async fn relocate_legacy_migrations_table(pool: &PgPool) -> Result<(), sqlx::Error> { + let needs_relocation: bool = sqlx::query_scalar( + "SELECT to_regclass('public._sqlx_migrations') IS NOT NULL \ + AND to_regclass('apalis._sqlx_migrations') IS NULL", + ) + .fetch_one(pool) + .await?; + if !needs_relocation { + return Ok(()); + } + + // Only adopt a table that is actually apalis's: it must contain apalis's + // very first migration. A user's own `public._sqlx_migrations` won't. + let is_apalis_table: bool = sqlx::query_scalar( + "SELECT EXISTS (SELECT 1 FROM public._sqlx_migrations WHERE version = 20220530084123)", + ) + .fetch_one(pool) + .await?; + if !is_apalis_table { + return Ok(()); + } + + sqlx::query("CREATE SCHEMA IF NOT EXISTS apalis") + .execute(pool) + .await?; + sqlx::query("ALTER TABLE public._sqlx_migrations SET SCHEMA apalis") + .execute(pool) + .await?; + // Re-stamp checksums to the embedded migrator's values so edited + // migrations are recognized as already applied (no-op for unchanged ones). + for migration in Self::migrations().iter() { + sqlx::query( + "UPDATE apalis._sqlx_migrations SET checksum = $1 \ + WHERE version = $2 AND checksum <> $1", + ) + .bind(migration.checksum.as_ref()) + .bind(migration.version) + .execute(pool) + .await?; + } + Ok(()) + } + /// Get postgres migrations without running them #[cfg(feature = "migrate")] pub fn migrations() -> sqlx::migrate::Migrator { diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 0cf7513..cdc73c9 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -55,22 +55,18 @@ criteria = "safe-to-deploy" version = "1.14.0" criteria = "safe-to-deploy" -[[exemptions.async-global-executor]] -version = "2.4.1" +[[exemptions.async-fs]] +version = "2.2.0" criteria = "safe-to-deploy" -[[exemptions.async-io]] -version = "1.13.0" +[[exemptions.async-global-executor]] +version = "2.4.1" criteria = "safe-to-deploy" [[exemptions.async-io]] version = "2.6.0" criteria = "safe-to-deploy" -[[exemptions.async-lock]] -version = "2.8.0" -criteria = "safe-to-deploy" - [[exemptions.async-lock]] version = "3.4.2" criteria = "safe-to-deploy" @@ -99,14 +95,6 @@ criteria = "safe-to-deploy" version = "0.22.1" criteria = "safe-to-deploy" -[[exemptions.base64ct]] -version = "1.8.3" -criteria = "safe-to-deploy" - -[[exemptions.bitflags]] -version = "1.3.2" -criteria = "safe-to-deploy" - [[exemptions.bitflags]] version = "2.11.1" criteria = "safe-to-deploy" @@ -115,6 +103,10 @@ criteria = "safe-to-deploy" version = "0.10.4" criteria = "safe-to-deploy" +[[exemptions.block-buffer]] +version = "0.12.0" +criteria = "safe-to-deploy" + [[exemptions.blocking]] version = "1.6.2" criteria = "safe-to-deploy" @@ -139,16 +131,20 @@ criteria = "safe-to-deploy" version = "1.0.4" criteria = "safe-to-deploy" +[[exemptions.chacha20]] +version = "0.10.0" +criteria = "safe-to-deploy" + [[exemptions.chrono]] version = "0.4.44" criteria = "safe-to-deploy" -[[exemptions.concurrent-queue]] -version = "2.5.0" +[[exemptions.cmov]] +version = "0.5.4" criteria = "safe-to-deploy" -[[exemptions.const-oid]] -version = "0.9.6" +[[exemptions.concurrent-queue]] +version = "2.5.0" criteria = "safe-to-deploy" [[exemptions.core-foundation]] @@ -163,6 +159,10 @@ criteria = "safe-to-deploy" version = "0.2.17" criteria = "safe-to-deploy" +[[exemptions.cpufeatures]] +version = "0.3.0" +criteria = "safe-to-deploy" + [[exemptions.crc]] version = "3.4.0" criteria = "safe-to-deploy" @@ -183,8 +183,12 @@ criteria = "safe-to-deploy" version = "0.1.7" criteria = "safe-to-deploy" -[[exemptions.der]] -version = "0.7.10" +[[exemptions.crypto-common]] +version = "0.2.2" +criteria = "safe-to-deploy" + +[[exemptions.ctutils]] +version = "0.4.2" criteria = "safe-to-deploy" [[exemptions.deranged]] @@ -195,6 +199,10 @@ criteria = "safe-to-deploy" version = "0.10.7" criteria = "safe-to-deploy" +[[exemptions.digest]] +version = "0.11.3" +criteria = "safe-to-deploy" + [[exemptions.displaydoc]] version = "0.2.5" criteria = "safe-to-deploy" @@ -216,7 +224,7 @@ version = "0.3.14" criteria = "safe-to-deploy" [[exemptions.etcetera]] -version = "0.8.0" +version = "0.11.0" criteria = "safe-to-deploy" [[exemptions.event-listener]] @@ -231,10 +239,6 @@ criteria = "safe-to-deploy" version = "0.5.4" criteria = "safe-to-deploy" -[[exemptions.fastrand]] -version = "1.9.0" -criteria = "safe-to-deploy" - [[exemptions.fastrand]] version = "2.4.1" criteria = "safe-to-deploy" @@ -248,13 +252,17 @@ version = "0.5.7" criteria = "safe-to-run" [[exemptions.flume]] -version = "0.11.1" +version = "0.12.0" criteria = "safe-to-deploy" [[exemptions.foldhash]] version = "0.1.5" criteria = "safe-to-deploy" +[[exemptions.foldhash]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.foreign-types]] version = "0.3.2" criteria = "safe-to-deploy" @@ -291,10 +299,6 @@ criteria = "safe-to-deploy" version = "0.3.32" criteria = "safe-to-deploy" -[[exemptions.futures-lite]] -version = "1.13.0" -criteria = "safe-to-deploy" - [[exemptions.futures-lite]] version = "2.6.1" criteria = "safe-to-deploy" @@ -343,22 +347,22 @@ criteria = "safe-to-deploy" version = "0.15.5" criteria = "safe-to-deploy" +[[exemptions.hashbrown]] +version = "0.16.1" +criteria = "safe-to-deploy" + [[exemptions.hashbrown]] version = "0.17.0" criteria = "safe-to-deploy" [[exemptions.hashlink]] -version = "0.10.0" +version = "0.11.0" criteria = "safe-to-deploy" [[exemptions.heck]] version = "0.5.0" criteria = "safe-to-deploy" -[[exemptions.hermit-abi]] -version = "0.3.9" -criteria = "safe-to-deploy" - [[exemptions.hermit-abi]] version = "0.5.2" criteria = "safe-to-deploy" @@ -368,15 +372,15 @@ version = "0.4.3" criteria = "safe-to-deploy" [[exemptions.hkdf]] -version = "0.12.4" +version = "0.13.0" criteria = "safe-to-deploy" [[exemptions.hmac]] -version = "0.12.1" +version = "0.13.0" criteria = "safe-to-deploy" -[[exemptions.home]] -version = "0.5.12" +[[exemptions.hybrid-array]] +version = "0.4.12" criteria = "safe-to-deploy" [[exemptions.iana-time-zone]] @@ -431,14 +435,6 @@ criteria = "safe-to-deploy" version = "2.14.0" criteria = "safe-to-deploy" -[[exemptions.instant]] -version = "0.1.13" -criteria = "safe-to-deploy" - -[[exemptions.io-lifetimes]] -version = "1.0.11" -criteria = "safe-to-deploy" - [[exemptions.itoa]] version = "1.0.18" criteria = "safe-to-deploy" @@ -451,10 +447,6 @@ criteria = "safe-to-deploy" version = "1.0.7" criteria = "safe-to-deploy" -[[exemptions.lazy_static]] -version = "1.5.0" -criteria = "safe-to-deploy" - [[exemptions.leb128fmt]] version = "0.1.0" criteria = "safe-to-deploy" @@ -463,22 +455,10 @@ criteria = "safe-to-deploy" version = "0.2.186" criteria = "safe-to-deploy" -[[exemptions.libm]] -version = "0.2.16" -criteria = "safe-to-deploy" - -[[exemptions.libredox]] -version = "0.1.16" -criteria = "safe-to-deploy" - [[exemptions.libsqlite3-sys]] version = "0.30.1" criteria = "safe-to-deploy" -[[exemptions.linux-raw-sys]] -version = "0.3.8" -criteria = "safe-to-deploy" - [[exemptions.linux-raw-sys]] version = "0.12.1" criteria = "safe-to-deploy" @@ -496,7 +476,7 @@ version = "0.4.29" criteria = "safe-to-deploy" [[exemptions.md-5]] -version = "0.10.6" +version = "0.11.0" criteria = "safe-to-deploy" [[exemptions.memchr]] @@ -511,22 +491,10 @@ criteria = "safe-to-deploy" version = "0.2.18" criteria = "safe-to-deploy" -[[exemptions.num-bigint-dig]] -version = "0.8.6" -criteria = "safe-to-deploy" - [[exemptions.num-conv]] version = "0.2.1" criteria = "safe-to-deploy" -[[exemptions.num-integer]] -version = "0.1.46" -criteria = "safe-to-deploy" - -[[exemptions.num-iter]] -version = "0.1.45" -criteria = "safe-to-deploy" - [[exemptions.num-traits]] version = "0.2.19" criteria = "safe-to-deploy" @@ -563,10 +531,6 @@ criteria = "safe-to-deploy" version = "0.9.12" criteria = "safe-to-deploy" -[[exemptions.pem-rfc7468]] -version = "0.7.0" -criteria = "safe-to-deploy" - [[exemptions.percent-encoding]] version = "2.3.2" criteria = "safe-to-deploy" @@ -595,26 +559,10 @@ criteria = "safe-to-deploy" version = "0.2.5" criteria = "safe-to-deploy" -[[exemptions.pkcs1]] -version = "0.7.5" -criteria = "safe-to-deploy" - -[[exemptions.pkcs8]] -version = "0.10.2" -criteria = "safe-to-deploy" - [[exemptions.pkg-config]] version = "0.3.33" criteria = "safe-to-deploy" -[[exemptions.plain]] -version = "0.2.3" -criteria = "safe-to-deploy" - -[[exemptions.polling]] -version = "2.8.0" -criteria = "safe-to-deploy" - [[exemptions.polling]] version = "3.11.0" criteria = "safe-to-deploy" @@ -651,16 +599,12 @@ criteria = "safe-to-deploy" version = "6.0.0" criteria = "safe-to-deploy" -[[exemptions.rand]] -version = "0.8.6" -criteria = "safe-to-deploy" - [[exemptions.rand]] version = "0.9.4" criteria = "safe-to-deploy" -[[exemptions.rand_chacha]] -version = "0.3.1" +[[exemptions.rand]] +version = "0.10.1" criteria = "safe-to-deploy" [[exemptions.rand_chacha]] @@ -668,33 +612,21 @@ version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.rand_core]] -version = "0.6.4" +version = "0.9.5" criteria = "safe-to-deploy" [[exemptions.rand_core]] -version = "0.9.5" +version = "0.10.1" criteria = "safe-to-deploy" [[exemptions.redox_syscall]] version = "0.5.18" criteria = "safe-to-deploy" -[[exemptions.redox_syscall]] -version = "0.7.5" -criteria = "safe-to-deploy" - [[exemptions.ring]] version = "0.17.14" criteria = "safe-to-deploy" -[[exemptions.rsa]] -version = "0.9.10" -criteria = "safe-to-deploy" - -[[exemptions.rustix]] -version = "0.37.28" -criteria = "safe-to-deploy" - [[exemptions.rustix]] version = "1.1.4" criteria = "safe-to-deploy" @@ -715,10 +647,6 @@ criteria = "safe-to-deploy" version = "1.0.22" criteria = "safe-to-deploy" -[[exemptions.ryu]] -version = "1.0.23" -criteria = "safe-to-deploy" - [[exemptions.schannel]] version = "0.1.29" criteria = "safe-to-deploy" @@ -755,24 +683,24 @@ criteria = "safe-to-deploy" version = "1.0.149" criteria = "safe-to-deploy" -[[exemptions.serde_urlencoded]] -version = "0.7.1" +[[exemptions.serde_spanned]] +version = "0.6.9" criteria = "safe-to-deploy" [[exemptions.sha1]] -version = "0.10.6" +version = "0.11.0" criteria = "safe-to-deploy" [[exemptions.sha2]] version = "0.10.9" criteria = "safe-to-deploy" -[[exemptions.shlex]] -version = "1.3.0" +[[exemptions.sha2]] +version = "0.11.0" criteria = "safe-to-deploy" -[[exemptions.signature]] -version = "2.2.0" +[[exemptions.shlex]] +version = "1.3.0" criteria = "safe-to-deploy" [[exemptions.slab]] @@ -783,10 +711,6 @@ criteria = "safe-to-deploy" version = "1.15.1" criteria = "safe-to-deploy" -[[exemptions.socket2]] -version = "0.4.10" -criteria = "safe-to-deploy" - [[exemptions.socket2]] version = "0.6.3" criteria = "safe-to-deploy" @@ -795,36 +719,32 @@ criteria = "safe-to-deploy" version = "0.9.8" criteria = "safe-to-deploy" -[[exemptions.spki]] -version = "0.7.3" -criteria = "safe-to-deploy" - [[exemptions.sqlx]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-core]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-macros]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-macros-core]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-mysql]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-postgres]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.sqlx-sqlite]] -version = "0.8.6" +version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.stable_deref_trait]] @@ -903,6 +823,22 @@ criteria = "safe-to-deploy" version = "0.7.18" criteria = "safe-to-run" +[[exemptions.toml]] +version = "0.8.23" +criteria = "safe-to-deploy" + +[[exemptions.toml_datetime]] +version = "0.6.11" +criteria = "safe-to-deploy" + +[[exemptions.toml_edit]] +version = "0.22.27" +criteria = "safe-to-deploy" + +[[exemptions.toml_write]] +version = "0.1.2" +criteria = "safe-to-deploy" + [[exemptions.tower]] version = "0.5.3" criteria = "safe-to-run" @@ -979,10 +915,6 @@ criteria = "safe-to-deploy" version = "0.9.5" criteria = "safe-to-deploy" -[[exemptions.waker-fn]] -version = "1.2.0" -criteria = "safe-to-deploy" - [[exemptions.wasi]] version = "0.11.1+wasi-snapshot-preview1" criteria = "safe-to-deploy" @@ -995,10 +927,6 @@ criteria = "safe-to-deploy" version = "0.4.0+wasi-0.3.0-rc-2026-01-06" criteria = "safe-to-deploy" -[[exemptions.wasite]] -version = "0.1.0" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen]] version = "0.2.121" criteria = "safe-to-deploy" @@ -1035,28 +963,12 @@ criteria = "safe-to-deploy" version = "1.1.0" criteria = "safe-to-deploy" -[[exemptions.webpki-roots]] -version = "0.26.11" -criteria = "safe-to-deploy" - [[exemptions.webpki-roots]] version = "1.0.7" criteria = "safe-to-deploy" [[exemptions.whoami]] -version = "1.6.1" -criteria = "safe-to-deploy" - -[[exemptions.winapi]] -version = "0.3.9" -criteria = "safe-to-deploy" - -[[exemptions.winapi-i686-pc-windows-gnu]] -version = "0.4.0" -criteria = "safe-to-deploy" - -[[exemptions.winapi-x86_64-pc-windows-gnu]] -version = "0.4.0" +version = "2.1.2" criteria = "safe-to-deploy" [[exemptions.windows-core]] @@ -1083,10 +995,6 @@ criteria = "safe-to-deploy" version = "0.5.1" criteria = "safe-to-deploy" -[[exemptions.windows-sys]] -version = "0.48.0" -criteria = "safe-to-deploy" - [[exemptions.windows-sys]] version = "0.52.0" criteria = "safe-to-deploy" @@ -1095,34 +1003,18 @@ criteria = "safe-to-deploy" version = "0.61.2" criteria = "safe-to-deploy" -[[exemptions.windows-targets]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows-targets]] version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_aarch64_gnullvm]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_aarch64_gnullvm]] version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_aarch64_msvc]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_aarch64_msvc]] version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_i686_gnu]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_i686_gnu]] version = "0.52.6" criteria = "safe-to-deploy" @@ -1131,36 +1023,24 @@ criteria = "safe-to-deploy" version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_i686_msvc]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_i686_msvc]] version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_x86_64_gnu]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_x86_64_gnu]] version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_x86_64_gnullvm]] -version = "0.48.5" -criteria = "safe-to-deploy" - [[exemptions.windows_x86_64_gnullvm]] version = "0.52.6" criteria = "safe-to-deploy" [[exemptions.windows_x86_64_msvc]] -version = "0.48.5" +version = "0.52.6" criteria = "safe-to-deploy" -[[exemptions.windows_x86_64_msvc]] -version = "0.52.6" +[[exemptions.winnow]] +version = "0.7.15" criteria = "safe-to-deploy" [[exemptions.wit-bindgen]] From 4325b08862455e719efe3189770922aa5f51c91f Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Wed, 10 Jun 2026 11:29:19 -0400 Subject: [PATCH 2/4] Update docs --- CHANGELOG.md | 3 ++- README.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67ac18..75901bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ - `generate_ulid` is now `apalis.generate_ulid` and no longer depends on `pgcrypto` — its random bytes come from core `gen_random_uuid()`. The sole caller (`apalis.push_job`) is repointed and the `public.generate_ulid` copy is dropped (via a new forward migration; existing migrations are not rewritten). - The sqlx migrations table is tracked in `apalis._sqlx_migrations` instead of `public._sqlx_migrations` (configured in a new `sqlx.toml`). This also isolates apalis's migration history from a user's own sqlx migrations on the same database, which previously collided over the shared default table name. - bump: upgrade `sqlx` 0.8 → 0.9 (required for `sqlx.toml`); remap the runtime/TLS cargo features since 0.9 removed the combined `runtime-*-tls` flags. -- **Upgrade is automatic.** `PostgresStorage::setup()` relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums on first run, so existing deployments migrate with no manual steps and nothing is re-run. (The only edited migration is the first one — `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS` — whose checksum is healed automatically.) Deployments that apply migrations only via the sqlx CLI rather than `setup()` should run `ALTER TABLE public._sqlx_migrations SET SCHEMA apalis;` once before upgrading. +- **Upgrade is automatic when you use `PostgresStorage::setup()`.** On first run it relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums before running migrations, so existing deployments migrate with no manual steps and nothing is re-run. (The only edited migration is the first one — `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS`, needed so the `apalis` schema can be created before the tracking table on fresh installs — whose checksum is healed automatically.) +- **If you apply migrations yourself** (sqlx CLI, or you copied the migration files into your own project), `setup()` doesn't run, so on an existing database run the one-time relocation + checksum re-stamp documented under "Upgrading to 1.0" in the README before upgrading. Fresh databases need no manual steps — `sqlx.toml` creates the schema and tracking table. - note: `pgcrypto` is no longer used by apalis but is left where an earlier version installed it (usually `public`). If nothing else needs it, you can `DROP EXTENSION pgcrypto;`. ## [1.0.0-rc.8] - 2026-05-08 diff --git a/README.md b/README.md index 3916f93..b39f0f4 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,43 @@ async fn main() { Track your jobs using [apalis-board](https://github.com/apalis-dev/apalis-board). ![Task](https://github.com/apalis-dev/apalis-board/raw/main/screenshots/task.png) +## Upgrading to 1.0 + +1.0 confines everything apalis creates to the `apalis` schema. Two things move out of `public`: + +- The sqlx migrations table is now tracked in `apalis._sqlx_migrations` (configured in `sqlx.toml`) instead of `public._sqlx_migrations`. This also keeps apalis's migration history from colliding with your own sqlx migrations on the same database. +- `generate_ulid()` is now `apalis.generate_ulid()` and no longer depends on the `pgcrypto` extension — its random bytes come from core `gen_random_uuid()`. The `public.generate_ulid()` copy is dropped. + +### If you use `PostgresStorage::setup()` + +Nothing to do. On the next start, `setup()` relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums before running migrations, so the upgrade is automatic and nothing is re-run. + +### If you apply migrations yourself (sqlx-cli, or you copied the migration files into your own project) + +`setup()` is what performs the relocation, so paths that bypass it need one manual, one-time step **before** running the 1.0 migrations against an existing database: + +```sql +-- Move apalis's existing migration history into the apalis schema. +ALTER TABLE public._sqlx_migrations SET SCHEMA apalis; + +-- The first migration gained `IF NOT EXISTS` (so the apalis schema can be +-- created before the tracking table on fresh installs). Re-stamp its checksum +-- so sqlx doesn't reject it as modified: +UPDATE apalis._sqlx_migrations + SET checksum = decode('d0839c6f57a379769dc27ccd581feb3d2709239c8f138e05271c9e3c760c4517a78a4d8912ab3d63b074b28d15ec74e9', 'hex') + WHERE version = 20220530084123; +``` + +Fresh databases need none of this — `sqlx.toml` creates the `apalis` schema and tracking table for you. + +### `pgcrypto` + +apalis no longer uses `pgcrypto`. An earlier version installed it (usually in `public`); it is left untouched in case something else depends on it. If nothing else needs it, you can remove it: + +```sql +DROP EXTENSION pgcrypto; +``` + ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. From fcfd12bb404cc5625498b31d80702da6a54e19bf Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Mon, 22 Jun 2026 10:44:44 -0400 Subject: [PATCH 3/4] Separate checksum mutation from rest of setup --- CHANGELOG.md | 4 ++- README.md | 12 +++++++++ src/lib.rs | 71 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75901bd..a6525e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ - The sqlx migrations table is tracked in `apalis._sqlx_migrations` instead of `public._sqlx_migrations` (configured in a new `sqlx.toml`). This also isolates apalis's migration history from a user's own sqlx migrations on the same database, which previously collided over the shared default table name. - bump: upgrade `sqlx` 0.8 → 0.9 (required for `sqlx.toml`); remap the runtime/TLS cargo features since 0.9 removed the combined `runtime-*-tls` flags. - **Upgrade is automatic when you use `PostgresStorage::setup()`.** On first run it relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums before running migrations, so existing deployments migrate with no manual steps and nothing is re-run. (The only edited migration is the first one — `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS`, needed so the `apalis` schema can be created before the tracking table on fresh installs — whose checksum is healed automatically.) -- **If you apply migrations yourself** (sqlx CLI, or you copied the migration files into your own project), `setup()` doesn't run, so on an existing database run the one-time relocation + checksum re-stamp documented under "Upgrading to 1.0" in the README before upgrading. Fresh databases need no manual steps — `sqlx.toml` creates the schema and tracking table. +- **If you apply migrations yourself**, `setup()` doesn't run, so on an existing database you perform the one-time transition before upgrading (no-op on fresh databases). The relocation and checksum re-stamp are exposed as `PostgresStorage::relocate_legacy_migrations_table` and `PostgresStorage::reconcile_migration_checksums`: + - sqlx CLI, or you copied the migration files into your own project: run the relocation + re-stamp SQL documented under "Upgrading to 1.0" in the README. + - you merge `PostgresStorage::migrations()` into your own `Migrator`: your migrator keeps its own tracking table, so don't relocate — just call `reconcile_migration_checksums(&pool, "")` before running it to heal the one edited migration's checksum. - note: `pgcrypto` is no longer used by apalis but is left where an earlier version installed it (usually `public`). If nothing else needs it, you can `DROP EXTENSION pgcrypto;`. ## [1.0.0-rc.8] - 2026-05-08 diff --git a/README.md b/README.md index b39f0f4..3648f2c 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,18 @@ UPDATE apalis._sqlx_migrations Fresh databases need none of this — `sqlx.toml` creates the `apalis` schema and tracking table for you. +### If you merge `PostgresStorage::migrations()` into your own `Migrator` + +Your migrator owns its own tracking table, so it doesn't move to `apalis` and you don't run the relocation above. You only need to heal the one edited migration's checksum so your migrator doesn't reject it as modified. On an existing database, before running your migrator: + +```rust +// `_sqlx_migrations` (or whatever table your Migrator uses) +PostgresStorage::reconcile_migration_checksums(&pool, "_sqlx_migrations").await?; +merged_migrator.run(&pool).await?; +``` + +It is a no-op on fresh databases. + ### `pgcrypto` apalis no longer uses `pgcrypto`. An earlier version installed it (usually in `public`); it is left untouched in case something else depends on it. If nothing else needs it, you can remove it: diff --git a/src/lib.rs b/src/lib.rs index e42af4a..9e14a6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,30 +111,27 @@ impl Clone } impl PostgresStorage<(), (), ()> { - /// Perform migrations for storage + /// Perform migrations for storage. + /// + /// On an existing database this also performs the one-time `1.0` migration + /// history transition before running migrations, so upgrades are automatic. + /// If you instead merge [`migrations()`](Self::migrations) into your own + /// [`Migrator`](sqlx::migrate::Migrator), you bypass `setup()` — call + /// [`reconcile_migration_checksums`](Self::reconcile_migration_checksums) + /// against that migrator's table before running it. #[cfg(feature = "migrate")] pub async fn setup(pool: &PgPool) -> Result<(), sqlx::Error> { Self::relocate_legacy_migrations_table(pool).await?; + Self::reconcile_migration_checksums(pool, "apalis._sqlx_migrations").await?; Self::migrations().run(pool).await?; Ok(()) } - /// One-time transition for deployments created before the migrations table - /// was confined to the `apalis` schema. - /// - /// Up to `1.0.0-rc.8`, sqlx tracked applied migrations in the default - /// `public._sqlx_migrations`; the migrations table now lives in - /// `apalis._sqlx_migrations` (configured in `sqlx.toml`). If we find an - /// apalis-owned `public._sqlx_migrations` and no `apalis._sqlx_migrations`, - /// we move it into `apalis` and re-stamp the checksum of any migration whose - /// content changed in `1.0` (only the first migration did, gaining - /// `IF NOT EXISTS`), so the embedded migrator recognizes the existing history - /// instead of re-running every migration on a populated database. - /// - /// The move is guarded so it never adopts a `public._sqlx_migrations` that - /// belongs to a user's own sqlx migrations sharing the default name. + /// Move apalis's pre-`1.0` migration history from `public._sqlx_migrations` + /// into `apalis._sqlx_migrations`, where `1.0` tracks it. No-op once the + /// `apalis` table exists or on fresh databases. Does not re-stamp checksums. #[cfg(feature = "migrate")] - async fn relocate_legacy_migrations_table(pool: &PgPool) -> Result<(), sqlx::Error> { + pub async fn relocate_legacy_migrations_table(pool: &PgPool) -> Result<(), sqlx::Error> { let needs_relocation: bool = sqlx::query_scalar( "SELECT to_regclass('public._sqlx_migrations') IS NOT NULL \ AND to_regclass('apalis._sqlx_migrations') IS NULL", @@ -145,8 +142,7 @@ impl PostgresStorage<(), (), ()> { return Ok(()); } - // Only adopt a table that is actually apalis's: it must contain apalis's - // very first migration. A user's own `public._sqlx_migrations` won't. + // Only adopt a table that is apalis's: a user's own won't have this row. let is_apalis_table: bool = sqlx::query_scalar( "SELECT EXISTS (SELECT 1 FROM public._sqlx_migrations WHERE version = 20220530084123)", ) @@ -162,17 +158,36 @@ impl PostgresStorage<(), (), ()> { sqlx::query("ALTER TABLE public._sqlx_migrations SET SCHEMA apalis") .execute(pool) .await?; - // Re-stamp checksums to the embedded migrator's values so edited - // migrations are recognized as already applied (no-op for unchanged ones). - for migration in Self::migrations().iter() { - sqlx::query( - "UPDATE apalis._sqlx_migrations SET checksum = $1 \ - WHERE version = $2 AND checksum <> $1", - ) - .bind(migration.checksum.as_ref()) - .bind(migration.version) - .execute(pool) + Ok(()) + } + + /// Re-stamp the checksum of any migration whose content changed in `1.0` (only + /// the first, which gained `IF NOT EXISTS`) so an existing table isn't rejected + /// as modified. `setup()` calls this; call it yourself only when merging + /// [`migrations()`](Self::migrations) into your own + /// [`Migrator`](sqlx::migrate::Migrator), passing its table (e.g. + /// `"_sqlx_migrations"`), before you run it. No-op if `table` is fresh or absent. + #[cfg(feature = "migrate")] + pub async fn reconcile_migration_checksums( + pool: &PgPool, + table: &str, + ) -> Result<(), sqlx::Error> { + let table_exists: bool = sqlx::query_scalar("SELECT to_regclass($1) IS NOT NULL") + .bind(table) + .fetch_one(pool) .await?; + if !table_exists { + return Ok(()); + } + + let update = + format!("UPDATE {table} SET checksum = $1 WHERE version = $2 AND checksum <> $1"); + for migration in Self::migrations().iter() { + sqlx::query(sqlx::AssertSqlSafe(update.clone())) + .bind(migration.checksum.as_ref()) + .bind(migration.version) + .execute(pool) + .await?; } Ok(()) } From 49511df743814598301af59765b5e7684e6e67a8 Mon Sep 17 00:00:00 2001 From: Ty Larrabee Date: Tue, 23 Jun 2026 12:41:27 -0400 Subject: [PATCH 4/4] Drop automatic migration relocation for a documented manual step Remove the auto relocation + checksum re-stamp from setup() and the public relocate_legacy_migrations_table / reconcile_migration_checksums helpers. setup() now just runs migrations. Docs now cover the two failure modes (function-already-exists on a blind upgrade, and the empty apalis._sqlx_migrations that then blocks the ALTER TABLE). --- CHANGELOG.md | 5 +--- README.md | 26 +++++++----------- src/lib.rs | 77 +++------------------------------------------------- 3 files changed, 15 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6525e3..f7ac3e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,7 @@ - `generate_ulid` is now `apalis.generate_ulid` and no longer depends on `pgcrypto` — its random bytes come from core `gen_random_uuid()`. The sole caller (`apalis.push_job`) is repointed and the `public.generate_ulid` copy is dropped (via a new forward migration; existing migrations are not rewritten). - The sqlx migrations table is tracked in `apalis._sqlx_migrations` instead of `public._sqlx_migrations` (configured in a new `sqlx.toml`). This also isolates apalis's migration history from a user's own sqlx migrations on the same database, which previously collided over the shared default table name. - bump: upgrade `sqlx` 0.8 → 0.9 (required for `sqlx.toml`); remap the runtime/TLS cargo features since 0.9 removed the combined `runtime-*-tls` flags. -- **Upgrade is automatic when you use `PostgresStorage::setup()`.** On first run it relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums before running migrations, so existing deployments migrate with no manual steps and nothing is re-run. (The only edited migration is the first one — `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS`, needed so the `apalis` schema can be created before the tracking table on fresh installs — whose checksum is healed automatically.) -- **If you apply migrations yourself**, `setup()` doesn't run, so on an existing database you perform the one-time transition before upgrading (no-op on fresh databases). The relocation and checksum re-stamp are exposed as `PostgresStorage::relocate_legacy_migrations_table` and `PostgresStorage::reconcile_migration_checksums`: - - sqlx CLI, or you copied the migration files into your own project: run the relocation + re-stamp SQL documented under "Upgrading to 1.0" in the README. - - you merge `PostgresStorage::migrations()` into your own `Migrator`: your migrator keeps its own tracking table, so don't relocate — just call `reconcile_migration_checksums(&pool, "")` before running it to heal the one edited migration's checksum. +- **Existing databases need a one-time manual step before upgrading** (fresh databases need nothing — `sqlx.toml` creates the `apalis` schema and tracking table). Move the migration history into the `apalis` schema and re-stamp the one edited migration's checksum — the first migration changed `CREATE SCHEMA` → `CREATE SCHEMA IF NOT EXISTS` so the `apalis` schema can be created before the tracking table on fresh installs. See "Upgrading to 1.0" in the README for the SQL; it applies to every apply path (`setup()`, sqlx-cli, copied migrations, or a merged `Migrator`). - note: `pgcrypto` is no longer used by apalis but is left where an earlier version installed it (usually `public`). If nothing else needs it, you can `DROP EXTENSION pgcrypto;`. ## [1.0.0-rc.8] - 2026-05-08 diff --git a/README.md b/README.md index 3648f2c..e452f8b 100644 --- a/README.md +++ b/README.md @@ -227,13 +227,9 @@ Track your jobs using [apalis-board](https://github.com/apalis-dev/apalis-board) - The sqlx migrations table is now tracked in `apalis._sqlx_migrations` (configured in `sqlx.toml`) instead of `public._sqlx_migrations`. This also keeps apalis's migration history from colliding with your own sqlx migrations on the same database. - `generate_ulid()` is now `apalis.generate_ulid()` and no longer depends on the `pgcrypto` extension — its random bytes come from core `gen_random_uuid()`. The `public.generate_ulid()` copy is dropped. -### If you use `PostgresStorage::setup()` +### Existing databases: one-time manual step -Nothing to do. On the next start, `setup()` relocates an existing `public._sqlx_migrations` into the `apalis` schema and re-stamps checksums before running migrations, so the upgrade is automatic and nothing is re-run. - -### If you apply migrations yourself (sqlx-cli, or you copied the migration files into your own project) - -`setup()` is what performs the relocation, so paths that bypass it need one manual, one-time step **before** running the 1.0 migrations against an existing database: +This applies to **every** way of applying migrations — `PostgresStorage::setup()`, sqlx-cli, copied migration files, or a merged `Migrator`. Run this **once per database, before upgrading**: ```sql -- Move apalis's existing migration history into the apalis schema. @@ -241,25 +237,23 @@ ALTER TABLE public._sqlx_migrations SET SCHEMA apalis; -- The first migration gained `IF NOT EXISTS` (so the apalis schema can be -- created before the tracking table on fresh installs). Re-stamp its checksum --- so sqlx doesn't reject it as modified: +-- so the migrator doesn't reject it as modified: UPDATE apalis._sqlx_migrations SET checksum = decode('d0839c6f57a379769dc27ccd581feb3d2709239c8f138e05271c9e3c760c4517a78a4d8912ab3d63b074b28d15ec74e9', 'hex') WHERE version = 20220530084123; ``` -Fresh databases need none of this — `sqlx.toml` creates the `apalis` schema and tracking table for you. +Run it **before** upgrading. If you upgrade first without it, the migrator re-runs the first migration against your existing objects and fails with e.g. `function "notify_new_jobs" already exists`. If you've already hit that failure, an empty `apalis._sqlx_migrations` may have been created, which makes the `ALTER TABLE` above fail because the name is taken — drop it first: -### If you merge `PostgresStorage::migrations()` into your own `Migrator` +```sql +DROP TABLE apalis._sqlx_migrations; +``` -Your migrator owns its own tracking table, so it doesn't move to `apalis` and you don't run the relocation above. You only need to heal the one edited migration's checksum so your migrator doesn't reject it as modified. On an existing database, before running your migrator: +then run the two statements above. -```rust -// `_sqlx_migrations` (or whatever table your Migrator uses) -PostgresStorage::reconcile_migration_checksums(&pool, "_sqlx_migrations").await?; -merged_migrator.run(&pool).await?; -``` +If you maintain your **own** `Migrator` (merging in `PostgresStorage::migrations()`), your tracking table stays where it is — skip the `ALTER TABLE` and run only the `UPDATE`, targeting your table name. -It is a no-op on fresh databases. +Fresh databases need none of this — `sqlx.toml` creates the `apalis` schema and tracking table for you. ### `pgcrypto` diff --git a/src/lib.rs b/src/lib.rs index 9e14a6a..5f6a722 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,85 +113,16 @@ impl Clone impl PostgresStorage<(), (), ()> { /// Perform migrations for storage. /// - /// On an existing database this also performs the one-time `1.0` migration - /// history transition before running migrations, so upgrades are automatic. - /// If you instead merge [`migrations()`](Self::migrations) into your own - /// [`Migrator`](sqlx::migrate::Migrator), you bypass `setup()` — call - /// [`reconcile_migration_checksums`](Self::reconcile_migration_checksums) - /// against that migrator's table before running it. + /// On an existing (pre-`1.0`) database, run the one-time transition documented + /// under "Upgrading to 1.0" in the README before calling this — `setup()` no + /// longer relocates the migration history automatically. Fresh databases need + /// no manual steps. #[cfg(feature = "migrate")] pub async fn setup(pool: &PgPool) -> Result<(), sqlx::Error> { - Self::relocate_legacy_migrations_table(pool).await?; - Self::reconcile_migration_checksums(pool, "apalis._sqlx_migrations").await?; Self::migrations().run(pool).await?; Ok(()) } - /// Move apalis's pre-`1.0` migration history from `public._sqlx_migrations` - /// into `apalis._sqlx_migrations`, where `1.0` tracks it. No-op once the - /// `apalis` table exists or on fresh databases. Does not re-stamp checksums. - #[cfg(feature = "migrate")] - pub async fn relocate_legacy_migrations_table(pool: &PgPool) -> Result<(), sqlx::Error> { - let needs_relocation: bool = sqlx::query_scalar( - "SELECT to_regclass('public._sqlx_migrations') IS NOT NULL \ - AND to_regclass('apalis._sqlx_migrations') IS NULL", - ) - .fetch_one(pool) - .await?; - if !needs_relocation { - return Ok(()); - } - - // Only adopt a table that is apalis's: a user's own won't have this row. - let is_apalis_table: bool = sqlx::query_scalar( - "SELECT EXISTS (SELECT 1 FROM public._sqlx_migrations WHERE version = 20220530084123)", - ) - .fetch_one(pool) - .await?; - if !is_apalis_table { - return Ok(()); - } - - sqlx::query("CREATE SCHEMA IF NOT EXISTS apalis") - .execute(pool) - .await?; - sqlx::query("ALTER TABLE public._sqlx_migrations SET SCHEMA apalis") - .execute(pool) - .await?; - Ok(()) - } - - /// Re-stamp the checksum of any migration whose content changed in `1.0` (only - /// the first, which gained `IF NOT EXISTS`) so an existing table isn't rejected - /// as modified. `setup()` calls this; call it yourself only when merging - /// [`migrations()`](Self::migrations) into your own - /// [`Migrator`](sqlx::migrate::Migrator), passing its table (e.g. - /// `"_sqlx_migrations"`), before you run it. No-op if `table` is fresh or absent. - #[cfg(feature = "migrate")] - pub async fn reconcile_migration_checksums( - pool: &PgPool, - table: &str, - ) -> Result<(), sqlx::Error> { - let table_exists: bool = sqlx::query_scalar("SELECT to_regclass($1) IS NOT NULL") - .bind(table) - .fetch_one(pool) - .await?; - if !table_exists { - return Ok(()); - } - - let update = - format!("UPDATE {table} SET checksum = $1 WHERE version = $2 AND checksum <> $1"); - for migration in Self::migrations().iter() { - sqlx::query(sqlx::AssertSqlSafe(update.clone())) - .bind(migration.checksum.as_ref()) - .bind(migration.version) - .execute(pool) - .await?; - } - Ok(()) - } - /// Get postgres migrations without running them #[cfg(feature = "migrate")] pub fn migrations() -> sqlx::migrate::Migrator {