Hadisələrin təkrarlanması
1) Niyə duplikasiya lazımdır
Dublikatlar retrajlar, şəbəkə taymautları, fayllardan sonra bərpa və tarixi məlumatların repleyni səbəbindən görünür. Onlara nəzarət etmirsinizsə:- invariantlar pozulur (ikiqat silinmə, təkrar email/SMS, «iki dəfə yaradılan» sifariş);
- artan xərclər (təkrar qeydlər/emal);
- təhrif analitikası.
Deduplikasiyanın məqsədi tez-tez idempotentliklə birlikdə icazə verilən nəqliyyat təkrarlarında bir dəfə müşahidə olunan effekti təmin etməkdir.
2) Deduplikasiya harada yerləşdirilir (səviyyələr)
1. Edge/API-şlyuz - 'Idempotency-Keu '/bədən + imza açıq dublları kəsin.
2. Broker/axın - anahtar/sekvens üzrə məntiqi deduplikasiya, səhv olduqda coalescing (az hallarda - dəyər səbəbindən).
3. Hadisələrin qəbuledicisi (consumer) - əsas yer: Inbox/açar cədvəli/cache.
4. Sink (BD/kesh) - unikal açarlar/UPSERT/versiyalar/kompaksiya.
5. ETL/analizi - zaman pəncərəsi və dayaq anahtarı ilə deadup.
Qayda: mümkün qədər tez, lakin saxta işlərin dəyəri və replay ehtiyacı nəzərə alınmaqla.
3) Duplikasiya açarları
3. 1 Təbii (üstünlük)
`payment_id`, `order_id`, `saga_id#step`, `aggregate_id#seq`.
Sabitlik və məna təmin edir.
3. 2 Kompozit
`(tenant_id, type, external_id, version)` или `(user_id, event_ts_truncated, payload_hash)`.
3. 3 Fingerprint
Tarlaların determinik alt çoxluğunun hash (qaydanı/registrləri normallaşdırın), 'HMAC (secret, payload)'.
3. 4 ardıcıllığı/versiyası
Monoton 'seq' per aggregate (optimist bloklama/versiya).
Anti-model: biznes mahiyyəti ilə əlaqə olmadan «random UUID» - dedup mümkün deyil.
4) Müvəqqəti pəncərələr və nizam
Təkrarlama pəncərəsi - hadisənin yenidən gələ biləcəyi dövr (adətən 24-72 saat; maliyyə üçün - daha uzun).
Out-of-order: gecikməyə icazə (lateness). Streaming frameworks - event time + watermarks.
Sliding/Fix-window dedup: "Son N dəqiqələrdə açarı gördünmü? ».
Sequence-aware: əgər 'seq' son emal ≤ - dubl/təkrar.
5) Verilənlərin strukturları və həyata keçirilməsi
5. 1 Dəqiq hesab (exact)
Redis SET/STRING + TTL: 'SETNX key 1 EX 86400' → 'ilk dəfə - emal edirik, əks halda - SKIP ".
LRU/LFU cache (in-proc): sürətli, lakin volatile → yalnız ilk maneə kimi daha yaxşı.
SQL unikal indeksləri + UPSERT: «daxil edin və ya yeniləyin» (idempotent effekti).
5. 2 Proxy strukturları (probabilistic)
Bloom/Cuckoo filter: ucuz yaddaş, mümkün saxta (false positive). Maliyyə/sifarişlər üçün deyil, açıq «səs-küylü» deşik (məsələn, telemetriya) üçün uyğundur.
Count-Min Sketch: «isti» dubllardan qorunmaq üçün tezliklərin qiymətləndirilməsi.
5. 3 Axın halları
Kafka Streams/Flink: keyed state store c TTL, pəncərədə açar dedup; checkpoint/restore.
Watermark + allowed lateness: gecikmiş hadisələrin pəncərəsini idarə edir.
6) Əməliyyat nümunələri
6. 1 Inbox (daxil olan cədvəl)
Yan təsirlərə qədər 'message _ id '/açarı və nəticəni saxlayın:pseudo
BEGIN;
ins = INSERT INTO inbox(id, received_at) ON CONFLICT DO NOTHING;
IF ins_not_inserted THEN RETURN cached_result;
result = handle(event);
UPSERT sink with result; -- idempotent sync
UPDATE inbox SET status='done', result_hash=... WHERE id=...;
COMMIT;
Təkrar yazını görəcək və təsiri təkrarlamayacaq.
6. 2 Outbox
Biznes qeydləri və bir əməliyyat hadisəsi → publisher broker verir. İstehlakçıdan ikiqat aradan qaldırmır, lakin «deşiklər» istisna edir.
6. 3 Unikal indekslər/UPSERT
sql
INSERT INTO payments(id, status, amount)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO NOTHING; -- "create once"
və ya nəzarət olunan yeniləmə versiyası:
sql
UPDATE orders
SET status = $new, version = version + 1
WHERE id=$id AND version = $expected; -- optimistic blocking
6. 4 Aqreqatların versiyalaşdırılması
Hadisə, əgər 'event. version = aggregate. version + 1`. Əks halda - dubl/təkrarlama/münaqişə.
7) Dedup və brokerlər/axınlar
7. 1 Kafka
Idempotent Producer girişdə dublları azaldır.
Transactions atom ofset + çıxış qeydləri kommitasiya imkan verir.
Compaction: per key son dəyəri saxlayır - post-faktum dedup/koalitesing (ödənişlər üçün deyil).
Consumer-side: pəncərə açarları üçün state store/Redis/DB.
7. 2 NATS / JetStream
Ack/redaktə → at-least-once. İstehlakçıda dedup (Inbox/Redis).
JetStream sequence/durabot istifadəçi təkrarlamaları asanlaşdırır.
7. 3 Növbələr (Rabbit/SQS)
Visibility timeout + təkrar çatdırılma → açar + deadup store lazımdır.
SQS FIFO ilə 'MessageGroupId '/' DeduplicationId' kömək edir, lakin TTL pəncərələri provayder ilə məhdudlaşır - biznes tələb edirsə, açarları daha uzun saxlayın.
8) Anbarlar və analitiklər
8. 1 ClickHouse/BigQuery
Pəncərə dedupu: 'ORDER BY key, ts' və 'argMax '/' anyLast' şərti ilə.
ClickHouse:sql
SELECT key,
anyLast(value) AS v
FROM t
WHERE ts >= now() - INTERVAL 1 DAY
GROUP BY key;
Və ya «unikal» hadisələrin materiallaşdırılmış təbəqəsi (açar/versiya üzrə merj).
8. 2 Log/Telemetriya
Deyək approximate dedup (Bloom) haqqında ingest → şəbəkə/disk qənaət.
9) Təkrar emal, replay və backfill
Dedup açarları replay (TTL ≥ replay pəncərəsi) yaşamalıdır.
Backfill üçün, onlayn pəncərəyə müdaxilə etməmək üçün ('key #source = batch2025') və ya fərdi «gavalı» olan açar sahəsindən istifadə edin.
Nəticənin artefaktlarını (hash/versiya) saxlayın - bu təkrarlarda «fast-skip» sürətləndirir.
10) Metrika və müşahidə
'dedup _ hit _ total '/' dedup _ hit _ rate' - tutulan dublların payı.
Ehtimal filtrləri üçün 'dedup _ fp _ rate'.
'window _ size _ seconds' faktiki (telemetry late arrivals üzrə).
`inbox_conflict_total`, `upsert_conflict_total`.
`replayed_events_total`, `skipped_by_inbox_total`.
tenant/key/type profilləri: ən çox dubl harada və niyə.
Логи: `message_id`, `idempotency_key`, `seq`, `window_id`, `action=process|skip`.
11) Təhlükəsizlik və məxfilik
PII açar qoymayın; hash/təxəllüslər istifadə edin.
Barmaq izi imzası üçün - HMAC (gizli, canonical_payload), toqquşma/saxta qarşısını almaq üçün.
Anahtarların saxlanma müddəti komplayensiyaya uyğundur (GDPR retenshn).
12) Performans və dəyəri
In-proc LRU ≪ Redis ≪ SQL bir əməliyyat üçün gizli/dəyəri.
Redis: ucuz və sürətli, lakin açar həcmi və TTL nəzərə; 'tenant/hash'.
SQL: p99 bahalı, lakin güclü zəmanət və auditoriya verir.
Probabilistic filtrlər: çox ucuz, lakin FP mümkündür - «əlavə SKIP» kritik olmayan yerlərdə tətbiq edin.
13) Anti-nümunələr
«Biz Kafka exactly-once var - açar lazım deyil». Lazım - mavi/biznes təbəqəsində.
Açarlar üçün çox qısa TTL → reples/gecikmə ikiqat çatdıracaq.
Qlobal tək dedup stor → hotspot və SPOF; tenant/açar ilə cilalanmır.
Dedup yalnız yaddaşda - proses itkisi = dalğa ikiqat.
Pul/sifarişlər üçün Bloom - false positive qanuni əməliyyatdan məhrum edəcək.
Razılaşdırılmamış payload kanonizasiyası - məna baxımından eyni mesajların müxtəlif heşləri.
Out-of-order görməməzlik - gec hadisələr səhvən iki dəfə qeyd olunur.
14) Giriş çek siyahısı
- Təbii açarı (və ya kompozit/izi) müəyyən edin.
- Dedup pəncərəsini və 'lateness' siyasətini quraşdırın.
- Bir səviyyə seçin: edge, consumer, sink; charding təmin edin.
- Inbox/UPSERT həyata; axınlar üçün - keyed state + TTL.
- approximate baryerinə ehtiyacınız varsa - Bloom/Cuckoo (yalnız kritik olmayan domenlər üçün).
- Replay uyğunluğunu (TTL ≥ replay/backfill pəncərəsi) konfiqurasiya edin.
- Metrik 'dedup _ hit _ rate', konfliktlər və pəncərələr; per-tenant daşbordları.
- Game Day: taymaut/retras, replay, out-of-order, düşmə cache.
- Payload və açar versiyasını sənədləşdirin.
- «isti açarlar» və uzun pəncərələrdə yükləmə testləri aparın.
15) Konfiqurasiya/kod nümunələri
15. 1 Redis SETNX + TTL (maneə)
lua
-- KEYS[1] = "dedup:{tenant}:{key}"
-- ARGV[1] = ttl_seconds local ok = redis. call("SET", KEYS[1], "1", "NX", "EX", ARGV[1])
if ok then return "PROCESS"
else return "SKIP"
end
15. 2 PostgreSQL Inbox
sql
CREATE TABLE inbox (
id text PRIMARY KEY,
received_at timestamptz default now(),
status text default 'received',
result_hash text
);
-- In the handler: INSERT... ON CONFLICT DO NOTHING -> check, then UPSERT in blue.
15. 3 Kafka Streams (pəncərədə dedup)
java var deduped = input
.selectKey((k,v) -> v.idempotencyKey())
.groupByKey()
.windowedBy(TimeWindows. ofSizeWithNoGrace(Duration. ofHours(24)))
.reduce((oldV,newV) -> oldV) // first wins
.toStream()
.map((wKey,val) -> KeyValue. pair(wKey. key(), val));
15. 4 Flink (keyed state + TTL, psevdo)
java
ValueState<Boolean> seen;
env. enableCheckpointing(10000);
onEvent(e):
if (!seen.value()) { process(e); seen. update(true); }
15. 5 NGINX/API-şlyuz (edge üzrə Idempotency-Key)
nginx map $http_idempotency_key $idkey { default ""; }
Proxy the key to the backend; backend solves deadup (Inbox/Redis).
16) FAQ
Q: seçmək üçün nə: dedupe və ya təmiz idempotent?
A: Adətən hər ikisi: dedup - sürətli «filter» (qənaət), idempotentlik - düzgün effekt zəmanəti.
Q: Hansı TTL qoymaq?
A: Mümkün təkrar çatdırılma üçün maksimum vaxt ≥ + ehtiyat. Tipik 24-72h; maliyyə və gecikmiş vəzifələr üçün - gün/həftə.
S: Gec hadisələri necə idarə etmək olar?
A: 'allowed lateness' və 'late _ event' siqnalizasiyasını konfiqurasiya edin; gec - ayrı bir filial vasitəsilə (recompute/skip).
Q: Bütün telemetriya axını deuplikasiya edilə bilərmi?
A: Bəli, edge-də approximate-filters (Bloom), lakin FP-ni nəzərə alın və kritik iş effektlərinə tətbiq etməyin.
Q: Dedup backfill mane olur?
A: Açar sahələrini bölün ('key #batch2025') və ya backfill zamanı maneəni söndürün; TTL açarları yalnız onlayn pəncərələri əhatə etməlidir.
17) Nəticələr
Deduplikasiya kompozisiyadır: düzgün açar, pəncərə və vəziyyət strukturu + əməliyyat nümunələri (Inbox/Outbox/UPSERT) və prosedur və gecikmiş hadisələrlə şüurlu iş. Maneələri ən ucuz yerdə yerləşdirin, çəngəllərdə idempotentliyi təmin edin, 'dedup _ hit _ rate' ölçün və reple/faylları sınayın - beləliklə, lazımsız gizli və dəyərli quyruqlar olmadan «effektiv exactly-once» əldə edəcəksiniz.