mirror of
https://github.com/d3vyce/taskiq-deduplication.git
synced 2026-08-04 19:14:07 +00:00
feat: warn when deduplication_key_fields are missing from task kwargs (#68)
This commit is contained in:
@@ -178,6 +178,10 @@ async def send_welcome_email(user_id: int, locale: str) -> None:
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a listed field is absent from a task's kwargs, it is dropped from the
|
||||||
|
fingerprint and a warning is logged, since this can make genuinely different
|
||||||
|
calls collide on the same lock.
|
||||||
|
|
||||||
## Opting out per task
|
## Opting out per task
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|||||||
@@ -144,11 +144,19 @@ class RedisDeduplicationMiddleware(TaskiqMiddleware):
|
|||||||
key_fields = parse_list_label(
|
key_fields = parse_list_label(
|
||||||
message.labels.get(DEDUP_KEY_FIELDS_LABEL), DEDUP_KEY_FIELDS_LABEL
|
message.labels.get(DEDUP_KEY_FIELDS_LABEL), DEDUP_KEY_FIELDS_LABEL
|
||||||
)
|
)
|
||||||
kwargs = (
|
if key_fields is not None:
|
||||||
{k: v for k, v in message.kwargs.items() if k in key_fields}
|
missing = [field for field in key_fields if field not in message.kwargs]
|
||||||
if key_fields is not None
|
if missing:
|
||||||
else message.kwargs
|
logger.warning(
|
||||||
)
|
"Task %s requested deduplication_key_fields %r but they are "
|
||||||
|
"absent from kwargs; they are dropped from the fingerprint, which "
|
||||||
|
"may cause distinct calls to collide.",
|
||||||
|
message.task_name,
|
||||||
|
missing,
|
||||||
|
)
|
||||||
|
kwargs = {k: v for k, v in message.kwargs.items() if k in key_fields}
|
||||||
|
else:
|
||||||
|
kwargs = message.kwargs
|
||||||
try:
|
try:
|
||||||
payload = json.dumps(
|
payload = json.dumps(
|
||||||
{"task": message.task_name, "kwargs": kwargs},
|
{"task": message.task_name, "kwargs": kwargs},
|
||||||
|
|||||||
@@ -605,6 +605,33 @@ class TestLabelTypeParsing:
|
|||||||
assert any("{'a': 1}" in r.message for r in caplog.records)
|
assert any("{'a': 1}" in r.message for r in caplog.records)
|
||||||
assert key is not None
|
assert key is not None
|
||||||
|
|
||||||
|
async def test_missing_key_fields_warns(self, middleware, make_message, caplog):
|
||||||
|
import logging
|
||||||
|
|
||||||
|
msg = make_message(
|
||||||
|
kwargs={"order_id": 1},
|
||||||
|
labels={DEDUP_KEY_FIELDS_LABEL: ["user_id", "order_id"]},
|
||||||
|
)
|
||||||
|
with caplog.at_level(logging.WARNING, logger="taskiq_deduplication.middleware"):
|
||||||
|
key = middleware._build_deduplication_key(msg)
|
||||||
|
assert any("user_id" in r.message for r in caplog.records)
|
||||||
|
# the field that is present must not be reported as missing
|
||||||
|
assert not any("order_id" in r.message for r in caplog.records)
|
||||||
|
assert key is not None
|
||||||
|
|
||||||
|
async def test_present_key_fields_do_not_warn(
|
||||||
|
self, middleware, make_message, caplog
|
||||||
|
):
|
||||||
|
import logging
|
||||||
|
|
||||||
|
msg = make_message(
|
||||||
|
kwargs={"user_id": 1, "order_id": 2},
|
||||||
|
labels={DEDUP_KEY_FIELDS_LABEL: ["user_id"]},
|
||||||
|
)
|
||||||
|
with caplog.at_level(logging.WARNING, logger="taskiq_deduplication.middleware"):
|
||||||
|
middleware._build_deduplication_key(msg)
|
||||||
|
assert not any("absent from kwargs" in r.message for r in caplog.records)
|
||||||
|
|
||||||
def test_stringified_key_fields_parsed_correctly(self, middleware, make_message):
|
def test_stringified_key_fields_parsed_correctly(self, middleware, make_message):
|
||||||
# taskiq's prepare_label() stringifies ["a", "b"] → "['a', 'b']" before pre_send
|
# taskiq's prepare_label() stringifies ["a", "b"] → "['a', 'b']" before pre_send
|
||||||
m1 = make_message(
|
m1 = make_message(
|
||||||
|
|||||||
Reference in New Issue
Block a user