diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1de0533..55b9292 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: rust: [stable, beta] include: - os: ubuntu-latest - rust: nightly + rust: nightly-2026-04-16 steps: - name: Checkout sources uses: actions/checkout@v6 @@ -108,6 +108,8 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2026-04-16 - name: Cache cargo registry uses: actions/cache@v5 @@ -132,9 +134,11 @@ jobs: - name: Install Rust nightly toolchain uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2026-04-16 - name: Install cargo-udeps uses: taiki-e/install-action@cargo-udeps - name: Check for unused dependencies - run: cargo +nightly udeps --all-targets --all-features + run: cargo udeps --all-targets --all-features diff --git a/.sqlx/query-1cd760004a2341bbded38a4fa431eaa74232f6f6f3121c7086a0b138195e9b0d.json b/.sqlx/query-1cd760004a2341bbded38a4fa431eaa74232f6f6f3121c7086a0b138195e9b0d.json index 1ed181c..c930097 100644 --- a/.sqlx/query-1cd760004a2341bbded38a4fa431eaa74232f6f6f3121c7086a0b138195e9b0d.json +++ b/.sqlx/query-1cd760004a2341bbded38a4fa431eaa74232f6f6f3121c7086a0b138195e9b0d.json @@ -67,6 +67,11 @@ "name": "metadata", "ordinal": 12, "type_info": "Text" + }, + { + "name": "idempotency_key", + "ordinal": 13, + "type_info": "Text" } ], "parameters": { @@ -85,6 +90,7 @@ true, true, false, + true, true ] }, diff --git a/.sqlx/query-40a420986a37c9db2fc35b161092d6063f186242ca1808cfae9d6477c7d8687b.json b/.sqlx/query-40a420986a37c9db2fc35b161092d6063f186242ca1808cfae9d6477c7d8687b.json index adb1f25..ee53bfb 100644 --- a/.sqlx/query-40a420986a37c9db2fc35b161092d6063f186242ca1808cfae9d6477c7d8687b.json +++ b/.sqlx/query-40a420986a37c9db2fc35b161092d6063f186242ca1808cfae9d6477c7d8687b.json @@ -67,6 +67,11 @@ "name": "metadata", "ordinal": 12, "type_info": "Text" + }, + { + "name": "idempotency_key", + "ordinal": 13, + "type_info": "Text" } ], "parameters": { @@ -85,6 +90,7 @@ true, true, false, + true, true ] }, diff --git a/.sqlx/query-a43052e877930a522d6a63789935653f4fe06d10361c555f8ade4b65589520bc.json b/.sqlx/query-a43052e877930a522d6a63789935653f4fe06d10361c555f8ade4b65589520bc.json index 86e2432..d225792 100644 --- a/.sqlx/query-a43052e877930a522d6a63789935653f4fe06d10361c555f8ade4b65589520bc.json +++ b/.sqlx/query-a43052e877930a522d6a63789935653f4fe06d10361c555f8ade4b65589520bc.json @@ -67,6 +67,11 @@ "name": "metadata", "ordinal": 12, "type_info": "Text" + }, + { + "name": "idempotency_key", + "ordinal": 13, + "type_info": "Text" } ], "parameters": { @@ -85,6 +90,7 @@ true, true, false, + true, true ] }, diff --git a/.sqlx/query-ae9b77d1f5b3f5125a37cdc29f6489b5f84003af0154b50166aaa6472863cd2d.json b/.sqlx/query-c9938cd19703a8268481ec3394a9479e67785606bccedf7876f23706feebcc89.json similarity index 59% rename from .sqlx/query-ae9b77d1f5b3f5125a37cdc29f6489b5f84003af0154b50166aaa6472863cd2d.json rename to .sqlx/query-c9938cd19703a8268481ec3394a9479e67785606bccedf7876f23706feebcc89.json index fa62399..b2e3975 100644 --- a/.sqlx/query-ae9b77d1f5b3f5125a37cdc29f6489b5f84003af0154b50166aaa6472863cd2d.json +++ b/.sqlx/query-c9938cd19703a8268481ec3394a9479e67785606bccedf7876f23706feebcc89.json @@ -1,12 +1,12 @@ { "db_name": "SQLite", - "query": "INSERT INTO\n Jobs\nVALUES\n (\n ?1,\n ?2,\n ?3,\n 'Pending',\n 0,\n ?4,\n ?5,\n NULL,\n NULL,\n NULL,\n NULL,\n ?6,\n ?7\n )\n", + "query": "INSERT INTO\n Jobs\nVALUES\n (\n ?1,\n ?2,\n ?3,\n 'Pending',\n 0,\n ?4,\n ?5,\n NULL,\n NULL,\n NULL,\n NULL,\n ?6,\n ?7,\n ?8\n ) ON CONFLICT(job_type, idempotency_key) DO NOTHING\n", "describe": { "columns": [], "parameters": { - "Right": 7 + "Right": 8 }, "nullable": [] }, - "hash": "ae9b77d1f5b3f5125a37cdc29f6489b5f84003af0154b50166aaa6472863cd2d" + "hash": "c9938cd19703a8268481ec3394a9479e67785606bccedf7876f23706feebcc89" } diff --git a/.sqlx/query-d7aefe54cd7388c208fff5b946390f217b575f0ca464a5faddd0fe2d51793983.json b/.sqlx/query-d7aefe54cd7388c208fff5b946390f217b575f0ca464a5faddd0fe2d51793983.json index 1760d46..cf2c0aa 100644 --- a/.sqlx/query-d7aefe54cd7388c208fff5b946390f217b575f0ca464a5faddd0fe2d51793983.json +++ b/.sqlx/query-d7aefe54cd7388c208fff5b946390f217b575f0ca464a5faddd0fe2d51793983.json @@ -67,6 +67,11 @@ "name": "metadata", "ordinal": 12, "type_info": "Text" + }, + { + "name": "idempotency_key", + "ordinal": 13, + "type_info": "Text" } ], "parameters": { @@ -85,6 +90,7 @@ true, true, false, + true, true ] }, diff --git a/.sqlx/query-eed1a436f9bad0f9ee6c46b47580fa7bdc9acef7704fe86466d28fc85126b8e3.json b/.sqlx/query-eed1a436f9bad0f9ee6c46b47580fa7bdc9acef7704fe86466d28fc85126b8e3.json index 5e87346..4aec34e 100644 --- a/.sqlx/query-eed1a436f9bad0f9ee6c46b47580fa7bdc9acef7704fe86466d28fc85126b8e3.json +++ b/.sqlx/query-eed1a436f9bad0f9ee6c46b47580fa7bdc9acef7704fe86466d28fc85126b8e3.json @@ -67,6 +67,11 @@ "name": "metadata", "ordinal": 12, "type_info": "Text" + }, + { + "name": "idempotency_key", + "ordinal": 13, + "type_info": "Text" } ], "parameters": { @@ -85,6 +90,7 @@ true, true, false, + true, true ] }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 0315c4f..b17f77d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- feat: idempotency for tasks (#77) + ## [1.0.0-rc.7] - 2026-04-07 - bump: to v1.0.0-rc.7 (#68) diff --git a/Cargo.lock b/Cargo.lock index 88ef3c2..a80bda5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "apalis" -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ca73a2477b3e6cf2650296c0441149ba50c40506b5aeb8ba75399e81181ce0" +checksum = "7780d1e7082500a4fdb463b0a6fc1c00e4012cd9b2af101c26fcabbb2f390f2c" dependencies = [ "apalis-core", "futures-util", @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "apalis-codec" -version = "0.1.0-rc.7" +version = "0.1.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f45e8249032bc5ed2c515471eca7913282190d8d8e7679cebf9b8fc6af40400a" +checksum = "c506e7f00c7c9c38eeb02290b3ec6328695f0614a257faefaeb8e8286746a665" dependencies = [ "apalis-core", "rmp-serde", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "apalis-core" -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc053b8daf9235fd974cf2a7d7f8ed7ee8108ba1736d3ecce20529d26c32f66" +checksum = "797af42a40f6bc297365f2fed187b74d089c63641f57ce2a5e0f629db560cb47" dependencies = [ "futures-channel", "futures-core", @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "apalis-sql" -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5adc4a7c146226036d44baed2862dc2ee22b7299d19e7c4941fd27e1267a45a" +checksum = "09b555912820da093b004a055105af258df116edcea761db6b759d01aabac2ec" dependencies = [ "apalis-core", "chrono", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "apalis-workflow" -version = "0.1.0-rc.7" +version = "0.1.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d223e7ce6a21e22c4da5ff9a5b82e57578544a73dda04718f8a2801dd13847" +checksum = "9392c07db462a5c0befec5d6685a37d9ec2424a244b7e7e021208c5c9544cef0" dependencies = [ "apalis-core", "futures", @@ -305,9 +305,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -354,9 +354,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.59" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "shlex", @@ -433,9 +433,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crossbeam-queue" @@ -847,9 +847,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "hashlink" @@ -1036,9 +1036,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1046,12 +1046,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -1084,9 +1084,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.94" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ "cfg-if", "futures-util", @@ -1120,9 +1120,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -1132,14 +1132,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.7.5", ] [[package]] @@ -1244,7 +1244,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -1293,15 +1293,14 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "openssl" -version = "0.10.76" +version = "0.10.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "foreign-types", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -1325,9 +1324,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.112" +version = "0.9.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" dependencies = [ "cc", "libc", @@ -1394,18 +1393,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" dependencies = [ "proc-macro2", "quote", @@ -1458,9 +1457,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plain" @@ -1564,9 +1563,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -1575,9 +1574,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -1627,16 +1626,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -1712,7 +1711,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1721,9 +1720,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "once_cell", "ring", @@ -1735,18 +1734,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -1786,7 +1785,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation", "core-foundation-sys", "libc", @@ -2057,7 +2056,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64", - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "bytes", "chrono", @@ -2079,7 +2078,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "sha1", @@ -2101,7 +2100,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64", - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "chrono", "crc", @@ -2119,7 +2118,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "sha2", @@ -2300,9 +2299,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.0" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -2411,9 +2410,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ulid" @@ -2421,7 +2420,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" dependencies = [ - "rand 0.9.2", + "rand 0.9.4", "serde", "web-time", ] @@ -2515,11 +2514,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -2528,7 +2527,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -2539,9 +2538,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.117" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -2552,9 +2551,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.67" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ "js-sys", "wasm-bindgen", @@ -2562,9 +2561,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.117" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2572,9 +2571,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.117" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -2585,9 +2584,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.117" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -2620,7 +2619,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", @@ -2642,14 +2641,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -2902,6 +2901,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -2951,7 +2956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.11.1", "indexmap", "log", "serde", diff --git a/Cargo.toml b/Cargo.toml index bbc764f..d342938 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,9 @@ features = ["sqlite"] [dependencies] serde = { version = "1", features = ["derive"] } serde_json = { version = "1" } -apalis-codec = { version = "0.1.0-rc.7", default-features = false } -apalis-core = { version = "1.0.0-rc.7", features = ["sleep"] } -apalis-sql = { version = "1.0.0-rc.7", default-features = false } +apalis-codec = { version = "0.1.0-rc.9", default-features = false } +apalis-core = { version = "1.0.0-rc.9", features = ["sleep"] } +apalis-sql = { version = "1.0.0-rc.9", default-features = false } log = "0.4.21" futures = "0.3.30" tokio = { version = "1", features = ["rt", "net"], optional = true } @@ -42,9 +42,9 @@ ulid = { version = "1", features = ["serde"] } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread"] } -apalis = { version = "1.0.0-rc.7" } -apalis-workflow = { version = "0.1.0-rc.7" } -apalis-codec = { version = "0.1.0-rc.7", features = ["msgpack"]} +apalis = { version = "1.0.0-rc.9" } +apalis-workflow = { version = "0.1.0-rc.9" } +apalis-codec = { version = "0.1.0-rc.9", features = ["msgpack"]} futures-util = "0.3.31" chrono = "0.4" diff --git a/examples/unique_jobs.rs b/examples/unique_jobs.rs new file mode 100644 index 0000000..e491566 --- /dev/null +++ b/examples/unique_jobs.rs @@ -0,0 +1,34 @@ +use apalis::prelude::*; +use apalis_sqlite::SqliteStorage; +use sqlx::SqlitePool; + +#[tokio::main] +async fn main() { + let dedupe_key = "a5bc4337-7789-4feb-b421-89c7231bac10"; + + let pool = SqlitePool::connect(":memory:").await.unwrap(); + SqliteStorage::setup(&pool).await.unwrap(); + let mut backend = SqliteStorage::new(&pool); + + let task_1 = TaskBuilder::new(42) + .with_idempotency_key(dedupe_key) + .build(); + + let task_2 = TaskBuilder::new(43) + .with_idempotency_key(dedupe_key) + .build(); + + backend.push_task(task_1).await.unwrap(); + backend.push_task(task_2).await.unwrap(); + + async fn task(task: u32, worker: WorkerContext) -> Result<(), BoxDynError> { + apalis_core::timer::sleep(std::time::Duration::from_secs(1)).await; + assert_eq!(task, 42); + worker.stop()?; + Ok(()) + } + let worker = WorkerBuilder::new("rango-tango") + .backend(backend) + .build(task); + worker.run().await.unwrap(); +} diff --git a/migrations/20260506101935_idempotency_key.sql b/migrations/20260506101935_idempotency_key.sql new file mode 100644 index 0000000..fcc991f --- /dev/null +++ b/migrations/20260506101935_idempotency_key.sql @@ -0,0 +1,6 @@ +ALTER TABLE + Jobs +ADD + COLUMN idempotency_key TEXT; + +CREATE UNIQUE INDEX idx_jobs_idempotency_key ON Jobs(job_type, idempotency_key); diff --git a/queries/task/sink.sql b/queries/task/sink.sql index 6c5e4c0..23eaef7 100644 --- a/queries/task/sink.sql +++ b/queries/task/sink.sql @@ -14,5 +14,6 @@ VALUES NULL, NULL, ?6, - ?7 - ) + ?7, + ?8 + ) ON CONFLICT(job_type, idempotency_key) DO NOTHING diff --git a/src/from_row.rs b/src/from_row.rs index 7c3f133..371eab7 100644 --- a/src/from_row.rs +++ b/src/from_row.rs @@ -15,6 +15,7 @@ pub(crate) struct SqliteTaskRow { pub(crate) done_at: Option, pub(crate) priority: Option, pub(crate) metadata: Option, + pub(crate) idempotency_key: Option, } impl TryInto for SqliteTaskRow { @@ -54,6 +55,7 @@ impl TryInto for SqliteTaskRow { metadata: self .metadata .map(|meta| serde_json::from_str(&meta).unwrap_or(serde_json::Value::Null)), + idempotency_key: self.idempotency_key, }) } } diff --git a/src/queries/list_tasks.rs b/src/queries/list_tasks.rs index ac907f9..a2c9a4d 100644 --- a/src/queries/list_tasks.rs +++ b/src/queries/list_tasks.rs @@ -21,10 +21,9 @@ where { fn list_tasks( &self, - queue: &str, filter: &Filter, ) -> impl Future>, Self::Error>> + Send { - let queue = queue.to_owned(); + let queue = self.config().queue().to_string(); let pool = self.pool.clone(); let limit = filter.limit() as i32; let offset = filter.offset() as i32; diff --git a/src/queries/list_workers.rs b/src/queries/list_workers.rs index aa8b7d4..ce956e7 100644 --- a/src/queries/list_workers.rs +++ b/src/queries/list_workers.rs @@ -22,11 +22,8 @@ where Error = sqlx::Error, >, { - fn list_workers( - &self, - queue: &str, - ) -> impl Future, Self::Error>> + Send { - let queue = queue.to_owned(); + fn list_workers(&self) -> impl Future, Self::Error>> + Send { + let queue = self.config().queue().to_string(); let pool = self.pool.clone(); let limit = 100; let offset = 0; diff --git a/src/queries/metrics.rs b/src/queries/metrics.rs index 3ef9c67..a21db25 100644 --- a/src/queries/metrics.rs +++ b/src/queries/metrics.rs @@ -40,12 +40,9 @@ where Ok(rec) } } - fn fetch_by_queue( - &self, - queue_id: &str, - ) -> impl Future, Self::Error>> + Send { + fn fetch_by_queue(&self) -> impl Future, Self::Error>> + Send { let pool = self.pool.clone(); - let queue_id = queue_id.to_owned(); + let queue_id = self.config().queue().to_string(); async move { let rec = sqlx::query_file_as!( StatisticRow, diff --git a/src/sink.rs b/src/sink.rs index 9d592aa..02c0fb4 100644 --- a/src/sink.rs +++ b/src/sink.rs @@ -58,6 +58,7 @@ pub async fn push_tasks( let args = task.args; // Use specified queue if specified, otherwise use default let job_type = cfg.queue().to_string(); + let idempotency_key = task.parts.idempotency_key; let meta = serde_json::to_string(&task.parts.ctx.meta()).unwrap_or_default(); sqlx::query_file!( "queries/task/sink.sql", @@ -67,7 +68,8 @@ pub async fn push_tasks( max_attempts, run_at, priority, - meta + meta, + idempotency_key ) .execute(&mut *tx) .await?; diff --git a/supply-chain/config.toml b/supply-chain/config.toml index a6a5993..7c5bf8e 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -20,19 +20,19 @@ version = "1.0.102" criteria = "safe-to-deploy" [[exemptions.apalis]] -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" criteria = "safe-to-run" [[exemptions.apalis-codec]] -version = "0.1.0-rc.7" +version = "0.1.0-rc.9" criteria = "safe-to-deploy" [[exemptions.apalis-core]] -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" criteria = "safe-to-deploy" [[exemptions.apalis-sql]] -version = "1.0.0-rc.7" +version = "1.0.0-rc.9" criteria = "safe-to-deploy" [[exemptions.apalis-sqlite]] @@ -40,7 +40,7 @@ version = "1.0.0-rc.2" criteria = "safe-to-deploy" [[exemptions.apalis-workflow]] -version = "0.1.0-rc.7" +version = "0.1.0-rc.9" criteria = "safe-to-run" [[exemptions.async-channel]] @@ -108,7 +108,7 @@ version = "1.3.2" criteria = "safe-to-deploy" [[exemptions.bitflags]] -version = "2.11.0" +version = "2.11.1" criteria = "safe-to-deploy" [[exemptions.block-buffer]] @@ -132,7 +132,7 @@ version = "1.11.1" criteria = "safe-to-deploy" [[exemptions.cc]] -version = "1.2.59" +version = "1.2.61" criteria = "safe-to-deploy" [[exemptions.cfg-if]] @@ -168,7 +168,7 @@ version = "3.4.0" criteria = "safe-to-deploy" [[exemptions.crc-catalog]] -version = "2.4.0" +version = "2.5.0" criteria = "safe-to-deploy" [[exemptions.crossbeam-queue]] @@ -344,7 +344,7 @@ version = "0.15.5" criteria = "safe-to-deploy" [[exemptions.hashbrown]] -version = "0.16.1" +version = "0.17.0" criteria = "safe-to-deploy" [[exemptions.hashlink]] @@ -424,11 +424,11 @@ version = "1.1.0" criteria = "safe-to-deploy" [[exemptions.idna_adapter]] -version = "1.2.1" +version = "1.2.2" criteria = "safe-to-deploy" [[exemptions.indexmap]] -version = "2.13.1" +version = "2.14.0" criteria = "safe-to-deploy" [[exemptions.instant]] @@ -444,7 +444,7 @@ version = "1.0.18" criteria = "safe-to-deploy" [[exemptions.js-sys]] -version = "0.3.94" +version = "0.3.97" criteria = "safe-to-deploy" [[exemptions.kv-log-macro]] @@ -460,7 +460,7 @@ version = "0.1.0" criteria = "safe-to-deploy" [[exemptions.libc]] -version = "0.2.184" +version = "0.2.186" criteria = "safe-to-deploy" [[exemptions.libm]] @@ -468,7 +468,7 @@ version = "0.2.16" criteria = "safe-to-deploy" [[exemptions.libredox]] -version = "0.1.15" +version = "0.1.16" criteria = "safe-to-deploy" [[exemptions.libsqlite3-sys]] @@ -536,7 +536,7 @@ version = "1.21.4" criteria = "safe-to-deploy" [[exemptions.openssl]] -version = "0.10.76" +version = "0.10.79" criteria = "safe-to-deploy" [[exemptions.openssl-macros]] @@ -548,7 +548,7 @@ version = "0.2.1" criteria = "safe-to-deploy" [[exemptions.openssl-sys]] -version = "0.9.112" +version = "0.9.115" criteria = "safe-to-deploy" [[exemptions.parking]] @@ -576,11 +576,11 @@ version = "0.8.3" criteria = "safe-to-run" [[exemptions.pin-project]] -version = "1.1.11" +version = "1.1.12" criteria = "safe-to-deploy" [[exemptions.pin-project-internal]] -version = "1.1.11" +version = "1.1.12" criteria = "safe-to-deploy" [[exemptions.pin-project-lite]] @@ -604,7 +604,7 @@ version = "0.10.2" criteria = "safe-to-deploy" [[exemptions.pkg-config]] -version = "0.3.32" +version = "0.3.33" criteria = "safe-to-deploy" [[exemptions.plain]] @@ -652,11 +652,11 @@ version = "6.0.0" criteria = "safe-to-deploy" [[exemptions.rand]] -version = "0.8.5" +version = "0.8.6" criteria = "safe-to-deploy" [[exemptions.rand]] -version = "0.9.2" +version = "0.9.4" criteria = "safe-to-deploy" [[exemptions.rand_chacha]] @@ -680,7 +680,7 @@ version = "0.5.18" criteria = "safe-to-deploy" [[exemptions.redox_syscall]] -version = "0.7.3" +version = "0.7.5" criteria = "safe-to-deploy" [[exemptions.ring]] @@ -708,15 +708,15 @@ version = "1.1.4" criteria = "safe-to-deploy" [[exemptions.rustls]] -version = "0.23.37" +version = "0.23.40" criteria = "safe-to-deploy" [[exemptions.rustls-pki-types]] -version = "1.14.0" +version = "1.14.1" criteria = "safe-to-deploy" [[exemptions.rustls-webpki]] -version = "0.103.10" +version = "0.103.13" criteria = "safe-to-deploy" [[exemptions.rustversion]] @@ -896,7 +896,7 @@ version = "0.1.1" criteria = "safe-to-deploy" [[exemptions.tokio]] -version = "1.51.0" +version = "1.52.2" criteria = "safe-to-deploy" [[exemptions.tokio-macros]] @@ -936,7 +936,7 @@ version = "0.1.36" criteria = "safe-to-deploy" [[exemptions.typenum]] -version = "1.19.0" +version = "1.20.0" criteria = "safe-to-deploy" [[exemptions.ulid]] @@ -996,7 +996,7 @@ version = "0.11.1+wasi-snapshot-preview1" criteria = "safe-to-deploy" [[exemptions.wasip2]] -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" criteria = "safe-to-deploy" [[exemptions.wasip3]] @@ -1008,23 +1008,23 @@ version = "0.1.0" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen]] -version = "0.2.117" +version = "0.2.120" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-futures]] -version = "0.4.67" +version = "0.4.70" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro]] -version = "0.2.117" +version = "0.2.120" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro-support]] -version = "0.2.117" +version = "0.2.120" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-shared]] -version = "0.2.117" +version = "0.2.120" criteria = "safe-to-deploy" [[exemptions.wasm-encoder]] @@ -1048,7 +1048,7 @@ version = "0.26.11" criteria = "safe-to-deploy" [[exemptions.webpki-roots]] -version = "1.0.6" +version = "1.0.7" criteria = "safe-to-deploy" [[exemptions.whoami]] @@ -1175,6 +1175,10 @@ criteria = "safe-to-deploy" version = "0.51.0" criteria = "safe-to-deploy" +[[exemptions.wit-bindgen]] +version = "0.57.1" +criteria = "safe-to-deploy" + [[exemptions.wit-bindgen-core]] version = "0.51.0" criteria = "safe-to-deploy"