Όταν χρειάζεστε ενημερώσεις πραγματικού χρόνου, η προεπιλογή το 2025 είναι διαχειριζόμενη υπηρεσία: Firebase Realtime Database, Supabase Realtime, Pusher, Ably ή φιλοξενούμενο επίπεδο WebSocket. Αποκρύπτουν την πολυπλοκότητα προώθησης ενημερώσεων στους συνδεδεμένους πελάτες. Λειτουργούν καλά — μέχρι να μη λειτουργούν.
Στο Atlas Verified αρχίσαμε με διαχειριζόμενη πλατφόρμα βάσης με συνδρομές πραγματικού χρόνου. Καθώς μεγαλώναμε, φτάσαμε στα όρια της αφαίρεσης. Περιορισμοί ομαδοποίησης συνδέσεων, προβλήματα αξιοπιστίας, ανάγκη χωρικών ερωτημάτων PostGIS και επιθυμία προσαρμοσμένων triggers μας ώθησαν στην ίδια κατεύθυνση: έλεγχο του επιπέδου βάσης.
Μεταφερθήκαμε σε αυτοφιλοξενούμενο PostgreSQL και δημιουργήσαμε σύστημα πραγματικού χρόνου με τον εγγενή μηχανισμό LISTEN/NOTIFY και συμβάντα που αποστέλλονται από τον διακομιστή. Η ανάρτηση καλύπτει την απόφαση, την αρχιτεκτονική και τα διδάγματα.
Γιατί αφήσαμε τη διαχειριζόμενη πλατφόρμα
Οι διαχειριζόμενες βάσεις προσφέρουν ελκυστική αξία: ταυτοποίηση, ασφάλεια ανά γραμμή, συνδρομές πραγματικού χρόνου, αυτόματα API και πίνακα — έτοιμα. Για πρώιμα προϊόντα είναι πραγματικά χρήσιμο. Δημιουργείτε λειτουργική εφαρμογή χωρίς ούτε μία γραμμή κώδικα backend.
Τα προβλήματα εμφανίζονται όταν ωριμάζει η εφαρμογή και οι απαιτήσεις αποκλίνουν από τις παραδοχές της πλατφόρμας.
Όρια συνδέσεων. Οι πλατφόρμες συχνά δρομολογούν συνδέσεις μέσω διαχειριστή ομάδας συνδέσεων με δικά του όρια. Με πολλές υπηρεσίες backend που χρειάζονται δικές τους ομάδες — διακομιστή API, σύστημα εργασιών παρασκηνίου, ακροατή πραγματικού χρόνου — εξαντλείτε γρήγορα τις διαθέσιμες συνδέσεις. Καταλήξαμε να παρακάμπτουμε τους περιορισμούς του διαχειριστή αντί να λύνουμε προβλήματα προϊόντος.
Αξιοπιστία πραγματικού χρόνου. Το σύστημα πραγματικού χρόνου της πλατφόρμας λειτουργούσε με καταγραφή αλλαγών δεδομένων στο Write-Ahead Log του PostgreSQL. Είναι κομψό αρχιτεκτονικά, αλλά έφερε αποτυχίες που δεν ελέγχαμε. Η αντιγραφή μέσω WAL έχει εγγενή καθυστέρηση. Η υπηρεσία έμενε κάποιες φορές πίσω, παρέχοντας παλιές ενημερώσεις. Η επανασύνδεση μετά από διακοπές δικτύου ήταν αναξιόπιστη. Η διάγνωση απαιτούσε πρόσβαση σε υποδομή που δεν ελέγχαμε.
Απαιτήσεις επεκτάσεων. Η εφαρμογή μας επεξεργάζεται εμπορικά έγγραφα με γεωγραφικά δεδομένα: λιμάνια, διαδρομές και θέσεις πλοίων. Το PostGIS — η χωρική επέκταση PostgreSQL — είναι απαραίτητο. Η πλατφόρμα το υποστήριζε, αλλά χρειαζόμασταν συγκεκριμένες εκδόσεις και ρυθμίσεις που δεν ήταν πάντα διαθέσιμες.
Προσαρμοσμένη λογική triggers. Χρειαζόμασταν triggers βάσης για συμβάντα ειδικά της εφαρμογής — όχι γενικές ειδοποιήσεις «άλλαξε γραμμή». Όταν αλλάζει η κατάσταση επεξεργασίας εγγράφου, πρέπει να ειδοποιείται ο συγκεκριμένος συνδεδεμένος πελάτης που το ανέβασε. Η πλατφόρμα λειτουργούσε σε επίπεδο πίνακα· εμείς χρειαζόμασταν επίπεδο οντότητας.
Η απόφαση μετάβασης δεν αφορούσε κόστος ή αρχές. Ήταν πρακτική: ξοδεύαμε περισσότερο χρόνο παρακάμπτοντας περιορισμούς από όσο θα χρειαζόταν να δημιουργήσουμε τις λειτουργίες μόνοι μας.
LISTEN/NOTIFY: Το κρυφό σύστημα δημοσίευσης και συνδρομής του PostgreSQL
Το PostgreSQL περιλαμβάνει εγγενή μηχανισμό δημοσίευσης και συνδρομής από την έκδοση 9.0 του 2010. Παρότι έχει ηλικία πάνω από 15 χρόνια, το LISTEN/NOTIFY χρησιμοποιείται εκπληκτικά λίγο. Οι περισσότεροι προγραμματιστές δεν γνωρίζουν ότι υπάρχει.
Ο μηχανισμός είναι απλός. Κάθε σύνδεση εκτελεί LISTEN σε ονομασμένο κανάλι για εγγραφή. Κάθε σύνδεση εκτελεί NOTIFY για μήνυμα σε όλους τους ακροατές του καναλιού. Το περιεχόμενο είναι συμβολοσειρά έως 8,000 bytes.
Τι κάνει το LISTEN/NOTIFY ισχυρό για εφαρμογές πραγματικού χρόνου:
Μηδενική πρόσθετη υποδομή. Δεν υπάρχει ξεχωριστός διαμεσολαβητής μηνυμάτων για εγκατάσταση, παρακολούθηση ή κλιμάκωση. Με PostgreSQL έχετε δημοσίευση και συνδρομή. Χωρίς Redis, RabbitMQ ή Kafka — για αυτή τη συγκεκριμένη χρήση.
Εγγυήσεις συναλλαγών. Τα μηνύματα NOTIFY παραδίδονται μόνο όταν οριστικοποιείται η περιβάλλουσα συναλλαγή. Αν εισάγει γραμμή και ειδοποίηση αλλά ανακληθεί, η ειδοποίηση δεν παραδίδεται ποτέ. Αυτή η ιδιότητα δεν παρέχεται από εξωτερικούς διαμεσολαβητές χωρίς προσεκτικό συντονισμό.
Ενσωμάτωση triggers. Τα triggers PostgreSQL μπορούν να καλούν pg_notify. Έτσι η βάση ειδοποιεί αυτόματα ακροατές όταν αλλάζουν συγκεκριμένα δεδομένα, χωρίς κώδικα εφαρμογής στη διαδρομή εγγραφής.
Οι περιορισμοί είναι πραγματικοί και πρέπει να γίνουν κατανοητοί:
Χωρίς μόνιμη αποθήκευση. Αν κανείς δεν ακούει όταν εκτελείται NOTIFY, το μήνυμα χάνεται. Δεν υπάρχει επανάληψη, ουρά ή εγγύηση παράδοσης σε αποσυνδεδεμένους πελάτες. Οι ακροατές χειρίζονται επανασύνδεση και ανάκτηση κατάστασης.
Μέγεθος περιεχομένου. Το όριο 8,000 bytes δεν επιτρέπει μεγάλα αντικείμενα μέσω NOTIFY. Στέλνουμε ελάχιστο περιεχόμενο — πίνακα, τύπο πράξης και πρωτεύον κλειδί — και ο πελάτης ανακτά τα πλήρη δεδομένα αν χρειάζεται.
Πεδίο μίας βάσης. Το LISTEN/NOTIFY λειτουργεί σε μία εγκατάσταση PostgreSQL. Δεν εκτείνεται σε αντίγραφα ή συστοιχίες. Αν όλες οι εγγραφές πηγαίνουν σε μία κύρια βάση, αυτό δεν είναι περιορισμός.
Η αρχιτεκτονική μας: Triggers, SSE και πολυπλεξία καναλιών
Έτσι συνδυάζονται τα μέρη.
Triggers βάσης δεδομένων εγκαθίστανται σε πίνακες όπου οι ενημερώσεις σε πραγματικό χρόνο έχουν σημασία: έγγραφα, μηνύματα, συνομιλίες, ειδοποιήσεις, επαληθεύσεις αποστολών και άλλους. Όταν εισάγεται, ενημερώνεται ή διαγράφεται μια γραμμή, το trigger ενεργοποιείται και καλεί το pg_notify με περιεχόμενο JSON που περιλαμβάνει το όνομα πίνακα, τον τύπο πράξης, το πρωτεύον κλειδί και τυχόν στήλες φίλτρων.
Μία αποκλειστική σύνδεση ακροατή στον διακομιστή API μας εκτελεί LISTEN στο κανάλι ειδοποιήσεων και λαμβάνει όλα τα συμβάντα των triggers. Πρόκειται για μια μακρόβια, αποκλειστική σύνδεση PostgreSQL, χωριστή από τη δεξαμενή συνδέσεων ερωτημάτων, που δεν κάνει τίποτε άλλο παρά να ακούει για ειδοποιήσεις.
Ένας δίαυλος συμβάντων λαμβάνει ειδοποιήσεις από τον ακροατή και τις δρομολογεί σε συνδεδεμένους πελάτες βάσει των συνδρομών τους. Ένας πελάτης εγγεγραμμένος σε έγγραφα που αντιστοιχούν σε συγκεκριμένο αναγνωριστικό αποστολής λαμβάνει μόνο ειδοποιήσεις για έγγραφα της συγκεκριμένης αποστολής.
Συμβάντα που αποστέλλονται από τον διακομιστή μεταφέρουν ειδοποιήσεις στο πρόγραμμα περιήγησης. Κάθε πελάτης ανοίγει μία σύνδεση SSE στον διακομιστή API, ορίζοντας κανάλια και φίλτρα. Ο δίαυλος αντιστοιχίζει εισερχόμενες ειδοποιήσεις με συνδρομές και προωθεί τα αντίστοιχα συμβάντα στις κατάλληλες συνδέσεις SSE.
Γιατί SSE αντί WebSockets
Παραμένουμε σταθερά ικανοποιημένοι με αυτή την απόφαση. Τα συμβάντα από τον διακομιστή είναι απλούστερα σε κάθε διάσταση σημαντική για τη χρήση μας.
Μονόδρομα από σχεδιασμό. Οι ανάγκες μας είναι από διακομιστή σε πελάτη: ενημέρωση όταν αλλάζουν δεδομένα. Ο πελάτης δεν στέλνει μηνύματα πραγματικού χρόνου — χρησιμοποιεί τυπικά αιτήματα HTTP. Το μονόδρομο μοντέλο SSE ταιριάζει στις απαιτήσεις μας.
Εγγενή στο HTTP. Οι συνδέσεις SSE είναι τυπικά HTTP αιτήματα με συγκεκριμένο τύπο περιεχομένου. Λειτουργούν σε κάθε proxy, εξισορροπητή φορτίου και CDN χωρίς ειδικές ρυθμίσεις. Τα WebSockets απαιτούν HTTP Upgrade, που μερικοί proxies δεν χειρίζονται σωστά και εταιρικά τείχη προστασίας αποκλείουν εντελώς.
Αυτόματη επανασύνδεση. Το EventSource API του προγράμματος περιήγησης χειρίζεται εγγενώς επανασύνδεση. Αν χαθεί η σύνδεση, επανασυνδέεται με εκθετική αύξηση διαστήματος. Με WebSockets υλοποιείτε τη λογική μόνοι σας — και κάνετε λάθος τουλάχιστον μία φορά.
Το ένα πεδίο όπου υπερέχουν WebSockets — αμφίδρομη επικοινωνία — δεν το χρειαζόμαστε. Για μηνύματα πραγματικού χρόνου από πελάτη σε διακομιστή, είναι η σωστή επιλογή. Για προώθηση ειδοποιήσεων από διακομιστή σε πελάτη, είναι το SSE.
Δημιουργία επιπέδου ερωτημάτων χωρίς ORM
Όταν αφήσαμε την πλατφόρμα, αφήσαμε και το αυτόματα παραγόμενο API ερωτημάτων. Χρειαζόμασταν επίπεδο πρόσβασης βάσης για τον διακομιστή API.
Οι τυπικές επιλογές είναι ORMs όπως Prisma, TypeORM ή Drizzle — ή απευθείας SQL με οδηγό. Επιλέξαμε τρίτο δρόμο: ελαφρύ δημιουργό ερωτημάτων με αλυσιδωτό API που παράγει και εκτελεί απευθείας SQL.
Οικείο API. Ο υπάρχων κώδικάς μας είχε γραφτεί για τη βιβλιοθήκη πελάτη της διαχειριζόμενης πλατφόρμας. Μια συμβατή διεπαφή API σήμαινε ελάχιστες αλλαγές στον κώδικα κατά τη μετάβαση. Τα ερωτήματα που προηγουμένως εκτελούνταν μέσω του πελάτη της πλατφόρμας μπορούσαν να εκτελούνται μέσω του δημιουργού ερωτημάτων μας με πανομοιότυπη σύνταξη.
Χωρίς μαγεία. Τα ORM παράγουν SQL που δεν μπορείτε να δείτε και μερικές φορές δεν μπορείτε να ελέγξετε. Όταν ένα ερώτημα είναι αργό, χρειάζεται να διαβάσετε την παραγόμενη SQL, που συχνά μοιάζει ελάχιστα με την κλήση ORM που την παρήγαγε. Ο δημιουργός ερωτημάτων μας παράγει προβλέψιμη SQL: κάθε αλυσιδωτά καλούμενη μέθοδος αντιστοιχεί σε συγκεκριμένη ρήτρα SQL και μπορείτε πάντα να εξετάσετε το παραγόμενο ερώτημα.
Ενσωματωμένες συνενώσεις. Τα εμπορικά δεδομένα είναι σχεσιακά. Η αποστολή έχει έγγραφα, αυτά κόμβους και αυτοί ελέγχους. Η δυνατότητα έκφρασης ένθετων επιλογών και λήψης ένθετων αποτελεσμάτων — χωρίς χειροκίνητο σύνθετο JOIN κάθε φορά — βελτιώνει σημαντικά την εμπειρία προγραμματιστή.
Το αποτέλεσμα είναι επίπεδο λίγων εκατοντάδων γραμμών κώδικα που καλύπτει τη μεγάλη πλειονότητα αλληλεπιδράσεων βάσης και παράγει SQL που πάντα διαβάζουμε και βελτιστοποιούμε.
Προϋπολογισμός ομάδων συνδέσεων
Σε αυτοφιλοξενούμενο PostgreSQL διαχειρίζεστε τον προϋπολογισμό συνδέσεων. Το max_connections ορίζει απόλυτο όριο και κάθε σύνδεση καταναλώνει μνήμη.
Ανακαλύψαμε ότι η αντιμετώπιση των συνδέσεων ως προϋπολογισμού, ενός πεπερασμένου πόρου που μοιράζεται μεταξύ υπηρεσιών, προλαμβάνει μια κατηγορία προβλημάτων που πλήττουν ομάδες οι οποίες δεν σχεδιάζουν γι’ αυτό.
Κάθε υπηρεσία δηλώνει μέγιστες συνδέσεις. Το άθροισμα πρέπει να μένει αρκετά κάτω από το όριο PostgreSQL, με περιθώριο για έκτακτες συνδέσεις — psql, σενάρια μετάβασης, εργαλεία παρακολούθησης. Ακούγεται προφανές, αλλά οι περισσότερες ομάδες το κάνουν μετά την εξάντληση στην παραγωγή. Με προϋπολογισμό εξαρχής αποφύγαμε εκείνη την ειδοποίηση στις 3 το πρωί.
Ασφαλείς μεταβάσεις σε κλίμακα
Η μετάβαση σε αυτοφιλοξενούμενο PostgreSQL σήμαινε μεταφορά εκατοντάδων πινάκων, συναρτήσεων, triggers και ευρετηρίων. Μεγαλύτερη πρόκληση ήταν ένα σύστημα μεταβάσεων που θα συνέχιζε να λειτουργεί καθώς εξελίσσεται η εφαρμογή.
Το αυτοματοποιημένο σύστημα λειτουργεί ως εξής: αρχεία μετάβασης ονομάζονται με χρονικές σημάνσεις σε ειδικό κατάλογο. Ο εκτελεστής σαρώνει τον κατάλογο, συγκρίνει με πίνακα εφαρμοσμένων μεταβάσεων και εκτελεί εκκρεμείς με τη σειρά. Κάθε μετάβαση είναι συναλλαγή — σε αποτυχία ανακαλείται καθαρά και αποκλείεται η ανάπτυξη μέχρι τη διόρθωση.
Ο εκτελεστής λειτουργεί αυτόματα σε κάθε ανάπτυξη πριν ξεκινήσει η εφαρμογή. Οι αλλαγές σχήματος εφαρμόζονται στη διαδικασία χωρίς χειροκίνητα βήματα. Ο προγραμματιστής δημιουργεί SQL αρχείο με χρονική σήμανση, το προωθεί στο αποθετήριο και η επόμενη ανάπτυξη το εφαρμόζει.
Βασικές αποφάσεις ασφάλειας: οι μεταβάσεις έχουν ελέγχους επαναληψιμότητας για να μην αποτυγχάνουν αν εφαρμόστηκαν μερικώς. Κάθε μετάβαση είναι αυτοτελές SQL σενάριο χωρίς ανθρώπινη παρέμβαση. Αν αποτύχει, ανακαλείται πλήρως η συναλλαγή — η εφαρμογή δεν ξεκινά με μερική αλλαγή σχήματος.
Τι κερδίσαμε
Η αυτοφιλοξενία PostgreSQL με προσαρμοσμένο πραγματικό χρόνο ήταν σημαντική επένδυση μηχανικής. Οι αποδόσεις όμως είναι ουσιαστικές:
Αξιοπιστία. Οι ειδοποιήσεις φτάνουν σε χιλιοστά του δευτερολέπτου από την αλλαγή. Χωρίς καθυστέρηση αντιγραφής WAL ή διακοπές τρίτων στην κρίσιμη διαδρομή.
Δυνατότητα διάγνωσης. Όταν κάτι πάει στραβά, μπορούμε να ιχνηλατήσουμε ολόκληρη τη διαδρομή: το trigger ενεργοποιείται, η ειδοποίηση αποστέλλεται, ο δίαυλος συμβάντων τη δρομολογεί και το SSE την παραδίδει. Κάθε βήμα βρίσκεται στον κώδικά μας, στα αρχεία καταγραφής μας και στην υποδομή μας.
Ευελιξία. Η προσθήκη ενημερώσεων σε πραγματικό χρόνο σε έναν νέο πίνακα απαιτεί μία μόνο μετάβαση SQL που εγκαθιστά ένα trigger. Χωρίς ρύθμιση πλατφόρμας, χωρίς πίνακα ελέγχου τρίτου παρόχου, χωρίς αίτημα υποστήριξης.
Απόδοση. Οι απευθείας συνδέσεις PostgreSQL με μια κατάλληλα ρυθμισμένη δεξαμενή συνδέσεων αποδίδουν καλύτερα από τις συνδέσεις που δρομολογούνται μέσω της υποδομής μιας διαχειριζόμενης πλατφόρμας. Ο χρόνος απόκρισης των ερωτημάτων μειώθηκε αισθητά μετά τη μετάβαση.
Το αντάλλαγμα είναι η λειτουργική ευθύνη. Διαχειριζόμαστε τη βάση δεδομένων, φροντίζουμε τα αντίγραφα ασφαλείας, παρακολουθούμε την αναπαραγωγή και σχεδιάζουμε τη δυναμικότητα. Για μια ομάδα εξοικειωμένη με τη λειτουργία του PostgreSQL, αυτό είναι διαχειρίσιμο. Για μια ομάδα που δεν είναι, οι αφαιρέσεις της διαχειριζόμενης πλατφόρμας μπορεί να αξίζουν τους περιορισμούς τους.
Το Atlas Verified λειτουργεί σε αυτοδιαχειριζόμενο PostgreSQL με προσαρμοσμένη υποδομή πραγματικού χρόνου, επεξεργαζόμενο καθημερινά χιλιάδες επαληθεύσεις εμπορικής συμμόρφωσης σε ολόκληρη την παγκόσμια εφοδιαστική αλυσίδα.
