Steven
Steven5 λεπτά ανάγνωσης

Η μέρα που η εφαρμογή μας έκανε DDoS στον εαυτό της

Ένα backlog από εκκρεμή uploads, που απελευθερώθηκε όλο μαζί κατά την εκκίνηση, μετέτρεψε κάθε GeekBye client σε μια μικρή επίθεση denial-of-service ενάντια στους ίδιους μας τους servers. Η διόρθωση — και η σκάλα connection-liveness που μας ανάγκασε να χτίσουμε — είναι ένα από τα πιο χρήσιμα πράγματα που μας δίδαξε το v2.

Αξιοπιστία
Δίκτυα
Engineering
Εκδόσεις GeekBye
Η μέρα που η εφαρμογή μας έκανε DDoS στον εαυτό της

Κάθε μηχανικός κατανεμημένων συστημάτων κάποια στιγμή συναντά το thundering herd (το κοπάδι που ορμά): μια μάζα από clients που όλοι κάνουν το ίδιο πράγμα την ίδια στιγμή, και ο κοινός πόρος πίσω τους λυγίζει. Συνήθως είναι οι clients κάποιου άλλου. Στο GeekBye v2.0.1 ήταν οι δικοί μας, και ο επιτιθέμενος ήταν η ίδια μας η εφαρμογή.

Πώς ένας notetaker επιτίθεται στον εαυτό του

Το GeekBye μπορεί να ανεβάζει ηχογραφήσεις στο Google Drive σου. Αν μια ηχογράφηση δεν μπορεί να ανέβει αμέσως — ήσουν offline, η εφαρμογή έκλεισε, το Drive κόμπιασε — μπαίνει σε ένα backlog για να ξαναδοκιμάσει αργότερα. Λογικό.

Η αστοχία ήταν στο πότε συνέβαινε αυτό το «αργότερα». Στην επόμενη εκκίνηση, η εφαρμογή προσπαθούσε να αδειάσει ολόκληρο το backlog όλο μαζί. Ένας χρήστης με μια βδομάδα εκκρεμείς ηχογραφήσεις γινόταν μια ριπή από ταυτόχρονα upload requests τη στιγμή που άνοιγε την εφαρμογή. Πολλαπλασίασε επί κάθε χρήστη που ανοίγει την εφαρμογή το πρωί, και ο backend μας καλούνταν να κάνει authenticate, rate-check και να επεξεργαστεί έναν τοίχο κίνησης μέσα στα ίδια λίγα δευτερόλεπτα — από clients που όλοι, τεχνικά, συμπεριφέρονταν σωστά.

Ο backend μας έκανε το σωστό και rate-limit-άρισε τον κατακλυσμό. Και εκεί ξεκίνησε η ζημιά δεύτερης τάξης. Μια απόκριση rate-limit είναι ένα HTTP 429, και το 429 μοιάζει πολύ με άλλες αστοχίες αν δεν προσέχεις:

  • Το profile fetch στην εκκίνηση πήρε ένα 429 και η εφαρμογή το αντιμετώπισε ως auth failed — κάνοντας για λίγο logout χρήστες από μια απολύτως έγκυρη session.
  • Η σύνδεση transcription πήρε ένα γενικό 429 και έδειξε ένα upgrade prompt τύπου audio-limit-reached — λέγοντας σε πληρωμένους χρήστες ότι χτύπησαν ένα quota που δεν είχαν χτυπήσει.

Έτσι μία ρίζα — ένα αρρύθμιστο backlog — παρήγαγε τρία ορατά συμπτώματα: καταπόνηση server, ψεύτικα logouts και ψεύτικα upsells. Το κλασικό σχήμα ενός self-DoS: το φορτίο είναι κακό, αλλά η παρερμηνεία των συμπτωμάτων του φορτίου είναι αυτό που πραγματικά νιώθουν οι χρήστες.

Η διόρθωση, σε δύο στρώματα

Σταμάτα την ποδοπατημή. Το upload backlog τώρα ρυθμίζεται — τα requests απλώνονται στον χρόνο με backoff, συν ένα cooldown μετά από κάθε 429, ώστε ο client να απομακρύνεται από έναν καταπονημένο server αντί να πιέζει πάνω του πιο σκληρά. Ένα thundering herd γίνεται μια τακτική ουρά.

Σταμάτα την παρερμηνεία. Ένα παροδικό 429 ή 5xx κατά την εκκίνηση δεν σε κάνει πια logout — ο client ξεχωρίζει το «ο server είναι στιγμιαία απασχολημένος» από το «η session σου είναι άκυρη». Και ένα γενικό 429 rate-limit στη διαδρομή του transcription δεν μεταμφιέζεται πια σε σφάλμα audio quota· μόνο μια γνήσια απόκριση quota δείχνει το upgrade prompt. Το μάθημα που έμεινε: ένας κωδικός σφάλματος δεν είναι μια σημασία σφάλματος. Το 429 σημαίνει «κόψε ταχύτητα», όχι «είσαι μη εξουσιοδοτημένος» και όχι «ξέμεινες από quota» — και ο client πρέπει να ξέρει τη διαφορά.

Η σκάλα liveness που ακολούθησε

Το ρύθμισμα του κοπαδιού ανέδειξε ένα πιο ήσυχο πρόβλημα: όταν μια σύνδεση όντως χαλούσε υπό φορτίο, πόσο γρήγορα το προσέχαμε; Πιο αργά απ' όσο θέλαμε. Έτσι το v2.0.4 έχτισε μια σκάλα connection-liveness για μεταγραφή σε πραγματικό χρόνο:

  • Heartbeats — το transcription socket δέχεται ping σε σταθερό διάστημα, ώστε μια σιωπηλά νεκρή σύνδεση να εντοπίζεται σε δευτερόλεπτα αντί να περιμένει το επόμενο κομμάτι audio να αποτύχει.
  • Ένα reconnect kick όταν επανέρχεται το δίκτυο — όταν το OS αναφέρει ότι το δίκτυο επέστρεψε, η εφαρμογή προληπτικά ξαναστήνει τη σύνδεση αντί να περιμένει να σκοντάψει στην ανακάλυψη.
  • Liveness μέσω control ping/pong — ένα round-trip σε επίπεδο εφαρμογής που επιβεβαιώνει όχι απλώς ότι «το socket είναι ανοιχτό» αλλά ότι «η άλλη άκρη όντως απαντά».
  • Μια πύλη reconnect που ανήκει στο service — ένα μέρος αποφασίζει αν θα ξανασυνδεθεί, αντί να τρέχουν διάφορα μέρη της εφαρμογής να το κάνουν και να πατά το ένα το άλλο.

Τίποτα από αυτά δεν είναι εντυπωσιακό. Όλα τους είναι ο λόγος που μια v2 session νιώθεις ότι απλώς... μένει συνδεδεμένη.

Συνέβη ξανά αυτή τη βδομάδα — ένα σκαλί πιο κάτω

Να το κομμάτι που το κάνει κάτι παραπάνω από πολεμική ιστορία. Η ίδια κατηγορία αστοχίας ξαναφάνηκε πριν λίγες μέρες, ένα επίπεδο κάτω από εμάς. Ένας μόνο client πυροδότησε 21 εκκινήσεις session μεταγραφής σε λιγότερο από 400 milliseconds — και σκάλωσε στο όριο επιπέδου λογαριασμού του speech provider μας, των 15 concurrent requests. Μια ποδοπατημή ενάντια σε κοινή υποδομή, ακριβώς όπως το upload backlog, απλώς στραμμένη σε ένα dependency αντί για τον δικό μας backend.

Το σχήμα είναι πανομοιότυπο, και η διόρθωση επίσης: ο client που ποδοπατά χρειάζεται ένα single-flight guard ώστε να μην μπορεί να πυροδοτήσει είκοσι εκκινήσεις για μία πρόθεση, και ο κοινός πόρος χρειάζεται ένα cap ανά χρήστη ώστε ένας client να μην μπορεί να καταναλώσει όλο το pool. Έχουμε χτίσει την client-side εκδοχή αυτού και πριν — ο upload pacer είναι αυτό το μοτίβο. Τώρα το εφαρμόζουμε στις εκκινήσεις session. Το thundering herd δεν είναι ένα bug που διορθώνεις μια φορά· είναι ένα σχήμα που μαθαίνεις να αναγνωρίζεις παντού όπου clients συναντούν ένα κοινό όριο.

Τρία πράγματα για να κρατήσεις

  1. Οι δικοί σου clients είναι ένα load test που δεν προγραμμάτισες. Οτιδήποτε κάνει batch-στην-εκκίνηση, retry-στο-άνοιγμα ή reconnect-στο-ξύπνημα είναι ένα thundering herd που περιμένει αρκετούς χρήστες. Ρύθμισέ του τον ρυθμό πριν τους αποκτήσεις.
  2. Ξεχώρισε τον κωδικό από τη σημασία. Το 429 είναι το πιο παρεξηγημένο status που υπάρχει. Το «κόψε ταχύτητα» δεν είναι «κάνε logout» και δεν είναι «πλήρωσέ μας». Δρομολόγησε κάθε αστοχία σε αυτό που όντως σημαίνει.
  3. Το liveness είναι σκάλα, όχι flag. Το «είναι ζωντανή η σύνδεση;» έχει πολλές ειλικρινείς απαντήσεις σε διαφορετικά στρώματα — socket ανοιχτό, bytes να ρέουν, η άλλη άκρη να απαντά, δίκτυο παρόν. Μια εύρωστη εφαρμογή ελέγχει περισσότερα από ένα.

Αυτή είναι η μη εντυπωσιακή μηχανική κάτω από την ηρεμία του GeekBye v2. Για τα features αξιοπιστίας που ενεργοποίησε, δες γιατί ο AI notetaker σου σταματά σε κακό Wi-Fi και live μεταγραφή όταν το firewall μπλοκάρει τα WebSockets (v2.0.8). Για τις γειτονικές εκδόσεις αυτής της σειράς, γιατί ο AI notetaker σου σταματά την ηχογράφηση στη μέση της σύσκεψης (v2.0.9).

Σχετικά Άρθρα

Live μεταγραφή όταν το firewall μπλοκάρει τα WebSockets
Steven
Steven5 λεπτά ανάγνωσης

Live μεταγραφή όταν το firewall μπλοκάρει τα WebSockets

Τα εταιρικά δίκτυα λατρεύουν να επιτρέπουν το HTTPS και να σκοτώνουν σιωπηλά τα WebSocket upgrades. Αυτό σπάει αθόρυβα τη μεταγραφή σε πραγματικό χρόνο. Το GeekBye v2.0.8 πέφτει αυτόματα σε ένα καθαρό HTTPS transport — και η κυκλοφορία του ξέθαψε ένα bug που θα έκανε ολόκληρο το feature άχρηστο.

Μεταγραφή
Δίκτυα
Engineering
Γιατί ο AI notetaker σου σταματά την ηχογράφηση στη μέση της σύσκεψης
Steven
Steven6 λεπτά ανάγνωσης

Γιατί ο AI notetaker σου σταματά την ηχογράφηση στη μέση της σύσκεψης

Η ίδια μας η εφαρμογή τερμάτισε δύο από τις συσκέψεις μας ενώ η άλλη πλευρά ήταν στη μέση της πρότασης. Το ιχνηλατικό ίχνος οδήγησε σε ένα καλοπροαίρετο idle timer που δεν μπορούσε να ακούσει κανέναν εκτός από σένα — και σε ένα δεύτερο bug που μπορούσε να κλειδώσει ολόκληρο το desktop σου. Και τα δύο διορθωμένα στο GeekBye v2.0.9.

Αξιοπιστία
Συσκέψεις
Engineering
Γιατί η μεταγραφή με AI παρακούει τους τεχνικούς όρους (και πώς το διορθώσαμε)
Steven
Steven7 λεπτά ανάγνωσης

Γιατί η μεταγραφή με AI παρακούει τους τεχνικούς όρους (και πώς το διορθώσαμε)

Μια live συνεδρία άκουσε το "what is the pointer in C++" ως "what is the point in life". Ιδού το ίχνος από εκείνο το transcript μέχρι το GeekBye v2.0.11 — keyterm biasing, μια κούρσα χρονισμού που έριχνε τη σύνδεση, και η μέρα που το ίδιο μας το fix γύρισε μπούμερανγκ.

Μεταγραφή
Αξιοπιστία
Engineering