Ιστολόγιο

Το πάτημα της Δημοσίευσης δύο φορές πλέον δημοσιεύει μία φορά

Τρία αιτήματα δημοσίευσης για την ίδια εφαρμογή έφτασαν στο API μας με διαφορά 84 χιλιοστών του δευτερολέπτου, και το καθένα ξεκίνησε ένα πλήρες deploy της εφαρμογής. Ανταγωνίστηκαν μεταξύ τους για το ίδιο hosting, αρκετές από τις κυκλοφορίες που προέκυψαν απέτυχαν επειδή κάποια άλλη είχε φτάσει πρώτη, και το μόνο ορατό σημάδι ήταν ένα email «η κυκλοφορία απέτυχε» — η εφαρμογή τελικά λειτουργούσε κανονικά. Τώρα, μια δημοσίευση που φτάνει ενώ άλλη τρέχει για την ίδια εφαρμογή δεν ξεκινά δεύτερο build. Η βάση δεδομένων αποφασίζει ποιο αίτημα κερδίζει, και τα υπόλοιπα λαμβάνουν την αλήθεια: μια δημοσίευση τρέχει ήδη.

Τι συνέβη

Όταν δημοσιεύετε μια εφαρμογή στο Mythex, το API καταγράφει μια ανάπτυξη για αυτήν και ζητά από τον orchestrator μας — την υπηρεσία που τρέχει τα builds — να την χτίσει και να την κυκλοφορήσει. Η δημοσίευση μπορεί να ξεκινήσει από το κουμπί Δημοσίευση, από άλλη καρτέλα του browser, ή από τον agent όταν του το ζητήσετε. Ένα διπλό κλικ, δύο καρτέλες ή ένα αίτημα του agent που φτάνει δύο φορές μπορούν να παράγουν περισσότερες από μία δημοσιεύσεις για την ίδια εφαρμογή σχεδόν την ίδια στιγμή.

Σε αυτή την περίπτωση, έφτασαν τρεις μέσα σε 84 ms. Καθεμία ξεκίνησε ένα πλήρες deploy. Στην πλατφόρμα hosting, οι κυκλοφορίες για την ίδια εφαρμογή δημιουργήθηκαν τον ίδιο δευτερόλεπτο, και τρεις από αυτές απέτυχαν επειδή οι άλλες βρίσκονταν ήδη σε εξέλιξη. Μια μεταγενέστερη κυκλοφορία κόλλησε, και η εφαρμογή λειτούργησε αργότερα.

Αυτό έκανε το πρόβλημα εύκολο να χαθεί. Ο χρήστης έβλεπε μία εφαρμογή που δούλευε. Οι αποτυχίες φαίνονταν μόνο σε email «αποτυχίας κυκλοφορίας».

Γιατί τίποτα δεν το σταμάτησε

Ο κώδικας δημοσίευσης είχε ήδη ένα βήμα που κοιτούσε τις αναπτύξεις σε εξέλιξη για το project. Αλλά αυτό που έκανε ήταν να σημαδεύει κάθε ανάπτυξη σε εξέλιξη ως αποτυχημένη πριν ξεκινήσει — «αντικαταστάθηκε από νέα δημοσίευση». Προοριζόταν να καθαρίζει builds που έμειναν πίσω από μια διεργασία που κράσαρε.

Στην πράξη, μια δεύτερη δημοσίευση που έφτανε στη μέση ενός build δήλωνε πεθαμένη την πρώτη και ξεκινούσε τη δική της. Και οι δύο έτρεχαν ως το τέλος απέναντι στην ίδια εφαρμογή. Δεν υπήρχε κλείδωμα· υπήρχε ένα βήμα που ενεργά άνοιγε τον δρόμο για τον αγώνα.

Υπήρχε και δεύτερο, πιο περίεργο σύμπτωμα για την πρώτη δημοσίευση μιας εφαρμογής. Χωρίς ακόμα εγγραφή ανάπτυξης για επαναχρησιμοποίηση, δύο ταυτόχρονες πρώτες δημοσιεύσεις προσπαθούσαν και οι δύο να εισάγουν μία. Η βάση έχει μοναδικό ευρετήριο στο όνομα συνδέσμου της εφαρμογής (το slug της, όπως your-app στο your-app.mythex.ai), οπότε η δεύτερη εισαγωγή απέτυχε. Το API το μετέφραζε σε «αυτό το όνομα συνδέσμου είναι πιασμένο» — και συμβούλευε τον χρήστη να μετονομάσει μια εφαρμογή που, εκείνη τη στιγμή, δημοσιευόταν κανονικά με αυτό το όνομα.

Η διόρθωση: η «κατοχή» είναι το κλείδωμα

Η κατοχή της εγγραφής ανάπτυξης γίνεται πλέον με μία μόνο υπό συνθήκη ενημέρωση της βάσης: πάρε αυτή τη γραμμή και σημείωσέ την ως «σε build» μόνο αν δεν την έχει ήδη κάποιος που δημοσιεύει. Επειδή είναι μία εντολή, η βάση διευθετεί αιτήματα που φτάνουν μέσα στο ίδιο χιλιοστό. Ακριβώς ένα από αυτά αλλάζει τη γραμμή. Τα υπόλοιπα βλέπουν ότι άλλαξαν μηδέν γραμμές, σταματούν, και ποτέ δεν φτάνουν στον orchestrator.

Ένα κλείδωμα χρειάζεται επίσης τρόπο να απελευθερώνεται όταν ο κάτοχός του πεθαίνει. Αν το API κάνει επανεκκίνηση στη μέση μιας δημοσίευσης, η γραμμή θα έμενε για πάντα σημειωμένη ως «σε build» και η εφαρμογή δεν θα μπορούσε να δημοσιευτεί ξανά. Γι' αυτό μια γραμμή της οποίας ο ιδιοκτήτης έχει σταματήσει να στέλνει heartbeats μπορεί να ξαναπιαστεί. Η εκτελούμενη δημοσίευση ενημερώνει τη γραμμή κάθε 30 δευτερόλεπτα, και πέντε λεπτά σιωπής θεωρούνται νεκρά. Αυτό το όριο προέρχεται από το heartbeat, όχι από μια εκτίμηση του πόσο διαρκεί ένα build. Το build ενός δικού σας Dockerfile μπορεί να τρέχει νόμιμα πολλή ώρα, και ένα αργό build που εξακολουθεί να στέλνει heartbeat κρατά την κατοχή του.

Το βήμα που σημάδευε κάθε ανάπτυξη σε εξέλιξη ως αποτυχημένη δεν τρέχει πλέον πριν από μια δημοσίευση. Καθαρίζονται μόνο οι γραμμές που άφησε πίσω της μια νεκρή διεργασία.

Η αλήθεια στον χαμένο

Το αίτημα που χάνει λαμβάνει πλέον μια σαφή απάντηση: publish_in_progress, μαζί με το ID της ανάπτυξης που τρέχει. Κάθε σημείο που μπορεί να ξεκινήσει δημοσίευση το χειρίζεται:

  • Το workspace ακολουθεί το build που ήδη τρέχει αντί να δείχνει σφάλμα πάνω από αυτό.
  • Ο agent ενημερώνεται ότι μια δημοσίευση αυτής της εφαρμογής ήδη τρέχει και να περιμένει, αντί να του λέγεται ότι κάτι απέτυχε — κάτι που παλαιότερα μπορούσε να τον στείλει να «διορθώσει» ένα Dockerfile και να δοκιμάσει ξανά.
  • Μια σύγκρουση πρώτης δημοσίευσης στο όνομα συνδέσμου ελέγχεται: αν η γραμμή που κρατά το όνομα είναι η δική αυτού του project που δημοσιεύεται αυτή τη στιγμή, η απάντηση είναι «ήδη δημοσιεύεται», όχι «αυτό το όνομα είναι πιασμένο». Μια πραγματική σύγκρουση με άλλο project εξακολουθεί να παίρνει το μήνυμα για μετονομασία.

Πώς το ελέγξαμε

Γράψαμε ένα σενάριο αναπαραγωγής που τρέχει τρεις δημοσιεύσεις της ίδιας εφαρμογής ταυτόχρονα σε πραγματική βάση δεδομένων και με ψεύτικο orchestrator, και μετρά πόσα deploys ζητήθηκαν. Καλύπτει μια πρώτη δημοσίευση και μια νέα δημοσίευση μιας ζωντανής εφαρμογής. Πριν από τη διόρθωση, μετρούσε δύο deploys. Μετά, ένα.

Το σενάριο καλύπτει και τη διαφυγή: σημαδεύει τη γραμμή της εφαρμογής ως «σε build» χωρίς heartbeat για δέκα λεπτά, κάνει νέα δημοσίευση, και ελέγχει ότι η νέα δημοσίευση αναλαμβάνει τη γραμμή και τρέχει.

Τα unit tests κρατούν το κλείδωμα στα δύο πράγματα που πρέπει να κάνει ένα κλείδωμα: από τρεις ταυτόχρονες νέες δημοσιεύσεις, περνά ακριβώς μία και οι άλλες δύο μαθαίνουν ότι μια δημοσίευση τρέχει· και ένα build του οποίου ο ιδιοκτήτης πέθανε μπορεί να ξαναπιαστεί. Ένα άλλο τεστ ελέγχει ότι μια νέα προσπάθεια δεν κουβαλά το μήνυμα σφάλματος της προηγούμενης.

Τι κρατήσαμε από αυτό

  • Ο κώδικας καθαρισμού μπορεί να είναι κρυφός αγώνας. «Αποτύχε ό,τι τρέχει, και μετά ξεκίνα» μοιάζει με υγιεινή. Με δύο καλούντες, είναι ο καθένας να ακυρώνει τον άλλο.
  • Αφήστε τη βάση να κρίνει. Μια ανάγνωση ακολουθούμενη από εγγραφή αφήνει κενό για άλλο αίτημα. Μία υπό συνθήκη ενημέρωση δεν αφήνει.
  • Κάθε κλείδωμα χρειάζεται λήξη συνδεδεμένη με τη ζωή του κατόχου. Χωρίς αυτήν, μια κατάρρευση γίνεται κολλημένη εφαρμογή.
  • Τα μηνύματα σφάλματος είναι οδηγίες. «Αυτό το όνομα είναι πιασμένο» έλεγε στους ανθρώπους να κάνουν το λάθος πράγμα. Το σωστό μήνυμα ήταν απλούστερο και αληθινό.

Περισσότερα για τη δημοσίευση στα docs: πώς να δημοσιεύσετε. Και μια σχετική διόρθωση: η δημοσιευμένη εφαρμογή σας μένει σε λειτουργία ενώ την ξαναδημοσιεύετε.

Mythex · Pricing · Χτίστε οτιδήποτε με AI · Πρότυπα ιστοσελίδων και εφαρμογών · Το Mythex σε σύγκριση · Γλωσσάρι δημιουργίας εφαρμογών με AI · Δωρεάν εργαλεία δημιουργίας εφαρμογών με AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)