Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/data/blog/en/interview/database-senior.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,67 @@ The database is the layer that actually decides whether your system scales. Inte

> Mindset: recite facts and you're mid-level. Walk through a tradeoff with real numbers and a production failure mode, and you've earned the "senior" checkbox. Every section below ends with the drill an interviewer actually runs.

## Interview question ladder (Junior → Mid → Senior)

> Drill these out loud. Junior = "do you know the concept"; Mid = "do you know the tradeoffs"; Senior = "can you defend a decision under pressure, with a number and a postmortem."

### Junior — foundations

- **Q: What's the difference between a clustered and a non-clustered index?**
A: A clustered index _is_ the table — rows are stored in its order (InnoDB's `PRIMARY KEY`), so there's exactly one per table; lookups by PK are one B-tree walk to the row. A non-clustered (secondary) index is a separate B-tree whose leaves point back to the clustered key, so a secondary-index lookup is two hops: index → clustered key → row.

- **Q: Name the four SQL transaction isolation levels.**
A: Read uncommitted, read committed, repeatable read, serializable — in increasing strictness. They trade concurrency for anomaly prevention.

- **Q: What does a primary key do that a unique constraint doesn't?**
A: A PK is the clustered key (InnoDB) — it defines physical row order and is non-null + unique. A unique constraint is just a non-clustered uniqueness guarantee; you can have several.

- **Q: What is a foreign key, and what does `ON DELETE CASCADE` do?**
A: A FK constrains a column to exist in another table's referenced column. `CASCADE` makes deleting the parent delete (or null, with `SET NULL`) the dependent rows — convenient, but a mass delete can lock/cascade harder than you expect.

- **Q: Why does `SELECT *` hurt?**
A: It pulls every column (more I/O, more network), defeats covering indexes (the index can't satisfy the query alone), and breaks when columns are added/removed. Name the columns you need.

### Mid — tradeoffs & pitfalls

- **Q: Why is `WHERE YEAR(created_at) = 2026` a full scan even when `created_at` is indexed?**
A: A function on the column hides it from the B-tree, so the optimizer can't do a range seek — it scans every row, applies the function, filters. Rewrite as a raw-column range: `created_at >= '2026-01-01' AND created_at < '2027-01-01'`. Same trap: `LIKE '%x%'`, arithmetic on the column, implicit casts.

- **Q: When is a composite index useful, and what's the leading-column rule?**
A: Composite indexes serve queries that filter on a _prefix_ of the columns, left to right (the leftmost-prefix rule). `(a, b, c)` helps `WHERE a=?`, `(a,b)=?`, `(a,b,c)=?` but NOT `WHERE b=?` alone. Put the most selective / most-filtered-leading column first, but also the one that benefits equality predicates.

- **Q: N+1 query — what is it and how do you kill it?**
A: You fetch N parents, then one query per parent for its children = N+1 round trips. Fix: a single `JOIN`/`IN` batch, or `@BatchSize`/`fetch join` in ORM. The tell: latency that never shows in any single slow-query log because each call is ~1 ms.

- **Q: Why is a 2000-connection pool worse than a 50-connection one?**
A: Connections are a _bounded_ resource the DB must schedule. Past the DB's `max_connections` every new request times out; more connections also mean more context-switch and lock contention on the DB side. Size by Little's law (`TPS × avg_query_time`), not by box core count.

- **Q: Read committed vs repeatable read — what anomaly does each still allow?**
A: Read committed still allows _non-repeatable reads_ (same row differs between reads in the same txn). Repeatable read still allows _phantom reads_ (a range query returns different rows). Serializable prevents both — at the cost of concurrency (often via range locks / SSI).

### Senior — design & defense

- **Q: Size the connection pool for a service doing 1,000 req/s with 20 ms avg query time. Now what if 10% of calls take 5 s?**
A: `1000 × 0.02s = 20` connections is the steady-state number; `cores × 10` is a fine starting heuristic and HikariCP defaults to 10. But the 10% at 5 s case needs `1000 × 0.1 × 5 = 500` connections _if_ every slow call holds one — which means a handful of slow queries can exhaust the pool and stall the 90% fast path. The senior move is a _separate_ bounded pool (or timeout + circuit breaker) for the slow path so it can't starve the fast one.

- **Q: "Indexes make everything fast." Defend or refute — with the write-side cost.**
A: Refute. Every index is maintained on every `INSERT`/`UPDATE`/`DELETE`: more B-tree walks, more page splits, more WAL. A write-heavy table with 8 indexes pays 8× the index-maintenance tax and slower inserts. The defense: index for the queries you actually run; drop the vanity indexes; consider a read replica for heavy analytical reads.

- **Q: A report says "the DB averages 0.1 ms but the app takes 800 ms." Where do you look first?**
A: The _pool_, not the DB. If the thread pool and connection pool both queue, requests wait in line for a connection while the DB sits idle. Check pool saturation, `connectionTimeout`, and whether `wait` time dwarfs `query` time. The fix is rarely "bigger DB."

- **Q: Walk me through a phantom read appearing in production and how you closed it.**
A: A batch processes "all unpaid orders," another txn inserts a new unpaid order in the same range mid-batch → the batch misses it (or double-counts on retry). Defense: `REPEATABLE READ`/`SERIALIZABLE` with range locks, or `SELECT … FOR UPDATE SKIP LOCKED` to claim rows atomically so concurrent workers don't collide. Name the isolation level and the lock type.

- **Q: You need to add an index to a 2-billion-row table with zero downtime. How?**
A: Online/DDL tools (`CREATE INDEX CONCURRENTLY` in Postgres; InnoDB online DDL with `ALGORITHM=INPLACE, LOCK=NONE`) build the index without blocking writes — but they still add load and can take hours at scale; do it in a maintenance window, monitor replication lag, and have a rollback. Never `LOCK=TABLE` on a hot table.

#### Self-check

- [ ] Junior: explain clustered vs non-clustered, the 4 isolation levels, PK vs unique, FK cascade, why `SELECT *` hurts.
- [ ] Mid: rewrite a function-on-column predicate to a range, state the leftmost-prefix rule, kill an N+1, size a pool with Little's law, name the anomaly each isolation level still allows.
- [ ] Senior: defend connection-pool sizing under a slow-query tail, quantify the write-side index cost, trace a phantom-read incident, and add a 2B-row index online without downtime.

## 1. Indexing — where interviews go to die

"Add an index" is the beginner answer. The senior answer explains why the index is three or four B-tree levels tall, which columns go in which order, why the optimizer still refuses to touch it, and what a hot index costs you on every write you didn't plan for.
Expand Down
61 changes: 61 additions & 0 deletions src/data/blog/en/interview/java-core-senior.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,67 @@ A junior knows Java syntax. A senior knows **what the JVM is doing, why it behav

> Mindset: "it depends, and here's the trade-off" beats reciting facts every time. The moment you answer with a tradeoff, a number, or a postmortem instead of a definition, you've cleared the bar.

## Interview question ladder (Junior → Mid → Senior)

> Drill these out loud. Junior = "do you know the concept"; Mid = "do you know the tradeoffs"; Senior = "can you defend a decision under pressure, with a number and a postmortem."

### Junior — foundations

- **Q: What's the difference between `==` and `.equals()` in Java?**
A: `==` compares references (are they the same object); `.equals()` compares _value_ (overridable). And `Integer.valueOf` caches -128..127, so `==` on boxed integers "works" in that range and bites at 128 — a bug that passes tests and dies in prod.

- **Q: What are the four pillars of OOP?**
A: Encapsulation, abstraction, inheritance, polymorphism — but a senior can say _why_ each one exists (e.g. encapsulation bounds the blast radius of change), not just the words.

- **Q: What's the difference between `ArrayList` and `LinkedList`?**
A: `ArrayList` is a growable array — O(1) random access, O(n) insert/delete in the middle; `LinkedList` is a doubly-linked list — O(1) add/remove at ends, O(n) access. Default to `ArrayList` unless you insert/remove constantly at the head.

- **Q: What does `final` mean on a class / method / variable?**
A: `final` class = no subclass; `final` method = no override; `final` variable = assigned once. A `final` reference can still mutate the object it points to (the reference is fixed, not the state).

- **Q: What's the difference between `String`, `StringBuilder`, and `StringBuffer`?**
A: `String` is immutable (every concat allocates). `StringBuilder` is mutable and non-synchronized (fast, single-thread). `StringBuffer` is the synchronized twin (use only when shared across threads). In a loop, `StringBuilder` avoids a storm of throwaway `String` objects.

### Mid — tradeoffs & pitfalls

- **Q: Why is double-checked locking broken without `volatile`?**
A: The unsynchronized read can observe a _partially constructed_ singleton — the reference store can float before the constructor's writes, so another thread sees a non-null but half-built instance. `volatile` creates the constructor-write → read happens-before edge that closes it.

- **Q: `i++` on a `volatile int` — is it safe?**
A: No. `volatile` gives visibility + ordering, not atomicity. `i++` is read-modify-write; two threads can both read 41 and both write 42. Use `AtomicInteger` (one CAS) or `LongAdder` under heavy contention (several× faster because it stripes across cells).

- **Q: What is false sharing and how do you fix it?**
A: Two independent fields on the same 64-byte cache line ping-pong across cores on every write, even in lock-free code. A per-thread `long[]` counter is the classic victim. `@Contended` (JEP 142) pads fields onto separate lines; a coherence miss costs ~100 ns per ping.

- **Q: `synchronized` vs `ReentrantLock` — when do you reach for which?**
A: Uncontended `synchronized` is nearly free (a mark-word update, ~tens of ns); contended pays a park/unpark into the kernel (microseconds). `ReentrantLock` adds `tryLock(timeout)`, multiple `Condition`s, and fairness — but you must `unlock()` in `finally` and prefer `tryLock(2, SECONDS)` so a stuck lock can't hang the thread.

- **Q: `CompletableFuture.thenApplyAsync` runs on which pool, and what breaks?**
A: On `ForkJoinPool.commonPool()` (parallelism = cores−1). Blocking work (JDBC, sleep) inside it starves the pool and stalls everything downstream even when the box is idle. Pass an explicit executor sized for the blocking work (or use virtual threads).

### Senior — design & defense

- **Q: A service does 2,000 req/s, each blocks ~50 ms in JDBC. Size the thread pool. Now 10% take 5 s — what changes?**
A: `2000 × 0.05 = 100` workers steady-state (Little's law). But the 10%-at-5s tail needs `2000 × 0.1 × 5 = 1000` workers _if_ every slow call holds one — so a few slow queries exhaust the pool and stall the fast 90%. Senior move: bound the pool, use `CallerRunsPolicy` for backpressure, and isolate the slow path on its own executor with a deadline.

- **Q: When do virtual threads help, when not, and what still pins a carrier after JDK 24?**
A: They help I/O-bound blocking (millions of concurrent HTTP/DB calls). They don't help CPU-bound work (still N CPUs). After JEP 491 (JDK 24) `synchronized` no longer pins; residual pinning is native frames (JNI/FFM), class loading, and local file I/O on Linux. A `ReentrantLock` never pinned — `LockSupport.park` unmounts the virtual thread.

- **Q: You see `OutOfMemoryError: Metaspace` after a redeploy with no class added. First command?**
A: `jcmd <pid> VM.native_memory` and `-XX:MaxMetaspaceSize`. The cause is a classloader leak: something roots the old loader (static field, JDBC driver in `DriverManager`, cached proxy) so metadata never unloads. A few MB per redeploy becomes 2 GB after a hundred deploys.

- **Q: A hot method is still slow after 10 minutes of traffic. What's the JIT possibly doing, and how do you prove it?**
A: Tiered compilation (C1→C2) with OSR; it may be **megamorphic** (too many receiver types to inline) or stuck recompiling past `-XX:CompileThreshold`. Prove with `-Xlog:jit+compilation=debug` — you'll see recompilation churn, not "we need a bigger box."

- **Q: `SELECT * FROM orders WHERE YEAR(created_at)=2026` is slow though indexed — wait, that's SQL. What's the Java analog that bites just as hard?**
A: Calling a method inside a loop that does a `findById` per element (N+1), or `computeIfAbsent` recursively on the same key (Java 8 deadlocks the bin). Both hide O(n²) / deadlock behind innocent-looking loops — the senior tells are "where does it hide," not "does it exist."

#### Self-check

- [ ] Junior: `==` vs `.equals`, the four pillars, `ArrayList` vs `LinkedList`, `final`, `String` vs `StringBuilder`.
- [ ] Mid: fix DCL with `volatile`, why `volatile int++` isn't safe, false sharing + `@Contended`, `synchronized` vs `ReentrantLock`, the `commonPool` trap.
- [ ] Senior: size a pool with Little's law under a slow tail, state when virtual threads help/pin, diagnose a Metaspace leak, prove a JIT issue from a log, name the Java-side N+1 / `computeIfAbsent` traps.

## 1. Heap, GC, and the pause math

Expect: "What happens when you `new` an object?" A mid answer stops at "it goes on the heap." A senior talks about **where**, **how fast**, and **what the pause costs** — because that's what actually bites in production. Every question in this section has a numeric answer; interviewers listen for the number, not the noun.
Expand Down
Loading
Loading