From d8120b98fcf93ae10d96093e3bd123fd5ef986d0 Mon Sep 17 00:00:00 2001 From: Tester Date: Tue, 11 Aug 2026 09:05:00 +0000 Subject: [PATCH] content(interview): Senior Java interview prep guide (EN + VI) --- .../interview/senior-java-interview-prep.md | 283 ++++++++++++++++++ .../interview/senior-java-interview-prep.md | 283 ++++++++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 src/data/blog/en/interview/senior-java-interview-prep.md create mode 100644 src/data/blog/vi/interview/senior-java-interview-prep.md diff --git a/src/data/blog/en/interview/senior-java-interview-prep.md b/src/data/blog/en/interview/senior-java-interview-prep.md new file mode 100644 index 0000000..36ebe99 --- /dev/null +++ b/src/data/blog/en/interview/senior-java-interview-prep.md @@ -0,0 +1,283 @@ +--- +title: "Senior Java Interview Prep: Java, OOP, Microservices, Database, Kafka, System Design" +description: "A practical study guide for senior Java backend interviews — the concepts interviewers actually probe, the traps that fail strong candidates, and the code-level details that signal senior-level thinking." +pubDatetime: 2026-08-12T10:00:00+07:00 +featured: true +draft: false +tags: + - java + - interview + - system-design + - microservices + - kafka + - database +--- + +Senior Java interviews are not about memorizing syntax. They are about proving you can **make trade-offs under ambiguity** — the exact thing a senior is paid to do. + +This guide walks through the six areas you named: Java core, OOP, microservices, databases, Kafka, and system design. For each, I give you the questions interviewers really ask, the answers that separate mid from senior, and the common traps. + +> Mindset first: an interviewer who hears "it depends" followed by a clear trade-off analysis learns more about you in 30 seconds than from ten facts. Facts are easy to Google. Judgment is not. + +## 1. Java Core — what "senior" really means + +A junior knows the syntax. A senior knows **what the JVM is doing, why it behaves the way it does, and where it will surprise you in production.** + +### 1.1 Memory model and GC + +Expect: "Explain what happens when you `new` an object." A mid answer stops at "it goes on the heap." A senior continues: + +- **Heap vs metaspace vs stack.** Short-lived objects go to the young generation (Eden). Most die there; survivors get copied to survivor spaces, then promoted to old gen. The metaspace holds class metadata (replaced the old perm gen). The stack holds frames and primitives/local references. +- **Stop-the-world.** Any GC pause freezes application threads. Throughput collectors (Parallel) optimize total work; low-latency collectors (G1, ZGC, Shenandoah) minimize pause time. For a latency-sensitive service, "we use G1" is a fine start, but be ready to talk about `MaxGCPauseMillis`, region sizing, and how ZGC gets sub-millisecond pauses via colored pointers / load barriers. +- **Common trap:** assuming GC means you don't need to manage memory. Unbounded caches, static collections, and thread-local leaks still OOM you. A senior mentions these. + +```java +// A classic leak: a static cache that never evicts +private static final Map CACHE = new HashMap<>(); + +// Senior fix: bounded + time-based eviction +private static final Cache CACHE = Caffeine.newBuilder() + .maximumSize(10_000) + .expireAfterWrite(Duration.ofMinutes(10)) + .build(); +``` + +### 1.2 Concurrency — the real differentiator + +This is where most candidates fail. Be fluent in: + +- **`synchronized` vs `ReentrantLock`.** `synchronized` is simpler and JVM-optimized (biased locking was deprecated in JDK 17 — know that). `ReentrantLock` gives you `tryLock(timeout)`, multiple condition variables, and fairness choices. +- **`volatile`** — visibility, not atomicity. It does **not** make `i++` safe. +- **`Atomic*` / `LongAdder`** — `LongAdder` wins under high contention because it spreads updates across cells. +- **Thread pools.** Never `Executors.newFixedThreadPool` with an unbounded `LinkedBlockingQueue` for untrusted workloads — it can buffer infinitely and OOM. Size it deliberately, use a bounded queue + a `RejectedExecutionHandler`, and understand `corePoolSize` / `maxPoolSize` / `keepAliveTime` / `workQueue` interaction. +- **`CompletableFuture`** — non-blocking composition, `thenCompose` (flatMap) vs `thenCombine`, exception handling with `handle`/`exceptionally`. This is heavily tested. +- **Virtual threads (Project Loom, Java 21+).** A senior in 2026 should know them: millions of cheap virtual threads scheduled on a few carrier threads. They are **not** faster for CPU work, but they destroy thread-per-request bottlenecks for I/O-heavy services. Know the pinning pitfall (long `synchronized` blocks or native calls pin the carrier thread). + +```java +// Blocking I/O on a virtual thread is fine and cheap: +try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + List> futures = urls.stream() + .map(url -> executor.submit(() -> fetch(url))) + .toList(); +} +``` + +### 1.3 JVM internals interviewers love + +- **Class loading:** bootstrap → platform → application, parent-delegation model, why it exists (security + avoiding duplicate core classes). Mention how you'd debug `ClassNotFoundException` vs `NoClassDefFoundError`. +- **JMM and happens-before:** final, volatile, lock acquisition, thread start/join all establish happens-before edges. This is the rigorous answer to "why is my flag change not visible?" +- **`String` and immutability,** `Integer` caching (`-128..127`), and why `==` on wrappers bites people. + +### 1.4 Runtime & tooling + +A senior says "when it's slow in prod, I don't guess — I measure": `jstack`, `jmap`, `jstat`, async-profiler, flight recorder. Name at least two you've actually used to find a real problem. + +## 2. OOP — principles are the entry ticket, design is the test + +### 2.1 SOLID without reciting definitions + +Interviewers want to see SOLID applied, not defined. The two that get probed hardest: + +- **Open/Closed:** add features via new types, not by editing working classes. Strategy / Plugin patterns. +- **Dependency Inversion:** depend on abstractions. This is *why* Spring exists — you inject `PaymentGateway`, not `StripeGateway`. + +```java +// Violates DIP: concrete dependency baked in +class OrderService { + private final StripeGateway gateway = new StripeGateway(); +} + +// Senior: depends on abstraction, injected +class OrderService { + private final PaymentGateway gateway; + OrderService(PaymentGateway gateway) { this.gateway = gateway; } +} +``` + +### 2.2 Composition over inheritance + +Expect a "favor composition over inheritance" question. The senior answer: inheritance couples you to a parent's implementation and breaks encapsulation (the "fragile base class" problem). Delegate behavior instead. + +### 2.3 Polymorphism & interfaces in the real world + +Interface segregation matters at scale — a 40-method `UserService` interface that forces every implementer to stub 35 methods is a design smell. Split by role. + +### 2.4 Common trap + +Saying "OOP is outdated because of functional Java." A senior says: both. Streams for data transforms, OOP for modeling behavior-rich domain objects. Records (Java 16+) are great for immutable DTOs but a `Record` with business logic is a code smell — put behavior in services or rich domain types. + +## 3. Microservices — you will be asked "when NOT to" + +### 3.1 The distributed-monolith trap + +The most senior answer to "design a microservice" is sometimes "don't, yet." Premature decomposition gives you **network calls instead of method calls**, distributed transactions, and 10× operational cost with none of the benefit. Know the triggers for splitting: independent deployability, different scaling profiles, different teams, different failure domains. + +### 3.2 Service communication + +- **Synchronous (REST/gRPC):** simplest, but every hop adds latency and a failure point. Use timeouts + retries with backoff + circuit breakers (Resilience4j). Never retry without idempotency. +- **Asynchronous (events/messages):** decouples producers/consumers, absorbs load spikes, enables replay. The cost is eventual consistency and harder debugging. + +### 3.3 Resilience patterns (draw these) + +- **Circuit breaker:** open after N failures, half-open to probe recovery. Prevents cascading failure. +- **Bulkhead:** isolate failures (separate thread pools / connection pools) so one slow dependency can't exhaust everything. +- **Retry + backoff + jitter:** naive `for (i<3) retry` during an outage is a **self-inflicted DDoS**. Add jitter. + +```java +// Resilience4j: timeout + retry + circuit breaker composed +Supplier decorated = Decorators.ofSupplier(() -> callDownstream()) + .withTimeout(Timeout.of(Duration.ofMillis(800))) + .withRetry(Retry.ofDefaults("svc")) + .withCircuitBreaker(CircuitBreaker.ofDefaults("svc")) + .decorate(); +``` + +### 3.4 Service discovery, config, gateway + +Know the roles: discovery (Consul/Eureka/K8s DNS), centralized config (Spring Cloud Config / K8s ConfigMap), API gateway (routing, auth, rate limiting), and observability (traces via OpenTelemetry, metrics via Micrometer/Prometheus). + +### 3.5 Distributed data & transactions + +- **Saga pattern:** sequence of local transactions with compensating actions. Two styles: orchestration (a coordinator) vs choreography (events). Know the trade-off: orchestration is easier to reason about; choreography avoids a central bottleneck but is harder to trace. +- **Two-phase commit (2PC):** avoid it — it holds locks and doesn't survive coordinator failure. Mention it only to explain why you don't use it. + +## 4. Database — the layer that actually decides scale + +### 4.1 Indexing is non-negotiable + +- **B-tree vs hash indexes**, and why range queries need B-tree. +- **Composite index column order** — most selective / equality-first, range-last. Explain why `WHERE a=? AND b>?` wants `(a,b)` not `(b,a)`. +- **Covering indexes** avoid a table lookup. +- **Trap:** a query that "uses an index" but still scans millions of rows (low cardinality, functions on the column, implicit type casts). Read `EXPLAIN`. + +### 4.2 Transactions & isolation + +Interviewers love "explain isolation levels." Be precise: + +- **Read uncommitted / committed / repeatable read / serializable.** +- **Dirty / non-repeatable / phantom reads** — which levels prevent which. +- **Lost updates** and how to prevent: `SELECT ... FOR UPDATE`, optimistic locking with a version column, or `SERIALIZABLE`. +- **MVCC** — readers don't block writers (PostgreSQL/InnoDB). This is why "my read locked the table" is usually a misunderstanding. + +```sql +-- Optimistic concurrency: bump version, fail if someone changed it +UPDATE accounts SET balance = balance - 100, version = version + 1 +WHERE id = ? AND version = ?; +-- 0 rows updated => someone else moved first => retry or reject +``` + +### 4.3 Connection pooling + +A senior knows the pool is a shared, scarce resource. HikariCP sizing: `connections ≈ ((core_count * 2) + effective_spindle_count)` is a starting heuristic, but the real answer is "measure under load." A too-large pool causes context-switch thrash; too small causes queueing. + +### 4.4 SQL vs NoSQL — the actual decision + +Don't say "NoSQL is faster." Say: pick the model that fits the access pattern. Document stores (MongoDB) for flexible schemas; wide-column (Cassandra) for write-heavy time-series at massive scale; relational (Postgres) when you need joins, transactions, and integrity. Know when to reach for Redis (cache / counters / pub-sub) vs a durable store. + +### 4.5 N+1 and the ORM trap + +- **N+1 queries** — lazy loading in a loop. Fix with `JOIN FETCH` / entity graphs / batch fetching. +- **Know what your ORM generates.** A senior reads the SQL. "It works" with 10 rows and dies with 10 million is a classic. + +## 5. Kafka — event-driven systems + +### 5.1 Core model + +- **Topics, partitions, offsets, consumer groups.** Partitions are the unit of parallelism and ordering — ordering is guaranteed *within* a partition, not across. +- **A consumer group** splits partitions among members; adding consumers beyond partition count does nothing. + +### 5.2 Delivery semantics — know all three + +- **At most once:** may lose messages (offset commit before processing). +- **At least once:** may duplicate (processing before commit) — the realistic default; make consumers **idempotent** (dedupe by message key / offset). +- **Exactly once:** Kafka's EOS via idempotent producer + transactional API, or the much simpler "idempotent consumer + at-least-once." + +```java +// Idempotent consumer: dedupe by a stable key, not by hoping for exactly-once +if (processedKeys.putIfAbsent(event.key(), event.offset()) != null) return; +``` + +### 5.3 Replication & durability + +- **Replication factor (RF)** and **ISR** (in-sync replicas). `acks=all` + RF≥3 survives broker loss without data loss. +- **Why "acks=1" is dangerous** in production: the leader can ack then die before replicating. + +### 5.4 Ordering & partitioning + +If order matters (payments, audit), you must key by the entity id so all its events land on one partition. Trade-off: hot keys create hot partitions — sometimes you shard the key. + +### 5.5 Real failure modes + +- **Rebalance storms** when consumers churn. Understand cooperative rebalancing. +- **Consumer lag** — monitor it; it's the first signal of a slow consumer or a producer surge. +- **Poison messages** — a bad record that always fails; without a dead-letter queue (DLQ) it blocks the partition forever. A senior always builds a DLQ. + +```java +// Always have a dead-letter path +try { process(record); } +catch (PoisonException e) { sendToDlq(record, e); /* commit and move on */ } +``` + +## 6. System Design — the senior capstone + +This is where judgment is tested for 45–60 minutes. Process matters more than the answer. + +### 6.1 The interview loop + +1. **Clarify requirements & scope.** QPS? reads vs writes? latency budget? data size? consistency vs availability? +2. **Back-of-envelope capacity.** "10M users, 100 reads/user/day = 1B reads/day ≈ 11.5k QPS." Numbers stop hand-waving. +3. **High-level components.** Clients → CDN → API gateway → services → cache → DB → async workers/queues. +4. **Drill one or two areas deeply** (the interviewer's interest). +5. **Address failure.** What breaks first? How do you degrade? + +### 6.2 Cache strategy + +- **Cache-aside (lazy):** app checks cache, misses DB, populates cache. Most common. Handle **cache stampede** (many requests miss at once) with request coalescing / single-flight; handle **stale data** with TTL; handle **thundering herd on expiry** with jittered TTL. +- **Write-through / write-behind** when consistency with the store matters. +- **Cache invalidation** is the hard part — prefer TTL + explicit invalidation on write. + +### 6.3 Consistency models + +- **CAP:** under partition, you choose CP (consistency) or AP (availability). Say it correctly — partitions are rare but unavoidable, so the real choice is "what do we sacrifice *during* a partition." +- **Eventual consistency:** acceptable for feeds, counts, search; dangerous for balances, inventory if not guarded. + +### 6.4 Scalability patterns + +- **Horizontal scaling + stateless services** (sessions in Redis, not local memory). +- **Sharding/partitioning** the database by tenant or hash. +- **Async processing** to flatten spikes (Kafka + workers). +- **Backpressure & queues** so a slow dependency degrades instead of collapsing. + +### 6.5 A mini example: design a URL shortener + +- Requirements: 100M new URLs/day, 1B redirects/day, low latency. +- Key-value store, key = base62(encoded counter or hash). Hash collisions → retry with salt. +- Cache hot URLs in Redis (most redirects hit a small set). +- Redirect is a 301/302 — 301 lets browsers cache (less load) but harder to change. +- Capacity: 1B redirects × ~500 bytes logs ≈ 0.5 TB/day; plan retention/aggregation. + +### 6.6 Observability is part of the design + +A senior bakes in tracing (request IDs across services), metrics (RED: rate/errors/duration), and structured logs from day one. "We'll add monitoring later" is a red flag. + +## 7. How to present yourself as senior + +- **Narrate trade-offs.** "I'd use at-least-once + idempotent consumer because exactly-once is heavier and rarely needed." +- **Admit uncertainty honestly.** "I'd measure before committing to RF=5; 3 is usually enough." +- **Connect to real experience.** "In prod we saw rebalance storms when…" beats textbook recitation. +- **Push back respectfully.** If a design is premature microservices, say so and explain the cost. + +## 8. Quick self-check + +Before the interview, make sure you can whiteboard: + +- [ ] A thread-safe counter under high contention (and why `AtomicLong` can bottleneck). +- [ ] A retry-with-backoff-and-jitter helper. +- [ ] An SQL query + index to fix an N+1 or a slow report. +- [ ] A Kafka consumer that's idempotent and has a DLQ. +- [ ] A system diagram for a read-heavy service with cache, DB, and a queue. +- [ ] The difference between `synchronized`, `volatile`, and `AtomicReference` in one sentence each. + +If those feel easy, you're ready. If not, those are exactly the gaps to close first. + +Good luck — and remember: senior means you can say "it depends" and then *justify it*. diff --git a/src/data/blog/vi/interview/senior-java-interview-prep.md b/src/data/blog/vi/interview/senior-java-interview-prep.md new file mode 100644 index 0000000..d39c962 --- /dev/null +++ b/src/data/blog/vi/interview/senior-java-interview-prep.md @@ -0,0 +1,283 @@ +--- +title: "Ôn thi Senior Java: Java, OOP, Microservices, Database, Kafka, System Design" +description: "Cẩm nang ôn thi phỏng vấn Java backend cấp senior — những khái niệm phỏng vấn viên thực sự kiểm tra, các bẫy khiến ứng viên giỏi trượt, và những chi tiết code thể hiện tư duy cấp cao." +pubDatetime: 2026-08-12T10:00:00+07:00 +featured: true +draft: false +tags: + - java + - interview + - system-design + - microservices + - kafka + - database +--- + +Phỏng vấn Senior Java không phải để kiểm tra bạn thuộc syntax. Nó để chứng minh bạn có thể **đưa ra đánh đổi khi đối mặt với sự mơ hồ** — đúng cái việc mà một senior được trả tiền để làm. + +Bài viết này đi qua sáu mảng bạn đã nhắc: Java core, OOP, microservices, database, Kafka và system design. Với mỗi mảng, mình cho bạn những câu hỏi phỏng vấn viên hay hỏi thật, câu trả lời phân biệt mid và senior, cùng các bẫy phổ biến. + +> Quan trọng nhất là tư duy: một phỏng vấn viên nghe câu "tùy thuộc" theo sau bởi một phân tích đánh đổi rõ ràng sẽ hiểu về bạn nhiều hơn trong 30 giây so với mười câu sự thật. Sự thật thì dễ Google, nhưng sự phán đoán thì không. + +## 1. Java Core — "senior" thực sự nghĩa là gì + +Junior biết cú pháp. Senior biết **JVM đang làm gì, tại sao nó lại hành xử như vậy, và ở đâu nó sẽ làm bạn bất ngờ trên production.** + +### 1.1 Memory model và GC + +Câu hỏi thường gặp: "Giải thích chuyện gì xảy ra khi bạn `new` một object." Câu trả lời của junior dừng ở "nó nằm trên heap." Senior thì tiếp tục: + +- **Heap vs metaspace vs stack.** Object sống ngắn nằm young generation (Eden). Đa số chết ở đó; những object sống sót được copy sang survivor, rồi promote lên old gen. Metaspace giữ metadata của class (thay thế perm gen cũ). Stack giữ frame và primitive/reference cục bộ. +- **Stop-the-world.** Bất kỳ GC pause nào cũng đóng băng luồng ứng dụng. Các throughput collector (Parallel) tối ưu tổng công việc; low-latency collector (G1, ZGC, Shenandoah) tối thiểu hóa thời gian pause. Với service nhạy latency, "dùng G1" là khởi đầu ổn, nhưng hãy sẵn sàng nói về `MaxGCPauseMillis`, region sizing, và cách ZGC đạt pause dưới mili-giây nhờ colored pointers / load barriers. +- **Bẫy hay gặp:** nghĩ GC nghĩa là không cần quản lý bộ nhớ. Unbounded cache, static collection, thread-local leak vẫn làm bạn OOM. Senior sẽ nhắc những thứ này. + +```java +// Rò rỉ kinh điển: static cache không bao giờ evict +private static final Map CACHE = new HashMap<>(); + +// Sửa kiểu senior: bounded + time-based eviction +private static final Cache CACHE = Caffeine.newBuilder() + .maximumSize(10_000) + .expireAfterWrite(Duration.ofMinutes(10)) + .build(); +``` + +### 1.2 Concurrency — chỗ phân hóa thực sự + +Đây là nơi phần lớn ứng viên trượt. Bạn nên thạo: + +- **`synchronized` vs `ReentrantLock`.** `synchronized` đơn giản và được JVM tối ưu (biased locking bị bỏ ở JDK 17 — cần biết). `ReentrantLock` cho `tryLock(timeout)`, nhiều condition variable, và lựa chọn fairness. +- **`volatile`** — chỉ đảm bảo visibility, không phải atomicity. Nó **không** làm `i++` an toàn. +- **`Atomic*` / `LongAdder`** — `LongAdder` thắng khi contention cao vì nó chia việc update thành nhiều cell. +- **Thread pools.** Đừng dùng `Executors.newFixedThreadPool` với `LinkedBlockingQueue` vô hạn cho workload không tin cậy — nó buffer vô hạn và OOM. Hãy size có chủ đích, dùng bounded queue + `RejectedExecutionHandler`, và hiểu tương tác `corePoolSize` / `maxPoolSize` / `keepAliveTime` / `workQueue`. +- **`CompletableFuture`** — composition non-blocking, `thenCompose` (flatMap) vs `thenCombine`, xử lý exception bằng `handle`/`exceptionally`. Bị kiểm tra rất nhiều. +- **Virtual threads (Project Loom, Java 21+).** Senior năm 2026 phải biết: hàng triệu virtual thread rẻ được schedule trên vài carrier thread. Nó **không** nhanh hơn cho tác vụ CPU, nhưng giải quyết triệt để nghẽn thread-per-request cho service nặng I/O. Biết bẫy pinning (block `synchronized` dài hoặc native call sẽ pin carrier thread). + +```java +// Blocking I/O trên virtual thread thì rẻ và ổn: +try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + List> futures = urls.stream() + .map(url -> executor.submit(() -> fetch(url))) + .toList(); +} +``` + +### 1.3 JVM internals mà phỏng vấn viên thích + +- **Class loading:** bootstrap → platform → application, parent-delegation model, tại sao nó tồn tại (bảo mật + tránh trùng class core). Biết cách debug `ClassNotFoundException` vs `NoClassDefFoundError`. +- **JMM và happens-before:** final, volatile, lock acquisition, thread start/join đều tạo happens-before edge. Đây là câu trả lời chặt chẽ cho "tại sao flag thay đổi không visible?" +- **`String` và immutability,** `Integer` caching (`-128..127`), và tại sao `==` trên wrapper "cắn" người ta. + +### 1.4 Runtime & tooling + +Senior nói "khi chậy trên prod, tôi không đoán — tôi đo": `jstack`, `jmap`, `jstat`, async-profiler, flight recorder. Kể tên ít nhất hai cái bạn từng dùng để tìm ra vấn đề thật. + +## 2. OOP — nguyên lý là vé vào cửa, thiết kế là bài kiểm tra + +### 2.1 SOLID không phải để đọc định nghĩa + +Phỏng vấn viên muốn thấy SOLID được áp dụng, không phải định nghĩa. Hai cái bị soi kỹ nhất: + +- **Open/Closed:** thêm tính năng bằng type mới, không sửa class đang chạy tốt. Strategy / Plugin patterns. +- **Dependency Inversion:** phụ thuộc vào abstraction. Đây là *lý do* Spring tồn tại — bạn inject `PaymentGateway`, không phải `StripeGateway`. + +```java +// Vi phạm DIP: dependency cụ thể được gắn cứng +class OrderService { + private final StripeGateway gateway = new StripeGateway(); +} + +// Senior: phụ thuộc vào abstraction, được inject +class OrderService { + private final PaymentGateway gateway; + OrderService(PaymentGateway gateway) { this.gateway = gateway; } +} +``` + +### 2.2 Composition over inheritance + +Câu hỏi "favor composition over inheritance" rất hay gặp. Câu trả lời senior: inheritance gắn bạn vào implementation của cha và phá encapsulation (kiểu "fragile base class"). Hãy delegate behavior thay vì kế thừa. + +### 2.3 Polymorphism & interface trong thực tế + +Interface segregation quan trọng khi scale — một interface `UserService` 40 method bắt mọi implementer phải stub 35 method là code smell. Hãy tách theo role. + +### 2.4 Bẫy phổ biến + +Nói "OOP lỗi thời rồi vì có functional Java." Senior nói: cả hai. Streams cho data transform, OOP cho model domain giàu behavior. Record (Java 16+) tuyệt cho DTO immutable, nhưng Record chứa business logic là code smell — hãy đặt behavior vào service hoặc rich domain type. + +## 3. Microservices — bạn sẽ bị hỏi "khi nào KHÔNG nên dùng" + +### 3.1 Bẫy distributed-monolith + +Câu trả lời senior nhất cho "thiết kế một microservice" đôi khi là "chưa, đừng." Chia nhỏ sớm quá mang lại **network call thay vì method call**, distributed transaction, và 10× chi phí vận hành mà chẳng có lợi ích. Biết trigger để tách: deploy độc lập, profile scaling khác nhau, team khác nhau, failure domain khác nhau. + +### 3.2 Service communication + +- **Đồng bộ (REST/gRPC):** đơn giản nhất, nhưng mỗi hop thêm latency và điểm chết. Dùng timeout + retry with backoff + circuit breaker (Resilience4j). Không bao giờ retry thiếu idempotency. +- **Bất đồng bộ (event/message):** decouple producer/consumer, hấp thụ spike, cho phép replay. Cái giá là eventual consistency và khó debug hơn. + +### 3.3 Resilience patterns (vẽ mấy cái này) + +- **Circuit breaker:** mở sau N failure, half-open để thăm dò phục hồi. Ngăn cascading failure. +- **Bulkhead:** cô lập failure (tách thread pool / connection pool) để một dependency chậm không làm cạn kiệt tất cả. +- **Retry + backoff + jitter:** retry `for(i<3)` ngây thơ lúc outage là **tự DDoS chính mình**. Phải thêm jitter. + +```java +// Resilience4j: timeout + retry + circuit breaker gộp lại +Supplier decorated = Decorators.ofSupplier(() -> callDownstream()) + .withTimeout(Timeout.of(Duration.ofMillis(800))) + .withRetry(Retry.ofDefaults("svc")) + .withCircuitBreaker(CircuitBreaker.ofDefaults("svc")) + .decorate(); +``` + +### 3.4 Service discovery, config, gateway + +Biết vai trò: discovery (Consul/Eureka/K8s DNS), centralized config (Spring Cloud Config / K8s ConfigMap), API gateway (routing, auth, rate limiting), và observability (trace qua OpenTelemetry, metric qua Micrometer/Prometheus). + +### 3.5 Distributed data & transactions + +- **Saga pattern:** chuỗi local transaction kèm compensating action. Hai style: orchestration (có coordinator) vs choreography (event). Biết đánh đổi: orchestration dễ suy luận; choreography tránh bottleneck trung tâm nhưng khó trace. +- **Two-phase commit (2PC):** tránh — nó giữ lock và không sống sót nếu coordinator chết. Chỉ nhắc để giải thích tại sao không dùng. + +## 4. Database — tầng quyết định scale thực sự + +### 4.1 Indexing là bắt buộc + +- **B-tree vs hash index**, và tại sao range query cần B-tree. +- **Thứ tự cột composite index** — selective nhất / equality trước, range cuối. Giải thích vì sao `WHERE a=? AND b>?` muốn `(a,b)` chứ không `(b,a)`. +- **Covering index** tránh table lookup. +- **Bẫy:** query "dùng index" mà vẫn scan hàng triệu row (low cardinality, function trên cột, implicit type cast). Hãy đọc `EXPLAIN`. + +### 4.2 Transaction & isolation + +Phỏng vấn viên thích "giải thích isolation levels." Phải chuẩn: + +- **Read uncommitted / committed / repeatable read / serializable.** +- **Dirty / non-repeatable / phantom reads** — level nào ngăn cái nào. +- **Lost updates** và cách ngăn: `SELECT ... FOR UPDATE`, optimistic locking với version column, hoặc `SERIALIZABLE`. +- **MVCC** — reader không block writer (PostgreSQL/InnoDB). Đó là lý do "read của tôi lock cả table" thường là hiểu sai. + +```sql +-- Optimistic concurrency: tăng version, fail nếu ai đó đã đổi +UPDATE accounts SET balance = balance - 100, version = version + 1 +WHERE id = ? AND version = ?; +-- 0 row updated => có người khác đi trước => retry hoặc reject +``` + +### 4.3 Connection pooling + +Senior biết pool là tài nguyên chia sẻ, khan hiếm. Sizing HikariCP: `connections ≈ ((core_count * 2) + effective_spindle_count)` là heuristic khởi điểm, nhưng đáp án thật là "đo dưới tải." Pool quá lớn gây context-switch thrash; quá nhỏ gây queueing. + +### 4.4 SQL vs NoSQL — quyết định thật + +Đừng nói "NoSQL nhanh hơn." Hãy nói: chọn model khớp access pattern. Document store (MongoDB) cho schema linh hoạt; wide-column (Cassandra) cho write-heavy time-series ở quy mô khổng lồ; relational (Postgres) khi cần join, transaction, integrity. Biết khi nào dùng Redis (cache / counter / pub-sub) thay store bền vững. + +### 4.5 N+1 và bẫy ORM + +- **N+1 queries** — lazy loading trong loop. Sửa bằng `JOIN FETCH` / entity graphs / batch fetching. +- **Biết SQL mà ORM sinh ra.** Senior đọc SQL. "Nó chạy" với 10 row và chết với 10 triệu là kinh điển. + +## 5. Kafka — hệ thống event-driven + +### 5.1 Core model + +- **Topics, partitions, offsets, consumer groups.** Partition là đơn vị parallelism và ordering — ordering được đảm bảo *trong* một partition, không phải across. +- **Một consumer group** chia partition cho các member; thêm consumer vượt quá partition count thì vô ích. + +### 5.2 Delivery semantics — biết cả ba + +- **At most once:** có thể mất message (commit offset trước khi xử lý). +- **At least once:** có thể trùng (xử lý trước khi commit) — default thực tế; hãy làm consumer **idempotent** (dedupe bằng message key / offset). +- **Exactly once:** EOS của Kafka qua idempotent producer + transactional API, hoặc đơn giản hơn nhiều là "idempotent consumer + at-least-once." + +```java +// Idempotent consumer: dedupe bằng key ổn định, không hy vọng exactly-once +if (processedKeys.putIfAbsent(event.key(), event.offset()) != null) return; +``` + +### 5.3 Replication & durability + +- **Replication factor (RF)** và **ISR** (in-sync replicas). `acks=all` + RF≥3 sống sót mất broker không mất data. +- **Tại sao "acks=1" nguy hiểm** trên prod: leader có thể ack rồi chết trước khi replicate. + +### 5.4 Ordering & partitioning + +Nếu order quan trọng (payment, audit), bạn phải key bằng entity id để mọi event của nó vào cùng một partition. Đánh đổi: hot key tạo hot partition — đôi khi shard cái key. + +### 5.5 Failure mode thực tế + +- **Rebalance storms** khi consumer churn. Hiểu cooperative rebalancing. +- **Consumer lag** — monitor nó; là tín hiệu đầu tiên của consumer chậm hoặc producer surge. +- **Poison messages** — record xấu luôn fail; thiếu dead-letter queue (DLQ) thì nó block partition mãi mãi. Senior luôn build DLQ. + +```java +// Luôn có đường dead-letter +try { process(record); } +catch (PoisonException e) { sendToDlq(record, e); /* commit và đi tiếp */ } +``` + +## 6. System Design — bài capstone của senior + +Đây là nơi phán đoán bị test trong 45–60 phút. Quy trình quan trọng hơn đáp án. + +### 6.1 Vòng lặp phỏng vấn + +1. **Làm rõ yêu cầu & scope.** QPS? read vs write? latency budget? data size? consistency vs availability? +2. **Tính capacity tầm bậy.** "10M user, 100 read/user/ngày = 1B read/ngày ≈ 11.5k QPS." Con số dẹp việc đoán mò. +3. **Component cao cấp.** Clients → CDN → API gateway → services → cache → DB → async workers/queues. +4. **Đào sâu 1–2 chỗ** (theo quan tâm phỏng vấn viên). +5. **Xử lý failure.** Cái gì gãy trước? Làm sao degrade? + +### 6.2 Cache strategy + +- **Cache-aside (lazy):** app check cache, miss thì đọc DB, ghi ngược cache. Phổ biến nhất. Xử lý **cache stampede** (nhiều request miss cùng lúc) bằng request coalescing / single-flight; xử lý **stale data** bằng TTL; xử lý **thundering herd lúc expire** bằng jittered TTL. +- **Write-through / write-behind** khi cần consistency với store. +- **Cache invalidation** là phần khó — ưu tiên TTL + explicit invalidation on write. + +### 6.3 Consistency models + +- **CAP:** dưới partition, bạn chọn CP (consistency) hoặc AP (availability). Nói đúng — partition hiếm nhưng không thể tránh, nên chọn thật là "bỏ cái gì *trong lúc* partition." +- **Eventual consistency:** chấp nhận cho feed, count, search; nguy hiểm cho balance, inventory nếu không guard. + +### 6.4 Scalability patterns + +- **Horizontal scaling + stateless services** (session trong Redis, không phải local memory). +- **Sharding/partitioning** database theo tenant hoặc hash. +- **Async processing** để làm phẳng spike (Kafka + workers). +- **Backpressure & queues** để dependency chậm degrade thay vì sập. + +### 6.5 Ví dụ nhỏ: thiết kế URL shortener + +- Yêu cầu: 100M URL mới/ngày, 1B redirect/ngày, low latency. +- Key-value store, key = base62(encoded counter hoặc hash). Hash collision → retry với salt. +- Cache URL nóng trong Redis (đa số redirect đánh vào tập nhỏ). +- Redirect là 301/302 — 301 cho browser cache (ít load hơn) nhưng khó đổi hơn. +- Capacity: 1B redirect × ~500 bytes log ≈ 0.5 TB/ngày; plan retention/aggregation. + +### 6.6 Observability là một phần của thiết kế + +Senior tích hợp tracing (request ID xuyên service), metrics (RED: rate/errors/duration), và structured log từ ngày đầu. "Sau này thêm monitoring" là red flag. + +## 7. Cách thể hiện mình là senior + +- **Narrate trade-off.** "Tôi dùng at-least-once + idempotent consumer vì exactly-once nặng hơn và hiếm khi cần." +- **Thừa nhận không chắc chắn một cách trung thực.** "Tôi sẽ đo trước khi chốt RF=5; 3 thường đủ." +- **Gắn với experience thật.** "Trên prod chúng tôi từng thấy rebalance storm khi…" đánh bại đọc thuộc lòng. +- **Phản biện nhẹ nhàng.** Nếu design là microservices sớm quá, hãy nói và giải thích cái giá. + +## 8. Tự kiểm tra nhanh + +Trước phỏng vấn, đảm bảo bạn whiteboard được: + +- [ ] Một counter thread-safe dưới contention cao (và tại sao `AtomicLong` có thể nghẽn). +- [ ] Một hàm retry-with-backoff-and-jitter. +- [ ] Một câu SQL + index để sửa N+1 hoặc report chậm. +- [ ] Một Kafka consumer idempotent và có DLQ. +- [ ] Một system diagram cho service nặng read với cache, DB, và queue. +- [ ] Khác nhau giữa `synchronized`, `volatile`, và `AtomicReference` trong một câu mỗi cái. + +Nếu mấy cái đó thấy dễ, bạn sẵn sàng rồi. Nếu chưa, đó chính là các lỗ hổng cần vá trước tiên. + +Chúc may mắn — và nhớ: senior nghĩa là bạn có thể nói "tùy thuộc" và sau đó *bảo vệ được nó*.