Raccontaci del compito più difficile e interessante che hai affrontato, in particolare riguardo all'esperienza architettonica.
Golang
Potresti indicare il tuo attuale livello di reddito?
Attualmente vivi a Mosca? Qual città stai considerando? Consideri un formato di lavoro ibrido? A che punto sei della ricerca?
Hai altri processi di colloquio attivi?
Come vengono integrate e visualizzate queste metriche in Grafana?
Qual è stato la composizione del team con cui hai lavorato l'ultima volta?
Progettazione di un sistema di messaggistica scalabile che supporti 150 milioni di utenti, 75 milioni di DAU, 225 milioni di MAU, 1,2 milioni di letture / 300k scritture QPS, 5 milioni di utenti simultanei, 60 PB di nuovi dati all'anno, crescita del 30% annuo, P99 <200 ms per la lettura, <300 ms per la scrittura, SLA 99,95%. CONTESTO È necessario progettare un sistema di messaggistica distribuito, simile a WhatsApp, che supporti chat 1:1 e di gruppo, garantisca la consegna dei messaggi, visualizzi gli stati online degli utenti e trasmetta file multimediali (foto, video, audio). Il sistema deve garantire alta disponibilità e bassa latenza, supportare alto parallelismo e scalare a livello globale. REQUISITI FUNZIONALI - Supporto per chat personali (1:1) e di gruppo con possibilità di aggiungere/rimuovere partecipanti - Invio e ricezione di messaggi di testo e file multimediali Requisiti non funzionali: - Nessuna implementazione esplicita di crittografia end-to-end a livello di servizi o client, tranne un'annotazione generale. - Mancanza di una descrizione chiara dello sharding e della replica dei database per chat_id o user_id per scalabilità e tolleranza ai guasti. - Nessun componente o meccanismo esplicito per gestire la sincronizzazione offline dei messaggi e le ricevute di consegna. - Non è chiaro come venga effettuato il bilanciamento del carico tra database e servizi, specialmente durante i picchi di traffico. **Punti critici da considerare:** (Il diagramma mostra un'architettura con Load Balancer, API Gateway, Message Queue, Service, Cache, Database, Object Storage e CDN)
Hai un GitHub o LinkedIn attivi?
Racconta brevemente cosa hai fatto nelle tue precedenti esperienze lavorative e quali funzionalità hai implementato.
Progettazione di un sistema di messaggistica scalabile che supporti 150 milioni di utenti, 75 milioni di DAU, 225 milioni di MAU, 1,2 milioni di QPS di lettura / 300k di scrittura in picco, 5 milioni di utenti simultanei, 60 PB di nuovi dati all'anno, crescita del 30% annuo, SLA del 99,95%, p99 <200 ms per la lettura, <300 ms per la scrittura. CONTESTO È necessario progettare un sistema di messaggistica distribuito, simile a WhatsApp, che supporti chat 1:1 e di gruppo, garantisca la consegna dei messaggi, mostri gli stati online degli utenti e consenta il trasferimento di file multimediali (foto, video, audio). Il sistema deve garantire alta disponibilità e bassa latenza, supportare un alto parallelismo e scalare a livello globale. REQUISITI FUNZIONALI - Supporto per chat personali (1:1) e di gruppo con possibilità di aggiungere/rimuovere partecipanti - Invio e ricezione di messaggi di testo e file multimediali Non si vede una implementazione chiara del meccanismo di crittografia end-to-end a livello di servizi o clienti, a parte una annotazione generale. - Mancanza di una descrizione esplicita di sharding e replica dei database per chat_id o user_id per scalabilità e tolleranza ai guasti. - Non c'è un componente o meccanismo chiaro per gestire la sincronizzazione offline dei messaggi e delle ricevute di consegna. - Non è chiaro come venga effettuato il bilanciamento del carico tra database e servizi, specialmente in caso di picchi di carico. **Punti critici da considerare:**
Come sono strutturati i test nel team — chi cosa scrive, quale copertura, ci sono E2E?
/* Dobbiamo trasferire dati da una sorgente a un consumatore. La sorgente fornisce i dati in piccoli batch (~dieci record), mentre il consumatore funziona meglio con batch più grandi (~mille record). Un esempio reale è il trasferimento di dati da code di tipo Kafka a un database Clickhouse. Sorgente: - Quasi infinita. - La sorgente non restituisce mai più di MaxItems record in una chiamata a Next. - In una "sessione" (una chiamata alla funzione Pipe), la sorgente restituisce dati nuovi ad ogni chiamata a Next. - Tuttavia, dopo un riavvio, la sorgente ricomincia dalla posizione "confermata" precedente, indicata da cookie. Pertanto, *ogni* valore di cookie restituito da Next, dopo aver salvato i dati nel ricevitore, deve essere confermato con la chiamata a Commit, nello stesso ordine in cui sono stati restituiti da Next. Ricevitore: - Non può elaborare più di MaxItems alla volta. Livello base: È necessario implementare la funzione func Pipe(p Producer, c Consumer) error che legge i dati dalla sorgente, li raggruppa in un buffer di dimensione non superiore a MaxItems e li salva nel ricevitore, dopodiché conferma il progresso nella sorgente. Difficoltà: I metodi Next, Process e Commit sono legati a chiamate di rete e possono richiedere molto tempo. Per accelerare il processo, è necessario parallelizzare i processi di lettura, scrittura e conferma del progresso. In modo che, durante Process o Commit, la lettura dalla sorgente e la formazione del nuovo buffer continuino. */ const MaxItems = 9999 type Producer interface { // Next restituisce: // - un batch di elementi da elaborare // - una cookie da confermare quando l'elaborazione è completata // - un errore Next() (items []any, cookie int, err error) // Commit viene usato per marcare un batch di dati come processato Commit(cookie int) error } type Consumer interface { Process(items []any) error } func Pipe(p Producer, c Consumer) error { // TODO }
Come si può migliorare l'efficienza della ricerca di elementi in una struttura dati Map?
/* Ci sono due server PostgreSQL: * PROD - server OLTP, * STATS - server per query analitiche lunghe. Sul server attuale, nel database prod, c'è una grande tabella (10Tb) con la seguente struttura: CREATE TABLE profiles( id SERIAL, data JSONB ) Nella tabella possono esserci "buchi", cioè alcuni `id` possono essere mancanti. È necessario scrivere un programma per copiare la tabella profiles da PROD a STATS. Si presume che si useranno le seguenti interfacce per lavorare con i database: type Row []interface{} type Database interface { // l'implementazione dell'interfaccia Database può ristabilire le connessioni // la chiamata a SaveRows è idempotente 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 // Se full=false, continuare il trasferimento dei dati dal punto dell'errore precedente // Se full=true, trasferire tutti i dati func CopyTable(fromName string, toName string, full bool) error { // ... il tuo codice } Se viene passato l'opzione `full=false`, il programma deve continuare il trasferimento dei dati dal punto dell'errore precedente. Se `full=true`, deve trasferire tutti i dati. **Livello base**: - trasferimento sequenziale dei dati in un solo flusso - recupero dopo errore (opzione `full=false`) Informazioni aggiuntive: - se necessario, puoi estendere l'interfaccia aggiungendo i tuoi metodi - se necessario, puoi usare direttamente il pacchetto **database/sql**
/* Una stringa di caratteri viene data. Trovare il numero di coppie di indici i e j (i <= j), tra cui non ci sono caratteri ripetuti. Per la stringa "aba" la risposta è 5: possono non essere solo ASCII [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") */
Qual è il problema con l'attuale implementazione dell'aggiornamento della mappa nel worker (tasksRes[t.id][task{...}])?
Spiega il principio di inversione delle dipendenze e perché la chiamata diretta ai metodi del repository da un caso d'uso viola il principio SOLID.
Data una stringa di caratteri. Trova il numero di coppie di indici i e j (i <= j), tra cui, inclusi, non ci sono caratteri ripetuti. Per la stringa "aba" la risposta è 5: [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") Per la stringa "abcb" la risposta è ?: aba 3 + 2 = 5 abcb 4 (a, b, c, d) + 1 (ab) + 1 (bc) + 1 (cb) + 1 (abc) = 8
/* Esiste un'applicazione con architettura a microservizi. Un microservizio può essere astratto tramite un'interfaccia Backend. Per accedere a un'istanza del microservizio, si può usare il tipo BackendImpl, che è già implementato. Ogni microservizio ha diverse decine di istanze in esecuzione, ciascuna accessibile tramite il proprio indirizzo addr. Tuttavia, le singole istanze del microservizio non sono affidabili: potrebbero fallire, essere inaccessibili o sovraccariche. Per questo, devi implementare il tipo Balancer, che implementa anche l'interfaccia Backend e effettua il bilanciamento del carico lato client tra le istanze del microservizio, scegliendo ogni volta l'istanza **meno caricata**. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr contiene ip:porta di una specifica istanza func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs contengono gli indirizzi di tutte le istanze bilanciate func NewBalancer(addrs []string) *Balancer { // TODO }
Come funziona la connessione WebSocket nell'architettura — in quale momento viene stabilita e chi comunica con chi?