Sobes.tech

/* Υπάρχουν δύο διακομιστές PostgreSQL: * PROD - διακομιστής OLTP, * STATS - διακομιστής για μακρές αναλυτικές ερωτήσεις. Στον τρέχοντα διακομιστή, στη βάση δεδομένων prod, υπάρχει ένας μεγάλος πίνακας (10Tb) με την ακόλουθη δομή: CREATE TABLE profiles( id SERIAL, data JSONB ) Στον πίνακα μπορεί να υπάρχουν "τρύπες", δηλαδή ορισμένα `id` μπορεί να λείπουν. Πρέπει να γράψετε ένα πρόγραμμα για να αντιγράψετε τον πίνακα profiles από το PROD στο STATS. Υποθέτουμε ότι θα χρησιμοποιηθούν οι ακόλουθες διεπαφές για εργασία με βάσεις δεδομένων: type Row []interface{} type Database interface { // η υλοποίηση της διεπαφής Database μπορεί να επανασυνδέσει // η κλήση SaveRows είναι idempotent io.Closer GetMaxID(ctx context.Context) (uint64, error) LoadRows(ctx context.Context, minID, maxID uint64) ([]Row, error) // [minID, maxID] SaveRows(ctx context.Context, rows []Row) error } func Connect(ctx context.Context, dbname string) (Database, error) // CopyTable // Αν full=false, συνεχίστε τη μεταφορά δεδομένων από το σημείο του προηγούμενου σφάλματος // Αν full=true, μεταφέρετε όλα τα δεδομένα func CopyTable(fromName string, toName string, full bool) error { // ... ο κώδικάς σας } Αν δοθεί η επιλογή `full=false`, το πρόγραμμα πρέπει να συνεχίσει τη μεταφορά δεδομένων από το σημείο του προηγούμενου σφάλματος. Αν `full=true`, πρέπει να μεταφέρει όλα τα δεδομένα. **Βασικό επίπεδο**: - σειριακή μεταφορά δεδομένων σε ένα ρεύμα - ανάκτηση μετά από σφάλμα (επιλογή `full=false`) Πρόσθετες πληροφορίες: - αν χρειάζεται, μπορείτε να επεκτείνετε τη διεπαφή προσθέτοντας δικές σας μεθόδους - αν χρειάζεται, μπορείτε να χρησιμοποιήσετε απευθείας το πακέτο **database/sql**

213

/* Δίνεται μια συμβολοσειρά χαρακτήρων. Βρείτε τον αριθμό των ζευγών δεικτών i και j (i <= j), μεταξύ των οποίων δεν υπάρχουν επαναλαμβανόμενοι χαρακτήρες. Για τη συμβολοσειρά "aba" η απάντηση είναι 5: μπορεί να μην είναι μόνο ASCII [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") */

213

/* Πρέπει να μεταφέρουμε δεδομένα από μια πηγή σε έναν καταναλωτή. Η πηγή παρέχει δεδομένα σε μικρές παρτίδες (~δέκα εγγραφές), ενώ ο καταναλωτής λειτουργεί πιο αποδοτικά με μεγάλες παρτίδες (~χίλιες εγγραφές). Ένα πραγματικό παράδειγμα είναι η μεταφορά δεδομένων από ουρές τύπου Kafka σε μια βάση δεδομένων Clickhouse. Πηγή: - Πρακτικά ατελείωτη. - Η πηγή ποτέ δεν επιστρέφει περισσότερα από MaxItems εγγραφές σε μια κλήση Next. - Σε μια "συνεδρία" (μία κλήση της λειτουργίας Pipe), η πηγή επιστρέφει νέα δεδομένα σε κάθε κλήση Next. - Ωστόσο, μετά από επανεκκίνηση, η πηγή ξεκινά από την προηγούμενη "επιβεβαιωμένη" θέση, που υποδεικνύεται από το cookie. Επομένως, κάθε τιμή cookie που επιστρέφεται από το Next, μετά την αποθήκευση των δεδομένων στον δέκτη, πρέπει να επιβεβαιωθεί με μια κλήση στο Commit, με την ίδια σειρά με αυτή που επιστράφηκε από το Next. Δέκτης: - Δεν μπορεί να επεξεργαστεί περισσότερα από MaxItems ταυτόχρονα. Βασικό επίπεδο: Απαιτείται η υλοποίηση της λειτουργίας func Pipe(p Producer, c Consumer) error που διαβάζει δεδομένα από την πηγή, τα ομαδοποιεί σε ένα buffer μεγέθους όχι μεγαλύτερο από MaxItems και τα αποθηκεύει στον δέκτη, μετά επιβεβαιώνει την πρόοδο στην πηγή. Πρόσθετη δυσκολία: Οι μέθοδοι Next, Process και Commit σχετίζονται με κλήσεις δικτύου και μπορεί να διαρκέσουν αρκετά. Για την επιτάχυνση της διαδικασίας, πρέπει να παραλληλοποιηθούν οι διαδικασίες ανάγνωσης, εγγραφής και επιβεβαίωσης προόδου. Έτσι, κατά τη διάρκεια των Process ή Commit, η ανάγνωση από την πηγή και η δημιουργία του νέου buffer συνεχίζονται. */ const MaxItems = 9999 type Producer interface { // Next επιστρέφει: // - ένα batch αντικειμένων προς επεξεργασία // - ένα cookie για επιβεβαίωση όταν η επεξεργασία ολοκληρωθεί // - ένα σφάλμα Next() (items []any, cookie int, err error) // Το Commit χρησιμοποιείται για να σηματοδοτήσει ένα batch δεδομένων ως επεξεργασμένο Commit(cookie int) error } type Consumer interface { Process(items []any) error } func Pipe(p Producer, c Consumer) error { // TODO }

213

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

212

Υπάρχει κάποια προθεσμία;

211

Πώς πραγματοποιήσατε τη δοκιμή και την επαλήθευση της ορθότητας των εκτελεσμένων αιτημάτων;

Junior — Middle
211

Ποια είναι τα διαφορετικά περιβάλλοντα local, dev, stage, prod;

211

Πώς ελέγχει το JWT την αυθεντικότητά του; Πώς διασφαλίζεται ότι το token δεν έχει παραποιηθεί;

210

Πες μου για βάσεις δεδομένων — με ποιες έχεις δουλέψει και με ποιες δουλεύεις τώρα;

210

/* Υπάρχει μια εφαρμογή με αρχιτεκτονική μικροϋπηρεσιών. Ένα μικροϋπηρεσία μπορεί να αποδοθεί μέσω μιας διεπαφής Backend. Για πρόσβαση σε ένα αντίγραφο της μικροϋπηρεσίας, μπορείτε να χρησιμοποιήσετε τον τύπο BackendImpl, ο οποίος έχει ήδη υλοποιηθεί. Κάθε μικροϋπηρεσία έχει αρκετές δεκάδες εκτελούμενα αντίγραφα, καθένα προσβάσιμο μέσω της δικής του διεύθυνσης addr. Ωστόσο, τα μεμονωμένα αντίγραφα της μικροϋπηρεσίας δεν είναι αξιόπιστα: μπορούν να πέσουν, να είναι μη προσβάσιμα ή υπερφορτωμένα. Επομένως, πρέπει να υλοποιήσετε τον τύπο Balancer, ο οποίος υλοποιεί επίσης το interface Backend και πραγματοποιεί φορτωτική ισορροπία από την πλευρά του πελάτη μεταξύ των αντιγράφων της μικροϋπηρεσίας, επιλέγοντας κάθε φορά το **ελάχιστα φορτωμένο** αντίγραφο. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr περιέχει ip:port ενός συγκεκριμένου αντιγράφου func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs περιέχουν τις διευθύνσεις όλων των ισορροπημένων αντιγράφων func NewBalancer(addrs []string) *Balancer { // TODO }

209

Έχεις εμπειρία στη διαχείριση ομάδας;

208

Τι προστίθεται στην απάντηση όταν το παράθυρο γίνεται έγκυρο;

207

Ποιος είναι ο περιορισμός ενός σημαφόρου και ενός καναλιού με buffer ως λύσεις για τον περιορισμό των παράλληλων αιτημάτων;

206

/* Υπάρχει μια εφαρμογή με αρχιτεκτονική μικροϋπηρεσιών. Μια μικροϋπηρεσία μπορεί να αποδοθεί μέσω της διεπαφής Backend. Για πρόσβαση σε ένα αντίγραφο της μικροϋπηρεσίας, μπορείτε να χρησιμοποιήσετε τον τύπο BackendImpl, ο οποίος έχει ήδη υλοποιηθεί. Κάθε μικροϋπηρεσία έχει αρκετές δεκάδες ενεργά αντίγραφα, καθένα προσβάσιμο μέσω της δικής του διεύθυνσης addr. Ωστόσο, τα μεμονωμένα αντίγραφα της μικροϋπηρεσίας δεν είναι αξιόπιστα: μπορεί να πέσουν, να είναι μη προσβάσιμα ή υπερφορτωμένα. Επομένως, πρέπει να υλοποιήσετε έναν τύπο Balancer, ο οποίος υλοποιεί επίσης το interface Backend και πραγματοποιεί κατανεμημένο φορτίο στην πλευρά του πελάτη μεταξύ των αντιγράφων της μικροϋπηρεσίας, επιλέγοντας κάθε φορά το **ελάχιστα φορτωμένο** αντίγραφο. */

206

Ποιος είναι ο μέγιστος αριθμός αιτημάτων ανά δευτερόλεπτο (RPS) που έχει επεξεργαστεί η υπηρεσία με τη μεγαλύτερη φόρτωση;

204

Μορφή εργασίας: απομακρυσμένα, στο γραφείο ή υβριδικά;

203

Πώς συγκρίνετε στοιχεία σε έναν χάρτη; Πώς ελέγχετε αν δύο χάρτες ταιριάζουν;

203

Τι είναι ένα JWT token; Από τι αποτελείται;

203

Περιγράψτε τον αλγόριθμο για την επίλυση του προβλήματος καταμέτρησης υποσυμβολοσειρών με όλα τα μοναδικά σύμβολα

202

Γράψτε μια εργασία στη γλώσσα Go — πρακτική ενότητα (προβλήματα από τη ζωή του προγραμματιστή, χωρίς αλγόριθμους): βελτιώστε ή διορθώστε μια υπηρεσία. Διάρκεια — 1,5 ώρες.

202
/11