Qual indicador de quantidade de operações por segundo na leitura de dados você atingiu ou analisou?
Golang
Poderia indicar o seu nível de rendimento atual?
Vives atualmente em Moscovo? Qual cidade estás a considerar? Consideras um formato de trabalho híbrido? Em que fase de procura estás?
Tem outros processos de entrevista ativos?
Como é que estes dados métricos são integrados e exibidos no Grafana?
Projetar um sistema de mensagens escalável com suporte para 150 milhões de utilizadores, 75 milhões de DAU, 225 milhões de MAU, 1.2M leituras / 300k escritas QPS, 5 milhões de utilizadores simultâneos, 60 PB de novos dados por ano, crescimento de 30% ao ano, P99 <200 ms para leitura, <300 ms para escrita, SLA 99.95%. CONTEXT É necessário projetar um sistema de mensagens distribuído, semelhante ao WhatsApp, que suporte chats 1:1 e em grupo, garanta a entrega de mensagens, exiba estados online dos utilizadores e transmita ficheiros multimédia (fotos, vídeos, áudio). O sistema deve garantir alta disponibilidade e baixa latência, suportar alto paralelismo e escalar globalmente. REQUISITOS FUNCIONAIS - Suporte a chats pessoais (1:1) e em grupo com possibilidade de adicionar/remover participantes - Envio e receção de mensagens de texto e ficheiros multimédia Requisitos não funcionais: - Sem implementação explícita de mecanismo de encriptação end-to-end ao nível de serviços ou clientes, além de uma anotação geral. - Ausência de descrição explícita de sharding e replicação de bases de dados por chat_id ou user_id para escalabilidade e tolerância a falhas. - Sem componente ou mecanismo explícito para processamento de sincronização offline de mensagens e recibos de entrega. - Não está claro como é feita a balanceamento de carga entre bases de dados e serviços, especialmente em picos de carga. **Pontos críticos a serem considerados:** (A arquitetura mostrada na diagrama inclui Load Balancer, API Gateway, Message Queue, Service, Cache, Database, Object Storage e CDN)
Qual foi a composição da equipa com a qual trabalhou por última vez?
Tens um GitHub ou LinkedIn ativo?
Fale brevemente sobre o que fez nos seus empregos anteriores e quais funcionalidades implementou.
Como são estruturados os testes na equipa — quem escreve o quê, qual é a cobertura, há E2E?
/* Precisamos transferir dados de uma fonte para um consumidor. A fonte fornece os dados em pequenos lotes (~dez registos), enquanto que o consumidor funciona melhor com lotes maiores (~mil registos). Um exemplo real é a transferência de dados de filas tipo Kafka para uma base de dados Clickhouse. Fonte: - Praticamente infinita. - A fonte nunca devolve mais de MaxItems registos numa chamada a Next. - Dentro de uma "sessão" (uma chamada à função Pipe), a fonte devolve dados novos em cada chamada a Next. - No entanto, após reiniciar, a fonte começa na posição "confirmada" anterior, indicada por cookie. Portanto, *cada* valor de cookie devolvido por Next, após guardar os dados no receptor, deve ser confirmado com a chamada a Commit, na mesma ordem em que foram devolvidos por Next. Receptor: - Não pode processar mais de MaxItems de cada vez. Nível básico: É necessário implementar a função func Pipe(p Producer, c Consumer) error que lê dados da fonte, os agrupa num buffer de tamanho não superior a MaxItems e os guarda no receptor, depois de o fazer, confirma o progresso na fonte. Dificuldade adicional: Os métodos Next, Process e Commit estão relacionados com chamadas de rede e podem demorar bastante. Para acelerar o processo, é necessário paralelizar os processos de leitura, escrita e confirmação de progresso. De modo que, durante Process ou Commit, a leitura da fonte e a formação do novo buffer continuem. */ const MaxItems = 9999 type Producer interface { // Next devolve: // - um lote de itens para processar // - uma cookie para confirmar quando o processamento estiver concluído // - um erro Next() (items []any, cookie int, err error) // Commit é usado para marcar um lote de dados como processado Commit(cookie int) error } type Consumer interface { Process(items []any) error } func Pipe(p Producer, c Consumer) error { // TODO }
De que formas é possível aumentar a eficiência da busca de elementos numa estrutura de dados Map?
Design de um sistema de mensagens escalável suportando 150 milhões de utilizadores, 75 milhões de DAU, 225 milhões de MAU, 1,2M de leituras / 300k de escritas no pico de QPS, 5 milhões de utilizadores simultâneos, 60 PB de novos dados por ano, crescimento de 30% ao ano, SLA de 99,95%, p99 <200 ms para leitura, <300 ms para escrita. CONTEXT É necessário projetar um sistema de mensagens distribuído, semelhante ao WhatsApp, que suporte chats 1:1 e em grupo, garanta a entrega de mensagens, exiba estados online dos utilizadores e permita a transferência de ficheiros multimédia (fotos, vídeos, áudios). O sistema deve garantir alta disponibilidade e baixa latência, suportar alto paralelismo e escalar globalmente. REQUISITOS FUNCIONAIS - Suporte para chats pessoais (1:1) e em grupo com capacidade de adicionar/remover participantes - Envio e receção de mensagens de texto e ficheiros multimédia Não há uma implementação clara do mecanismo de encriptação end-to-end ao nível de serviços ou clientes, além de uma anotação geral. - Ausência de descrição explícita de sharding e replicação de bases de dados por chat_id ou user_id para escalabilidade e tolerância a falhas. - Não há um componente ou mecanismo claro para lidar com sincronização offline de mensagens e recibos de entrega. - A forma como se realiza o balanceamento de carga entre bases de dados e serviços, especialmente em picos de carga, não está clara. **Pontos críticos a considerar:**
/* Existem dois servidores PostgreSQL: * PROD - servidor OLTP, * STATS - servidor para consultas analíticas longas. No servidor atual, na base de dados prod, há uma tabela grande (10Tb) com a seguinte estrutura: CREATE TABLE profiles( id SERIAL, data JSONB ) Na tabela podem haver "buracos", ou seja, alguns `id` podem estar ausentes. É necessário escrever um programa para copiar a tabela profiles do PROD para o STATS. Supõe-se que serão usadas as seguintes interfaces para trabalhar com bancos de dados: type Row []interface{} type Database interface { // a implementação da interface Database pode restabelecer conexões // a chamada 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, continuar a transferência de dados do ponto do erro anterior // Se full=true, transferir todos os dados func CopyTable(fromName string, toName string, full bool) error { // ... seu código } Se a opção `full=false` for passada, o programa deve continuar a transferência de dados do ponto do erro anterior. Se `full=true`, deve transferir todos os dados. **Nível básico**: - transferência sequencial de dados em um único fluxo - recuperação após falhas (opção `full=false`) Informação adicional: - se necessário, pode expandir a interface adicionando seus próprios métodos - se necessário, pode usar diretamente o pacote **database/sql**
/* É dada uma string de caracteres. Encontrar o número de pares de índices i e j (i <= j), entre os quais não há caracteres repetidos. Para a string "aba", a resposta é 5: podem não ser apenas ASCII [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") */
Dada uma cadeia de caracteres. Encontrar o número de pares de índices i e j (i <= j), entre os quais, inclusive, não há caracteres repetidos. Para a cadeia "aba", a resposta é 5: [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") Para a cadeia "abcb", a resposta é ?: aba 3 + 2 = 5 abcb 4 (a, b, c, d) + 1 (ab) + 1 (bc) + 1 (cb) + 1 (abc) = 8
Como funciona a conexão WebSocket na arquitetura — em que momento ela é estabelecida e quem comunica com quem?
/* Existe uma aplicação com arquitetura de microserviços. Um microserviço pode ser abstraído através de uma interface Backend. Para aceder a uma instância do microserviço, pode usar o tipo BackendImpl, que já está implementado. Cada microserviço tem várias dezenas de instâncias em execução, cada uma acessível pelo seu próprio endereço addr. No entanto, as instâncias individuais do microserviço não são confiáveis: podem falhar, estar inacessíveis ou sobrecarregadas. Por isso, precisa implementar o tipo Balancer, que também implementa a interface Backend e realiza balanceamento de carga do lado do cliente entre as instâncias do microserviço, escolhendo a cada vez a instância **menos carregada**. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr contém ip:porta de uma instância específica func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs contêm os endereços de todas as instâncias balanceadas func NewBalancer(addrs []string) *Balancer { // TODO }
Tem experiência com sistemas distribuídos?
Tem experiência em gestão de equipas?