Java Senior Interview Guide
Java Senior Interview Guide
Covers 70%+ of real interview questions with deep answers & code examples
Table of Contents
PART 1: CORE JAVA
• JVM Memory Model & Memory Areas
• Garbage Collection (All Algorithms)
• OutOfMemoryError -- Diagnosis & Fix
• HashMap & HashSet Internals
• ConcurrentHashMap vs SynchronizedMap
• ClassLoaders & Delegation Model
• Fail-Fast vs Fail-Safe Iterators
• ExecutorService & Thread Pools
• Virtual Threads (Java 21)
• Java Locks -- All Types
• Java 8 Stream API -- Deep Dive
• Java 17 & 21 New Features
• String Pool & Immutability
• equals() hashCode() Contract
• Exception Handling Best Practices
• Generics & Wildcards
Heap Shared All object instances, arrays. Split into Young Gen (Eden OutOfMemoryError: Java heap space,
+ S0/S1) and Old Gen. GC overhead limit
Stack (per thread) Per-thread Stack frames -- local vars, operand stack, method StackOverflowError (deep recursion)
references.
Metaspace (Java 8+) Shared Class metadata, method bytecode, static variables, OutOfMemoryError: Metaspace (class
interned Strings. loader leak)
Native / JNI Stack Per-thread C/C++ native method frames. Rarely seen
✔ Tip: Young Gen = Eden + Survivor0 + Survivor1. New objects go to Eden. After surviving a Minor GC they move to
Survivor spaces, and after N survivals (default 15) they are promoted to Old Gen.
■ Watch Out: PermGen was removed in Java 8 and replaced by Metaspace which auto-grows in native memory.
Class loader leaks cause Metaspace OOM.
Generational GC is the foundation: Minor GC collects Young Gen (fast, stop-the-world), Major/Full GC collects Old
Gen (slow, longer pause). Collectors differ in HOW they collect:
Collector Pause Type Java Version Best For Key JVM Flag
Parallel GC Stop-the-world Java 8 default Batch jobs, throughput over latency -XX:+UseParallelGC
(multi-thread)
ZGC Concurrent (<1ms Production Java Very large heaps (TB), ultra-low latency -XX:+UseZGC
pauses!) 15+
G1 GC internals: Heap is divided into equal-sized regions (1-32MB). Regions are dynamically assigned as Eden,
Survivor, or Old. G1 builds a 'remembered set' per region to track cross-region references, enabling it to collect the
most garbage-dense regions first (hence 'Garbage First').
✔ Tip: For low-latency microservices: use ZGC. For batch jobs: use Parallel GC. Default G1 is fine for most Spring
Boot apps.
Java heap space Too many live objects; memory leak; heap too Increase -Xmx; fix leak; use heap dump analysis
small
GC overhead limit exceeded GC spending >98% time but reclaiming <2% Same as above; likely leak or heap too small
Metaspace Class loader leak; too many dynamic Find leaking ClassLoader; set -XX:MaxMetaspaceSize
proxies/Lambdas generated
Direct buffer memory NIO [Link]() not freed; Netty Set -XX:MaxDirectMemorySize; check Netty leak
leak detector
Unable to create native thread OS thread limit hit; stack frames too large Reduce -Xss; increase OS ulimit; reduce thread count
// Common memory leak patterns -- know these! // 1. Static collection leak private static final
Map<String, Object> cache = new HashMap<>(); // grows forever! // 2. ThreadLocal leak in thread
pools ThreadLocal<SomeObject> tl = new ThreadLocal<>(); [Link](new SomeObject()); // If you
never call [Link](), the object lives as long as the thread // Thread pool threads live
forever -> LEAK. Always: try { [Link](value); // ... use it } finally { [Link](); // ALWAYS
clean up } // 3. Inner class holding outer reference class Outer { class Inner { } // Inner
holds implicit reference to Outer instance! // Use static nested class if Outer ref not needed
static class SafeInner { } }
HashMap uses an array of buckets (Node<K,V>[]). The bucket index = (n-1) & hash(key).
put() flow:
• 1. Compute hash: hashCode() is further spread with (h ^ (h >>> 16)) to reduce collisions
• 2. Find bucket index
• 3. If bucket empty: place new Node
• 4. If key exists (equals() match): replace value
• 5. Else: add to end of linked list
• 6. Java 8+: If bucket chain length >= 8 AND total size >= 64: convert to Red-Black Tree (O(log n) worst
case vs O(n) before)
• 7. If size > capacity * loadFactor (0.75): resize -- double capacity and rehash all entries
✔ Tip: Java 8 treeification threshold = 8 (list to tree). Untreeify threshold = 6 (tree back to list). This prevents DOS
attacks via hash flooding.
Use case Never use Simple single-map safety Production concurrent code
(legacy)
Java uses Parent Delegation Model: before loading a class, a ClassLoader delegates to its parent. Only if the
parent cannot find it, does the child try.
// ClassLoader hierarchy: // Bootstrap CL (native, loads [Link] / [Link] module) // +-
Platform CL (formerly Extension CL -- loads javax.*, etc.) // +- Application CL (loads your
classpath) // +- Custom ClassLoaders (OSGi, app servers, plugin systems) // Why parent
delegation? // 1. Security: rogue [Link] can't be loaded // 2. No duplicate class
loading // 3. Consistent behaviour // ClassLoader leak (common in app servers -- Tomcat,
JBoss): // Custom ClassLoader loads classes -> classes hold reference to CL // If CL not GC'd
-> all loaded classes stay in Metaspace -> OOM Metaspace // Fix: ensure ClassLoader is
dereferenced when app is undeployed
✔ Tip: In OSGi or Spring Boot's executable JAR, custom class loaders are used. Tomcat has a ClassLoader per
deployed webapp to isolate apps.
Simple definition: Virtual threads are lightweight threads managed by the JVM (not the OS). They are cheap --
you can create millions of them. They map M virtual threads onto N OS (platform) threads automatically.
■ Watch Out: PINNING: If virtual thread enters a synchronized block or calls native code, it 'pins' to its carrier OS
thread. Use ReentrantLock instead of synchronized to avoid pinning in virtual thread code.
synchronized Simple critical sections, no timeout needed High contention, need tryLock
Lock Type Use When Avoid When
ReentrantLock Need tryLock, timed lock, interruptible lock, Simple cases (overhead)
fairness
StampedLock Very read-heavy, optimistic read suits Need reentrancy (not reentrant!)
volatile Single flag, status variable (write once, read Compound check-then-act
many)
Q: Deep dive into Java Stream API -- internals, performance, parallel streams
Streams are lazy pipelines. Intermediate ops build a pipeline descriptor; terminal op triggers traversal in a single
pass (fused).
// -- INTERMEDIATE (lazy) ----------------------------------------- // filter, map, flatMap,
distinct, sorted, peek, limit, skip, mapToInt, etc. // -- TERMINAL (eager, triggers pipeline)
-------------------------- // collect, forEach, reduce, count, findFirst, findAny, anyMatch, //
allMatch, noneMatch, min, max, toArray, sum, average // -- Short-circuiting terminals (can stop
early): ----------------- // findFirst(), findAny(), anyMatch(), allMatch(), noneMatch(),
limit() // REAL EXAMPLE -- common patterns: List<Order> orders = getOrders(); // Group by
status and count Map<Status, Long> countByStatus = [Link]()
.collect([Link](Order::getStatus, [Link]())); // Sum with mapping
double totalRevenue = [Link]() .filter(o -> [Link]() == COMPLETED)
.mapToDouble(Order::getAmount) .sum(); // FlatMap -- flatten nested lists List<String> allItems
= [Link]() .flatMap(o -> [Link]().stream()) .collect([Link]()); // --
PARALLEL STREAMS -------------------------------------------- List<String> results =
[Link]() .filter(s -> expensiveCheck(s)) .collect([Link]()); // Uses
[Link]() by default (shared!) // Good for: CPU-intensive, large data, no
shared mutable state // BAD for: I/O operations, small lists, operations with side effects //
Custom ForkJoinPool for parallel stream: ForkJoinPool pool = new ForkJoinPool(4);
[Link](() -> [Link]().forEach(this::process)).get();
■ Watch Out: Parallel streams use the COMMON ForkJoinPool shared by all parallel streams in the JVM. A slow
parallel operation blocks other parallel work. For I/O-bound parallel ops, use CompletableFuture with a custom
Executor instead.
// PROPAGATION (what happens when @Transactional method calls another): // REQUIRED (default)
-- join existing tx, or create new if none // REQUIRES_NEW -- always create new tx, suspend
current if exists // NESTED -- run in nested tx (savepoint), rollback to savepoint on failure
// SUPPORTS -- run in tx if exists, else non-transactional // NOT_SUPPORTED -- suspend current
tx, run non-transactionally // MANDATORY -- must have existing tx, else throw exception //
NEVER -- must NOT have tx, else throw exception @Transactional(propagation =
Propagation.REQUIRES_NEW) public void auditLog(String msg) { /* always its own tx, not rolled
back with parent */ } // ISOLATION LEVELS (prevents concurrent tx problems): //
READ_UNCOMMITTED -- can read dirty data (fastest, almost never use) // READ_COMMITTED -- can't
read dirty data; phantom reads possible (PostgreSQL default) // REPEATABLE_READ -- same query
returns same data in tx; phantoms possible // SERIALIZABLE -- full isolation; slowest
@Transactional(isolation = Isolation.REPEATABLE_READ) public void criticalOperation() { } // --
COMMON PITFALLS ----------------------------------------------------- // 1. Self-invocation --
@Transactional DOES NOT work when calling within same class! @Service public class OrderService
{ @Transactional public void place(Order o) { validate(o); // @Transactional on validate() is
IGNORED -- no proxy! } @Transactional public void validate(Order o) { } // tx annotations
ignored in self-calls } // FIX: inject self (hacky) or extract to separate bean // 2.
@Transactional on private methods -- does nothing (proxy can't intercept) // 3. Exception
handling -- only RuntimeException (unchecked) triggers rollback by default!
@Transactional(rollbackFor = [Link]) // also rollback on checked exceptions // 4.
Transaction not active across thread boundaries @Transactional public void process() {
[Link](() -> [Link](obj)); // NEW THREAD -- no tx! }
✔ Tip: Use @Transactional(readOnly = true) for read-only methods. Hibernate optimizes -- skips dirty check, sets
connection read-only hint. Helps performance.
N+1 is the most common JPA performance killer. It occurs when you load N entities and then JPA executes N
additional queries to load a related association.
// N+1 PROBLEM EXAMPLE: // Entity: @Entity public class Order { @OneToMany(fetch =
[Link]) // default for collections private List<OrderItem> items; } // In service:
List<Order> orders = [Link](); // 1 query: SELECT * FROM orders for (Order o :
orders) { [Link]().size(); // N queries: SELECT * FROM order_items WHERE order_id=? } //
Result: 1 + N queries. For 1000 orders = 1001 DB roundtrips! // -- DETECTION
---------------------------------------------------------- // 1. Enable SQL logging:
[Link]-sql=true // 2. Use Hibernate statistics:
[Link].generate_statistics=true // 3. Look for many identical SELECTs
in logs with different ID params // 4. Use p6spy or datasource-proxy to log all queries with
stack traces // -- FIXES -------------------------------------------------------------- // FIX
1: JPQL JOIN FETCH (most common) @Query("SELECT o FROM Order o JOIN FETCH [Link] WHERE
[Link] = :status") List<Order> findByStatusWithItems(@Param("status") Status status); // FIX
2: Entity Graph (flexible, reusable) @EntityGraph(attributePaths = {"items", "[Link]"})
List<Order> findAll(); // FIX 3: Batch fetching (Hibernate feature -- transparent)
@OneToMany(fetch = [Link]) @BatchSize(size = 100) // loads items for 100 orders at once
instead of 1-by-1 // FIX 4: DTO projection (best performance -- only fetch needed columns)
@Query("SELECT new [Link]([Link], [Link], COUNT(i)) " + "FROM Order o LEFT JOIN
[Link] i GROUP BY [Link], [Link]") List<OrderDTO> findOrderSummaries(); // CAUTION: JOIN FETCH
with pagination is WRONG: @Query("SELECT o FROM Order o JOIN FETCH [Link]") // + Pageable //
Hibernate warns: HHH90003004 -- firstResult/maxResults with FETCH JOIN -- applies in memory! //
FIX: Use @BatchSize or two-query approach for paginated collections
// JWT FLOW: // 1. Client POSTs /login with credentials // 2. Server validates, generates JWT:
[Link] // 3. Client stores JWT (memory or httpOnly cookie) // 4. Client sends
JWT in Authorization: Bearer <token> header // 5. Server validates JWT signature and expiry on
each request // JWT STRUCTURE: // Header: {"alg":"HS256","typ":"JWT"} // Payload:
{"sub":"userId","roles":["ADMIN"],"exp":1234567890} // Signature: HMACSHA256(base64(header) +
"." + base64(payload), secret) // SPRING SECURITY FILTER CHAIN: @Component public class
JwtAuthFilter extends OncePerRequestFilter { @Override protected void
doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws
ServletException, IOException { String header = [Link]("Authorization"); if (header !=
null && [Link]("Bearer ")) { String token = [Link](7); if
([Link](token)) { String username = [Link](token); UserDetails user
= [Link](username); UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, [Link]());
[Link](new WebAuthenticationDetailsSource().buildDetails(req));
[Link]().setAuthentication(auth); } } [Link](req, res); } }
// COMMON PITFALLS: // 1. JWT stored in localStorage -- vulnerable to XSS. Use httpOnly
cookies! // 2. No token refresh mechanism -- user logs out when token expires // 3. Not
validating JWT expiry on every request // 4. Weak secret -- use RS256 (asymmetric) for
distributed systems
// @Async -- runs method in separate thread pool @Configuration @EnableAsync public class
AsyncConfig { @Bean public Executor asyncExecutor() { ThreadPoolTaskExecutor exec = new
ThreadPoolTaskExecutor(); [Link](5); [Link](10);
[Link](100); [Link]("async-");
[Link](new CallerRunsPolicy()); // fallback [Link]();
return exec; } } @Service public class EmailService { @Async public CompletableFuture<Boolean>
sendEmail(String to, String body) { // runs in asyncExecutor thread pool boolean sent =
[Link](to, body); return [Link](sent); } } //
CompletableFuture -- compose async operations: CompletableFuture<User> userFuture =
[Link](userId); CompletableFuture<Account> accountFuture =
[Link](userId); // Run both concurrently, combine results:
[Link](userFuture, accountFuture) .thenApply(v -> new
Dashboard([Link](), [Link]())) .exceptionally(ex -> { [Link]("Failed",
ex); return [Link](); }); // Chain operations: [Link](() ->
fetchUser(id)) .thenApply(user -> enrichUser(user)) .thenAcceptAsync(user -> notify(user),
notificationExecutor) .exceptionally(ex -> { handleError(ex); return null; });
PART 3 -- MICROSERVICES & ARCHITECTURE
A Saga is a sequence of local transactions, each publishing events/messages that trigger next step. On failure,
compensating transactions undo previous steps.
// E-COMMERCE ORDER SAGA EXAMPLE: // Steps: CreateOrder -> ReserveInventory -> ChargePayment ->
ConfirmOrder // Compensations: CancelOrder <- ReleaseInventory <- RefundPayment // --
CHOREOGRAPHY (event-driven, no central coordinator) --------------- // Each service listens to
events and reacts @KafkaListener(topics = "order-created") public void
onOrderCreated(OrderCreatedEvent e) { try { [Link]([Link](), [Link]());
[Link](new InventoryReservedEvent([Link]())); } catch
(InsufficientStockException ex) { [Link](new
InventoryReservationFailedEvent([Link]())); // Order service listens to this -> cancels
order } } // PRO: Simple, loose coupling, no SPOF // CON: Hard to track overall saga state,
complex error flows, risk of cyclic events // -- ORCHESTRATION (central coordinator)
------------------------------- @Service public class OrderSagaOrchestrator { public void
execute(CreateOrderCommand cmd) { SagaContext ctx = new SagaContext([Link]()); try {
[Link](cmd); // Step 1 [Link](cmd); // Step 2
[Link](cmd); // Step 3 [Link]([Link]()); // Step 4 } catch
(PaymentFailedException e) { [Link](cmd); // compensate step 2
[Link]([Link]()); // compensate step 1 } } } // PRO: Easy to understand
flow, easy to add steps, central monitoring // CON: Orchestrator can become a bottleneck,
coupling // PRODUCTION TOOLS: [Link], Camunda, AWS Step Functions, Netflix Conductor
// STATES: // CLOSED (normal) -> failures counted -> threshold exceeded -> OPEN // OPEN (fail
fast) -> wait duration -> HALF_OPEN // HALF_OPEN (probe) -> test calls -> success -> CLOSED,
failure -> OPEN // Resilience4j (Spring Boot): // pom: resilience4j-spring-boot3
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback") @Retry(name =
"paymentService", fallbackMethod = "paymentFallback") @TimeLimiter(name = "paymentService")
public CompletableFuture<PaymentResult> chargePayment(PaymentRequest req) { return
[Link](() -> [Link](req)); } public
CompletableFuture<PaymentResult> paymentFallback(PaymentRequest req, Exception e) {
[Link]("Payment service failed, using fallback", e); return
[Link]([Link]()); } // [Link] config: /*
resilience4j: circuitbreaker: instances: paymentService: slidingWindowType: COUNT_BASED # or
TIME_BASED slidingWindowSize: 10 # last 10 calls failureRateThreshold: 50 # open if 50% fail
waitDurationInOpenState: 30s # wait 30s before half-open
permittedNumberOfCallsInHalfOpenState: 3 slowCallRateThreshold: 50 # also trips on slow calls
slowCallDurationThreshold: 3s retry: instances: paymentService: maxAttempts: 3 waitDuration:
500ms exponentialBackoffMultiplier: 2 # 500ms, 1s, 2s */
CQRS (Command Query Responsibility Segregation): Separate the read model (query) from the write model
(command). Commands change state; queries return data. They can use different databases.
Event Sourcing: Instead of storing current state, store the sequence of events that led to it. State is rebuilt by
replaying events.
// CQRS Example: // Write side: handles commands @CommandHandler public void
handle(PlaceOrderCommand cmd) { Order order = new Order([Link](), [Link]());
[Link](); [Link](order); // Write DB (normalized, ACID)
[Link](new OrderPlacedEvent(order)); } // Read side: handles queries (denormalized
for fast reads) @EventHandler public void on(OrderPlacedEvent event) { // Update read-optimized
projection OrderView view = new OrderView([Link](), [Link](), ...);
[Link](view); // Read DB (denormalized, Elasticsearch/Redis) } @QueryHandler
public OrderSummaryDTO handle(GetOrderSummaryQuery query) { return
[Link]([Link]()); // Fast! } // EVENT SOURCING: // Instead of:
UPDATE orders SET status='PAID' WHERE id=? // Store: {type: "OrderPaid", orderId: "123",
amount: 99.99, timestamp: ...} // Current state = replay all events from event store //
BENEFITS: Full audit log, time travel (replay to any point), event-driven // DRAWBACKS:
Eventual consistency, complex queries, event schema evolution
PART 4 -- PERFORMANCE & DEBUGGING
Q: WHY IS THE API SLOW? -- Complete systematic diagnosis (most important answer!)
This is the most common senior interview question. Walk through these areas systematically:
1. APPLICATION LAYER
• Thread pool exhaustion: All Tomcat threads busy. Fix: Increase [Link] (default 200), or
switch to Virtual Threads (Spring Boot 3.2), or use reactive (WebFlux)
• Inefficient code: O(n²) algorithm in request path, unnecessary loops, repeated computation. Use profiler to find
hotspots.
• Blocking calls on main thread: Synchronous HTTP calls to other services. Use async/parallel execution with
CompletableFuture.
• Missing caching: Expensive computation or DB call repeated on every request. Use @Cacheable with Redis.
• Large object serialization: Jackson serializing huge objects. Use DTOs with only needed fields.
4. EXTERNAL DEPENDENCIES
• Downstream service slow: HTTP call to payment/inventory service timing out. Detect: distributed tracing
(Jaeger/Zipkin). Fix: timeout + circuit breaker + fallback.
• Redis slow: Large keys, hot keys, network latency. Detect: Redis SLOWLOG, latency monitor. Fix: pipeline
commands, reduce key size.
• Kafka consumer lag: Consumer processing slower than producer. Detect: [Link]
--describe. Fix: increase partitions + consumers.
// DEBUGGING TOOLKIT -- commands to run: // 1. Thread dump (see what all threads are doing):
jstack <pid> > thread_dump.txt // Look for: BLOCKED threads, threads waiting on locks, thread
pool states // 2. Heap dump: jmap -dump:format=b,file=[Link] <pid> // Analyze with Eclipse
MAT // 3. GC log analysis: java -Xlog:gc*:file=[Link]:time,level,tags -jar [Link] // or use
GCViewer / [Link] for visualization // 4. CPU profiling (Java Flight Recorder -- zero
overhead!): java -XX:StartFlightRecording=duration=60s,filename=[Link] -jar [Link] //
Analyze with JDK Mission Control // 5. PostgreSQL slow query analysis: EXPLAIN (ANALYZE,
BUFFERS, FORMAT TEXT) SELECT ...; -- Look for: Seq Scan (no index), high Rows Removed, large
Buffers hit -- Enable pg_stat_statements for automatic slow query tracking // 6. Spring Boot
Actuator metrics: // GET /actuator/metrics/[Link] -- response times // GET
/actuator/metrics/[Link] -- DB pool status // GET /actuator/metrics/[Link]
-- GC pause times
// DETECTION STEPS: // 1. Monitor heap over time -- if it grows without bound and GC can't
reclaim -> leak // 2. Enable GC logging and watch: if OldGen keeps growing after Full GC ->
leak // 3. Take heap dump: -XX:+HeapDumpOnOutOfMemoryError OR jmap -dump:... // COMMON MEMORY
LEAK PATTERNS AND FIXES: // PATTERN 1: Static collections growing forever private static
Map<String, Session> activeSessions = new HashMap<>(); // FIX: Use Guava Cache with expiry, or
ConcurrentHashMap + manual cleanup private static Cache<String, Session> sessions =
[Link]() .expireAfterWrite(30, MINUTES) .maximumSize(10_000) .build(); // PATTERN
2: Event listeners not deregistered [Link](this); // on startup // NEVER calls
[Link](this); -> this object never GC'd! @PreDestroy public void cleanup() {
[Link](this); } // PATTERN 3: ThreadLocal in thread pool ThreadLocal<Connection>
localConn = new ThreadLocal<>(); // Pool thread is reused -> old Connection from previous
request still there! try { [Link](getConnection()); process(); } finally {
[Link](); // MANDATORY in thread pool context } // PATTERN 4: Hibernate 1st-level
cache in batch processing @Transactional public void processBatch(List<Long> ids) { for (Long
id : ids) { // 1 million IDs Entity e = [Link](id).get(); // all loaded into 1st-level
cache! process(e); // Heap fills with all 1M entities! } } // FIX: for (Long id : ids) { Entity
e = [Link](id).get(); process(e); [Link](e); // or [Link]()
periodically }
PART 5 -- KAFKA, DOCKER & POSTGRESQL
Core architecture: Topics are divided into Partitions. Each partition is an ordered, immutable log on disk.
Partitions are replicated across brokers (replication factor). One broker is leader per partition -- handles
reads/writes.
// KEY CONCEPTS: // PARTITION ORDERING: // Messages within a partition are strictly ordered. //
Across partitions: NO ordering guarantee. // Use partition key to keep related messages in same
partition: ProducerRecord<String, Order> record = new ProducerRecord<>("orders",
[Link](), order); // key = customerId // All orders for same customer -> same
partition -> processed in order // CONSUMER GROUPS: // Each consumer group independently reads
all messages from a topic. // Within a group: each partition is assigned to exactly one
consumer. // Parallelism = number of partitions. More partitions = more parallel consumers. //
If consumers > partitions, extra consumers are idle. // DELIVERY SEMANTICS: // At-most-once:
auto-commit offsets BEFORE processing (message lost if consumer crashes) // At-least-once:
commit AFTER processing (may reprocess on crash) -- most common // Exactly-once: Kafka
transactions (producer + consumer in same transaction) // SPRING KAFKA AT-LEAST-ONCE (default):
@KafkaListener(topics = "orders", groupId = "order-processor") public void consume(Order order)
{ processOrder(order); // if this fails, offset not committed, message redelivered } //
EXACTLY-ONCE with transactions: @Transactional // Spring manages Kafka + DB transaction
together @KafkaListener(topics = "orders") public void consume(Order order, Acknowledgment ack)
{ [Link](toEntity(order)); // DB write [Link](); // commit Kafka offset -- all
or nothing } // CONSUMER LAG: // lag = latest_offset - committed_offset // High lag = consumer
can't keep up with producer // Monitor: [Link] --bootstrap-server :9092
--describe --group mygroup // Fix: increase partitions, add more consumer instances (up to
partition count) // PRODUCER RELIABILITY: Properties props = new Properties();
[Link]("acks", "all"); // wait for all ISR replicas to ack [Link]("retries", 3);
[Link]("[Link]", "true"); // exactly-once producer (deduplicates retries)
// INDEX TYPES: // B-tree (default) -- equality, range queries: =, <, >, BETWEEN, LIKE
'prefix%' // Hash -- only equality: = (rarely needed, B-tree usually better) // GIN --
full-text search, JSONB @>, array operators // GiST -- geometric types, full-text, range
overlap // BRIN -- very large tables where physical order matches query order (time-series!)
CREATE INDEX idx_orders_status ON orders(status); -- B-tree CREATE INDEX idx_orders_created ON
orders(created_at DESC); -- for ORDER BY CREATE INDEX idx_orders_composite ON
orders(customer_id, status); -- composite CREATE INDEX idx_orders_covering ON
orders(customer_id) INCLUDE (status, total); -- covering CREATE INDEX idx_orders_partial ON
orders(status) WHERE status = 'PENDING'; -- partial -- COMPOSITE INDEX ORDER MATTERS! --
(customer_id, status) helps: WHERE customer_id=? AND status=? -- or: WHERE customer_id=? --
Does NOT help: WHERE status=? (can't skip first column) -- EXPLAIN ANALYZE output
interpretation: EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 42 AND
status = 'PENDING'; -- "Seq Scan" -> no index used -> add index! -- "Index Scan" -> good --
"Index Only Scan" -> best (covering index, no heap access) -- "Bitmap Heap Scan" -> medium --
"Rows Removed by Filter: 9999" -> index not selective enough -- VACUUM: -- PostgreSQL MVCC:
UPDATE doesn't overwrite, creates new row version (dead tuples) -- VACUUM removes dead tuples,
reclaims space -- autovacuum runs automatically -- but may need tuning for high-write tables --
VACUUM ANALYZE also updates planner statistics (run after bulk inserts!) -- LOCKS: SELECT pid,
query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event IS NOT NULL;
-- see waiting queries SELECT * FROM pg_locks WHERE NOT granted; -- see blocked locks
Q: Stream API -- Group employees by department, find top 3 earners per dept
500 Internal Server Unhandled exception -- should never reach client in prod
Error
Remember: Think out loud. Mention tradeoffs. Give real examples from your experience.