Czy masz doświadczenie w pracy z systemami rozproszonymi?
Golang
/* Są dwa serwery PostgreSQL: * PROD - serwer OLTP, * STATS - serwer do długich zapytań analitycznych. Na aktualnym serwerze, w bazie danych prod, znajduje się duża tabela (10Tb) o następującej strukturze: CREATE TABLE profiles( id SERIAL, data JSONB ) W tabeli mogą występować "luki", tzn. niektóre `id` mogą być pominięte. Należy napisać program do kopiowania tabeli profiles z PROD do STATS. Zakłada się, że do pracy z bazami danych będą używane następujące interfejsy: type Row []interface{} type Database interface { // implementacja interfejsu Database może ponownie ustanawiać połączenia // wywołanie SaveRows jest idempotentne 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 // Jeśli full=false, kontynuować transfer danych od miejsca poprzedniego błędu // Jeśli full=true, przenieść wszystkie dane func CopyTable(fromName string, toName string, full bool) error { // ... twój kod } Jeśli przekazana jest opcja `full=false`, program powinien kontynuować transfer danych od miejsca poprzedniego błędu. Jeśli `full=true`, powinien przenieść wszystkie dane. **Podstawowy poziom**: - sekwencyjny transfer danych w jednym strumieniu - odzyskiwanie po błędach (opcja `full=false`) Dodatkowe informacje: - w razie potrzeby można rozbudować interfejs, dodając własne metody - w razie potrzeby można używać bezpośrednio pakietu **database/sql**
Dany jest ciąg znaków. Znajdź liczbę par indeksów i i j (i <= j), między którymi, włącznie, nie ma powtarzających się znaków. Dla ciągu "aba" odpowiedź to 5: [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") Dla ciągu "abcb" odpowiedź to ?: aba 3 + 2 = 5 abcb 4 (a, b, c, d) + 1 (ab) + 1 (bc) + 1 (cb) + 1 (abc) = 8
/* Podany jest ciąg znaków. Znajdź liczbę par indeksów i i j (i <= j), pomiędzy którymi nie ma powtarzających się znaków. Dla ciągu "aba" odpowiedź to 5: mogą to być nie tylko ASCII [0, 0] ("a") [0, 1] ("ab") [1, 1] ("b") [1, 2] ("ba") [2, 2] ("a") */
/* Musimy przesłać dane z pewnego źródła do pewnego odbiorcy. Źródło zwraca dane w małych partiach (~dziesięć rekordów), podczas gdy odbiorca działa efektywniej z dużymi partiami (~tysiąc rekordów). Przykład rzeczywisty to przesyłanie danych z kolejek typu Kafka do bazy danych Clickhouse. Źródło: - Praktycznie nieskończone. - Źródło nigdy nie zwraca więcej niż MaxItems rekordów na jedno wywołanie Next. - W ramach jednej "sesji" (jednego wywołania funkcji Pipe), źródło za każdym razem zwraca nowe dane na każde wywołanie Next. - Jednak po restarcie, źródło zaczyna od poprzedniej "potwierdzonej" pozycji, wskazywanej przez cookie. Dlatego *każda* wartość cookie zwrócona przez Next, po zapisaniu danych w odbiorniku, musi być potwierdzona wywołaniem Commit, w tej samej kolejności, w jakiej zostały zwrócone przez Next. Odbiornik: - Nie może przetworzyć więcej niż MaxItems naraz. Poziom podstawowy: Wymaga zaimplementowania funkcji func Pipe(p Producer, c Consumer) error która czyta dane ze źródła, grupuje je w bufor o rozmiarze nie większym niż MaxItems i zapisuje do odbiornika, a następnie potwierdza postęp w źródle. Utrudnienie: Metody Next, Process i Commit są powiązane z wywołaniami sieciowymi i mogą działać dość długo. Aby przyspieszyć proces, konieczne jest równoległe wykonywanie procesów czytania, zapisywania i potwierdzania postępu. Tak, aby podczas Process lub Commit, czytanie ze źródła i tworzenie nowego bufora kontynuowało. */ const MaxItems = 9999 type Producer interface { // Next zwraca: // - partię elementów do przetworzenia // - cookie do potwierdzenia po zakończeniu przetwarzania // - błąd Next() (items []any, cookie int, err error) // Commit służy do oznaczenia partii danych jako przetworzonych Commit(cookie int) error } type Consumer interface { Process(items []any) error } func Pipe(p Producer, c Consumer) error { // TODO }
Wyjaśnij zasadę odwracania zależności i dlaczego bezpośrednie wywoływanie metod repozytorium z przypadku użycia narusza SOLID.
Opowiedz o bazach danych — z czym pracowałeś i z czym pracujesz teraz?
Czym różnią się środowiska local, dev, stage, prod?
Jak przeprowadziłeś testowanie i weryfikację poprawności wykonanych zapytań?
/* Istnieje aplikacja z architekturą mikroserwisów. Mikroserwis można abstrakcyjnie opisać za pomocą interfejsu Backend. Aby uzyskać dostęp do jednej instancji mikroserwisu, można użyć typu BackendImpl, który jest już zaimplementowany. Każdy mikroserwis ma kilkadziesiąt uruchomionych instancji, z których każda jest dostępna pod własnym adresem addr. Jednak pojedyncze instancje mikroserwisu nie są niezawodne: mogą się zawiesić, być niedostępne lub przeciążone. Dlatego musisz zaimplementować typ Balancer, który również implementuje interfejs Backend i realizuje balansowanie obciążenia po stronie klienta między instancjami mikroserwisu, wybierając za każdym razem **najmniej obciążoną** instancję. */
Jak JWT sprawdza swoją autentyczność? Jak upewnić się, że token nie jest sfałszowany?
/* Istnieje aplikacja z architekturą mikroserwisów. Mikroserwis można abstrakcyjnie reprezentować za pomocą interfejsu Backend. Aby uzyskać dostęp do instancji mikroserwisu, można użyć typu BackendImpl, który jest już zaimplementowany. Każdy mikroserwis ma kilkadziesiąt uruchomionych instancji, z których każda jest dostępna pod własnym adresem addr. Jednak pojedyncze instancje mikroserwisu nie są niezawodne: mogą się zawiesić, być niedostępne lub przeciążone. Dlatego musisz zaimplementować typ Balancer, który również implementuje interfejs Backend i wykonuje balansowanie obciążenia po stronie klienta między instancjami mikroserwisu, wybierając za każdym razem **najmniej obciążoną** instancję. */ type Request interface{} type Response interface{} type Backend interface { Invoke(ctx context.Context, req Request) (Response, error) } var _ Backend = &BackendImpl{} // addr zawiera ip:port konkretnej instancji func NewBackend(addr string) *BackendImpl type Balancer struct { // TODO } var _ Backend = &Balancer{} // addrs zawierają adresy wszystkich balansowanych instancji func NewBalancer(addrs []string) *Balancer { // TODO }
Czy masz doświadczenie w zarządzaniu zespołem?
Jaki jest limit semafora i buforowanego kanału jako rozwiązania do ograniczania równoległych zapytań?
Co jest dodawane do odpowiedzi, gdy okno staje się ważne?
Jak porównywać elementy w mapie? Jak sprawdzić, czy dwie mapy są identyczne?
Jaka jest maksymalna liczba zapytań na sekundę (RPS), którą obsługiwała najbardziej obciążona usługa?
Napisać zadanie w języku Go — sekcja praktyczna (problemy z życia programisty, bez algorytmów): ulepszyć lub naprawić usługę. Czas trwania — 1,5 godziny.
Format pracy: zdalnie, w biurze czy hybrydowo?
Jak uczynić pamięć stałą (usunąć zależność od liczby unikalnych znaków)?