Μετάβαση στο περιεχόμενο
EuroVPSEuroVPS
Περιοχή πελατών

Κάθε ενοποίηση tickets παραδίδει δύο φορές. Χτίστε την γι' αυτό.

Webhooks που επαναλαμβάνουν, συνεργάτες που ξαναστέλνουν, δίκτυα που χάνουν απαντήσεις. Πώς χτίζεται μια γέφυρα tickets με υπογραφή, idempotent, χωρίς loops και με περιορισμένη εμβέλεια, με κώδικα που μπορείτε να επαναχρησιμοποιήσετε.

Από EuroVPS Engineering

5 λεπτά ανάγνωσης
Every ticket integration delivers twice. Build it for that.

Η σύνδεση του συστήματος tickets σας με το portal ενός συνεργάτη μοιάζει με δουλειά ενός απογεύματος: στέλνετε ένα webhook όταν κάτι αλλάζει, καλείτε το API τους όταν απαντούν. Το πρώτο demo δουλεύει πάντα. Τα προβλήματα ξεκινούν την τρίτη εβδομάδα, όταν ένα timeout κάνει τον αποστολέα να ξαναπροσπαθήσει, ένα ticket παίρνει την ίδια απάντηση δύο φορές, και δύο συστήματα αρχίζουν να απαντούν το ένα στο άλλο σε loop στις τρεις το πρωί. Αυτό το άρθρο αφορά τις τέσσερις ιδιότητες που το σταματούν, όποιες πλατφόρμες ticketing κι αν βρίσκονται στα δύο άκρα.

Υποθέστε παράδοση at-least-once

Κάθε αποστολέας που επαναλαμβάνει μπορεί να παραδώσει το ίδιο event περισσότερες από μία φορές. Δεν έχει άλλη επιλογή: αν ο server σας επεξεργάστηκε το αίτημα αλλά η απάντηση χάθηκε στον δρόμο της επιστροφής, ο αποστολέας βλέπει timeout και πρέπει να ξαναπροσπαθήσει. Το «exactly once» δεν υπάρχει σε δίκτυο. Αυτό που υπάρχει είναι παράδοση at-least-once συν έναν παραλήπτη που μπορεί να δει το ίδιο πράγμα δύο φορές με ασφάλεια.

Άρα ο πρώτος κανόνας σχεδιασμού είναι ότι κάθε event φέρει ένα σταθερό, μοναδικό αναγνωριστικό που επιλέγει ο αποστολέας, και ο παραλήπτης καταγράφει τα αναγνωριστικά που έχει ήδη εφαρμόσει.

CREATE TABLE applied_events (
  event_id    TEXT PRIMARY KEY,
  received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  ticket_ref  TEXT
);

Σε κάθε παράδοση, δοκιμάστε πρώτα να εισαγάγετε το ID. Αν το insert αποτύχει στο primary key, έχετε ήδη κάνει αυτή τη δουλειά: απαντήστε επιτυχία και σταματήστε. Το να απαντήσετε επιτυχία έχει σημασία. Αν απαντήσετε με σφάλμα, ο αποστολέας θα συνεχίσει να επαναλαμβάνει κάτι που έχετε ήδη εφαρμόσει.

Το ID του event πρέπει να ανήκει στο event, όχι στην προσπάθεια παράδοσης. Μια απάντηση στο ticket 4711 πρέπει να φέρει ένα ID που προκύπτει από εκείνη την απάντηση, για παράδειγμα το transaction ID στο σύστημα προέλευσης, ώστε κάθε επανάληψή της να φέρει την ίδια τιμή.

Υπογράψτε κάθε παράδοση, και ελέγξτε το ρολόι

Υπογράψτε κάθε παράδοση: ένα HMAC πάνω στο raw body με ένα κοινό μυστικό, σταλμένο σε header, μαζί με ένα timestamp ώστε ένα αίτημα που υποκλάπηκε να μην μπορεί να αναπαραχθεί την επόμενη εβδομάδα.

import hmac, hashlib, time

SECRET = load_secret()          # never in the code repository
MAX_SKEW = 300                  # seconds

def verify(raw_body: bytes, ts_header: str, sig_header: str) -> bool:
    ts = int(ts_header)
    if abs(time.time()-ts) > MAX_SKEW:
        return False
    signed = f"{ts}.".encode() + raw_body
    expected = hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig_header)

Τρεις λεπτομέρειες που οι περισσότεροι κάνουν λάθος. Υπογράψτε τα raw bytes, όχι ένα JSON αντικείμενο που ξανασειριοποιήθηκε, γιατί δύο βιβλιοθήκες δεν θα σειριοποιήσουν το ίδιο αντικείμενο με τον ίδιο τρόπο. Συγκρίνετε με συνάρτηση σταθερού χρόνου, όχι με ==. Και κρατήστε ξεχωριστό μυστικό ανά συνεργάτη, ώστε να μπορείτε να αλλάξετε το ένα χωρίς να αγγίξετε τα άλλα.

Σπάστε τα loops εσκεμμένα

Μια γέφυρα αντιγράφει απαντήσεις και προς τις δύο κατευθύνσεις. Αν το σύστημα Α προωθεί τις απαντήσεις του στο Β, και το Β προωθεί τις δικές του στο Α, τότε μια απάντηση που έφτασε από το Β είναι, από τη σκοπιά του Α, μια νέα απάντηση στο ticket. Χωρίς προστασία, πηγαίνει κατευθείαν πίσω. Μετά το Β κάνει το ίδιο.

Η λύση είναι να σημειώνετε την προέλευση και να την ελέγχετε πριν προωθήσετε:

  • Όταν εφαρμόζετε μια εισερχόμενη απάντηση, καταγράψτε ότι ήρθε από τον συνεργάτη, για παράδειγμα σε ένα custom field ή σε ένα attribute της συναλλαγής.
  • Το outbound trigger σας παραλείπει κάθε συναλλαγή που είναι σημειωμένη ως προερχόμενη από τον συνεργάτη.
  • Κάντε το ίδιο και από την πλευρά του συνεργάτη. Μη βασίζεστε μόνο στο ένα άκρο.

Μετά δοκιμάστε το με ένα πραγματικό ticket: στείλτε μια απάντηση από κάθε πλευρά και ελέγξτε ότι καθεμία εμφανίζεται ακριβώς μία φορά στην άλλη.

Δώστε στον συνεργάτη μία ουρά και τίποτα άλλο

Η εισερχόμενη κατεύθυνση συνήθως σημαίνει ότι ο συνεργάτης καλεί το API του συστήματος tickets σας. Δώστε του έναν αποκλειστικό λογαριασμό, ποτέ ένα κοινόχρηστο token διαχειριστή, και δώστε δικαιώματα σε μία μόνο ουρά.

Στο Request Tracker αυτό σημαίνει έναν χρήστη χωρίς καθολικά δικαιώματα, και μετά, μόνο στην κοινή ουρά, μόνο τα δικαιώματα που χρειάζεται η γέφυρα: τυπικά SeeQueue, ShowTicket, CreateTicket, ReplyToTicket και ModifyTicket. Αφήστε έξω το ShowTicketComments, ώστε ο συνεργάτης να μην μπορεί ποτέ να διαβάσει εσωτερικά σχόλια, και μη δώσετε τίποτα σε καμία άλλη ουρά. Εκδώστε ένα auth token για αυτόν τον χρήστη και δώστε το στον συνεργάτη.

Μετά αποδείξτε την εμβέλεια αντί να την εμπιστεύεστε:

# Should succeed: the shared queue
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: token $PARTNER_TOKEN" \
  https://rt.example.com/REST/2.0/queue/Partner-Shared

# Should be refused: any other queue
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: token $PARTNER_TOKEN" \
  https://rt.example.com/REST/2.0/queue/General

Μια συμβουλή ειδικά για το Request Tracker: αν το RT τρέχει κάτω από Apache με mod_fcgid, το header Authorization δεν περνά στην εφαρμογή εκτός αν προσθέσετε FcgidPassHeader Authorization στο virtual host. Τα αιτήματα με token αποτυγχάνουν τότε ως μη εξουσιοδοτημένα, παρόλο που το token είναι έγκυρο. Αξίζει να το ξέρετε πριν ξοδέψετε ένα βράδυ πάνω του.

Κρατήστε τα σχόλια έξω από τη γέφυρα

Προωθήστε απαντήσεις. Ποτέ μην προωθείτε σχόλια. Οι μηχανικοί σας χρειάζονται ένα μέρος να πουν «αυτός ο πελάτης το έχει ρωτήσει τρεις φορές» χωρίς να εμφανιστεί στο portal κάποιου άλλου. Δοκιμάστε το με ένα σχόλιο που περιέχει έναν προφανή δείκτη και αναζητήστε τον στην πλευρά του συνεργάτη.

Ποτέ μην απορρίπτετε τίποτα σιωπηλά

Όταν το endpoint του συνεργάτη είναι εκτός λειτουργίας, οι εξερχόμενες παραδόσεις σας πρέπει να περιμένουν, όχι να εξαφανίζονται:

  1. Επαναλάβετε με exponential backoff και jitter, για περιορισμένο χρόνο.
  2. Μετά από αυτό, σταθμεύστε το event σε ένα dead-letter store μαζί με το σφάλμα.
  3. Σαρώστε το dead-letter store σε πρόγραμμα και ξαναπροσπαθήστε.
  4. Ειδοποιήστε έναν άνθρωπο αν κάτι μείνει σταθμευμένο περισσότερο απ' όσο αντέχετε.
  5. Καταγράψτε κάθε παράδοση με event ID, ticket, κωδικό κατάστασης και διάρκεια.

Κάντε την παράδοση σε worker, όχι μέσα στο ίδιο το αίτημα του συστήματος tickets. Αν ο συνεργάτης είναι αργός, το προσωπικό σας δεν πρέπει να περιμένει για να αποθηκευτεί μια σελίδα.

Δύο ακόμα αμυντικές συνήθειες. Επαληθεύστε τα συνημμένα με checksum και στις δύο πλευρές κατά τις δοκιμές. Το «μοιάζει ίδιο» δεν είναι δοκιμή. Και βάλτε όριο στο μέγεθος όσων δέχεστε: μια inline εικόνα επικολλημένη σε πλήρη ανάλυση μέσα σε ένα ticket μπορεί να είναι πολλές φορές μεγαλύτερη από το κείμενο γύρω της. Περικόψτε την ή αποθηκεύστε την ξεχωριστά στην είσοδο, αντί να αφήσετε ένα βαρύ ticket να επιβραδύνει κάθε σελίδα που το εμφανίζει.

Μια λίστα ελέγχου για τη δική σας γέφυρα

  • Κάθε event έχει ID που επιλέγει ο αποστολέας, και ο παραλήπτης καταγράφει τα IDs που εφάρμοσε.
  • Κάθε αίτημα είναι υπογεγραμμένο πάνω στο raw body, με timestamp και μυστικό ανά συνεργάτη.
  • Οι αλλαγές που προέρχονται από τον συνεργάτη σημειώνονται και δεν στέλνονται ποτέ πίσω.
  • Το token του συνεργάτη δουλεύει σε μία ουρά και απορρίπτεται σε κάθε άλλη, αποδεδειγμένα με δοκιμή.
  • Τα σχόλια δεν περνούν ποτέ. Οι απαντήσεις περνούν πάντα.
  • Υπάρχουν επαναλήψεις, dead letters, σαρώσεις και ειδοποιήσεις, και κάθε παράδοση καταγράφεται.
  • Τα συνημμένα επαληθεύονται με checksum. Το υπερμέγεθες περιεχόμενο αντιμετωπίζεται κατά την άφιξη.

Χτίζουμε και λειτουργούμε τέτοιες γέφυρες ανάμεσα στο Request Tracker και σε portals συνεργατών. Αν σχεδιάζετε μία, ή ζείτε με μία που δεν συμπεριφέρεται σωστά, θα χαρούμε να βοηθήσουμε.

Μιλήστε με έναν μηχανικό

Related

Σχετικά: Managed Request Tracker με γέφυρες συνεργατών · Managed team chat σε Mattermost

Δημοσιεύθηκε 9 Οκτωβρίου 2026.

Όλες οι σημειώσεις