Machine Coding Problem

Log Search (ELK-style)

maco30maco60macoAlldevopsindex-rotationhot-warm
Commonly Asked By:SplunkElasticDatadog

Requirements & Scope

Functional Specifications

  • Time-based Index Rotation: Seal indices and launch fresh segments dynamically as query timelines progress.
  • Hot-Warm-Cold Storage tiers: Move older segments across storage mediums (Fast SSD โ†’ HDD โ†’ Cloud Object S3 bucket) based on index age.
  • Inverted Index Keyword Search: Parse and tokenize incoming messages to build fast index references for text searching.
  • Unified Query routing: Route query requests only to index segments overlapping with the target range.

Log Ingestion Structure

Structural layout detailing segment directories, index maps, and rotation triggers:

Loading...

Design Patterns

  • State Pattern (Tier Management): Adapts segment read/write behaviors and latency metrics according to their current tier state.
  • Observer Pattern (Log Watchers): Publishes ingestion errors directly to console trackers to launch immediate system sweeps.

Execution Workflows

Log Ingest & Rotation

  1. Ingest Event: Parse level, host origin, and payload details.
  2. Rotation Boundary check: Verify whether the active timestamp exceeds the current HOT segment's ending boundary.
  3. Trigger Rotation: If expired, seal the active segment, spin up a new hot segment block, and execute tier migrations on older indices.
  4. Tokenize & Index: Split logs into lowercase word strings and map references in inverted indexes.

Clean Code Blueprint

Production reference implementations demonstrating index rotations, inverted keyword maps, tier shifts, and query range intersections:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.util.*;
import java.util.concurrent.*;

enum StorageTier { HOT, WARM, COLD, PURGED }

class LogEvent {
    private final String id;
    private final long timestamp;
    private final String level;
    private final String message;
    private final String service;

    public LogEvent(long timestamp, String level, String message, String service) {
        this.id = UUID.randomUUID().toString();
        this.timestamp = timestamp;
        this.level = level;
        this.message = message;
        this.service = service;
    }

    public String getId() { return id; }
    public long getTimestamp() { return timestamp; }
    public String getLevel() { return level; }
    public String getMessage() { return message; }
    public String getService() { return service; }
}

class IndexSegment {
    private final String id;
    private final long startTime;
    private final long endTime;
    private StorageTier tier = StorageTier.HOT;
    private final List<LogEvent> logs = new CopyOnWriteArrayList<>();
    private final ConcurrentHashMap<String, List<LogEvent>> invertedIndex = new ConcurrentHashMap<>();

    public IndexSegment(String id, long startTime, long endTime) {
        this.id = id;
        this.startTime = startTime;
        this.endTime = endTime;
    }

    public void index(LogEvent log) {
        logs.add(log);
        String[] tokens = log.getMessage().toLowerCase().split("\s+");
        for (String token : tokens) {
            invertedIndex.computeIfAbsent(token, k -> new CopyOnWriteArrayList<>()).add(log);
        }
    }

    public List<LogEvent> search(String keyword) {
        return invertedIndex.getOrDefault(keyword.toLowerCase(), Collections.emptyList());
    }

    public String getId() { return id; }
    public long getStartTime() { return startTime; }
    public long getEndTime() { return endTime; }
    public StorageTier getTier() { return tier; }
    public void setTier(StorageTier tier) { this.tier = tier; }
    public List<LogEvent> getLogs() { return logs; }
    public Map<String, List<LogEvent>> getInvertedIndex() { return invertedIndex; }
}

class LogSearchEngine {
    private final List<IndexSegment> segments = new CopyOnWriteArrayList<>();
    private IndexSegment currentHotSegment;
    private final long rotationIntervalMs;

    public LogSearchEngine(long rotationIntervalMs) {
        this.rotationIntervalMs = rotationIntervalMs;
        rotate(); // Initialize first segment
    }

    public synchronized void ingest(LogEvent log) {
        if (log.getTimestamp() > currentHotSegment.getEndTime()) {
            rotate();
        }
        currentHotSegment.index(log);
    }

    public synchronized void rotate() {
        long now = System.currentTimeMillis();
        String nextId = "idx-" + (segments.size() + 1);
        IndexSegment next = new IndexSegment(nextId, now, now + rotationIntervalMs);
        segments.add(next);
        currentHotSegment = next;
        
        // Migrate older segments across storage tiers
        manageTiers();
    }

    public synchronized void manageTiers() {
        long now = System.currentTimeMillis();
        for (IndexSegment segment : segments) {
            if (segment == currentHotSegment) continue;
            long age = now - segment.getEndTime();

            // Storage tier thresholds based on time age (simulated thresholds)
            if (age > 5000L) {
                segment.setTier(StorageTier.PURGED);
            } else if (age > 3000L) {
                segment.setTier(StorageTier.COLD);
            } else if (age > 1000L) {
                segment.setTier(StorageTier.WARM);
            }
        }
    }

    public List<LogEvent> query(String keyword, long start, long end) {
        List<LogEvent> results = new ArrayList<>();
        for (IndexSegment segment : segments) {
            if (segment.getTier() == StorageTier.PURGED) continue;

            // Target segment only if query range overlaps segment time
            if (segment.getStartTime() <= end && segment.getEndTime() >= start) {
                results.addAll(segment.search(keyword));
            }
        }
        return results;
    }

    public List<IndexSegment> getSegments() {
        return segments;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== ELK-STYLE LOG SEARCH ENGINE SIMULATION ===");
        // Rotation every 1000 milliseconds for fast testing
        LogSearchEngine engine = new LogSearchEngine(1000L);

        // Ingest into first HOT index segment
        System.out.println("Ingesting logs into hot segment...");
        long t1 = System.currentTimeMillis();
        engine.ingest(new LogEvent(t1, "INFO", "gateway server initialized successfully", "gateway"));
        engine.ingest(new LogEvent(t1 + 100, "ERROR", "database connection timeout occurred", "auth-service"));
        
        // Wait to trigger index rotation
        Thread.sleep(1200L);
        System.out.println("\nIngesting logs post-rotation...");
        long t2 = System.currentTimeMillis();
        engine.ingest(new LogEvent(t2, "WARN", "high cpu utilization threshold warning", "checkout-service"));
        engine.ingest(new LogEvent(t2 + 100, "INFO", "user transaction processed successfully", "payment-service"));

        // Wait to trigger tier shifts
        Thread.sleep(2000L);
        System.out.println("\nTriggering explicit segment rotation to process migrations...");
        engine.rotate();

        // Print Segment Tiers
        System.out.println("\n--- Segment Tiers Assessment ---");
        for (IndexSegment seg : engine.getSegments()) {
            System.out.println("Segment " + seg.getId() + " | Tier: " + seg.getTier() + " | Logs count: " + seg.getLogs().size());
        }

        // Full text search querying
        System.out.println("\n--- Querying 'database' over time range ---");
        List<LogEvent> databaseLogs = engine.query("database", t1 - 1000, t2 + 2000);
        for (LogEvent log : databaseLogs) {
            System.out.println("[" + log.getLevel() + "] " + log.getService() + ": " + log.getMessage());
        }

        System.out.println("\n--- Querying 'successfully' over time range ---");
        List<LogEvent> successfulLogs = engine.query("successfully", t1 - 1000, t2 + 2000);
        for (LogEvent log : successfulLogs) {
            System.out.println("[" + log.getLevel() + "] " + log.getService() + ": " + log.getMessage());
        }

        System.out.println("\nSimulation completed successfully.");
    }
}

Interactive Simulator

โ– ELK-style Index Segment Rotator

Time-based index sealings, inverted keyword maps, and hot-warm-cold storage migrations.

๐Ÿ›  Storage Tiers Pipeline (SSD โ”€โ”€โ–บ HDD โ”€โ”€โ–บ Object Archive)

๐Ÿ”ฅ HOT Tier (Fast SSD)<5ms
idx-3 (Writing)
1 logs captured
๐Ÿ“ฆ WARM Tier (Storage HDD)~25ms
idx-2 (Sealed)
1 logs cached
โ„๏ธ COLD Tier (Cloud S3)~200ms
idx-1 (Archived)
2 logs zipped

๐Ÿ” Log Search & Inverted Index Query

๐Ÿ“– Segment Inverted Index Dictionary Inspector

Segment idx-1 Inverted Index
"gateway" โ”€โ”€โ–บ [log-1]
"server" โ”€โ”€โ–บ [log-1]
"startup" โ”€โ”€โ–บ [log-1]
"success" โ”€โ”€โ–บ [log-1]
"database" โ”€โ”€โ–บ [log-2]
"pool" โ”€โ”€โ–บ [log-2]
"exhausted" โ”€โ”€โ–บ [log-2]
"exception" โ”€โ”€โ–บ [log-2]
Segment idx-2 Inverted Index
"high" โ”€โ”€โ–บ [log-3]
"cpu" โ”€โ”€โ–บ [log-3]
"loading" โ”€โ”€โ–บ [log-3]
"checkout" โ”€โ”€โ–บ [log-3]
Segment idx-3 Inverted Index
"user" โ”€โ”€โ–บ [log-4]
"login" โ”€โ”€โ–บ [log-4]
"successful" โ”€โ”€โ–บ [log-4]
"alice" โ”€โ”€โ–บ [log-4]

๐Ÿ“ฅ Log Ingest pipeline

Log Level:
Service Origin:
Payload message:
ELK Rotator Audits & Ingestion Logs
[13:50:00] ELK Cluster online. Rotator interval target set to 2 minutes.
[13:50:02] Indices loaded: HOT (Fast SSD), WARM (Storage HDD), COLD (Archive S3).

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...