const std = @import("std"); const mongo = @import("mongo"); const usage = \\mongo-lite — lightweight MongoDB-compatible document database \\ \\usage: mongo-lite [options] \\ --port listen port (default 27017) \\ --bind bind address (default 127.0.0.1) \\ --db database file (default mongo-lite.log) \\ --ttl-sweep-secs \\ seconds between TTL index sweeps (default 60, 0 disables) \\ --compact-threshold \\ minimum log bytes between compactions; suffixes k/m/g \\ (default 16m). The actual trigger also scales with the \\ live data size, so total rewrite traffic stays linear \\ no matter how large the collection grows. \\ --help show this help \\ ; /// Parse a size with k/m/g suffix ("16m", "1g", "512k"), or null. fn parse_size_suffix(v: []const u8) ?u64 { const s = std.mem.trim(u8, v, " \t"); if (s.len == 0) return null; var mult: u64 = 1; var num_part = s; switch (s[s.len - 1]) { 'k', 'K' => { mult = 1024; num_part = s[0 .. s.len - 1]; }, 'm', 'M' => { mult = 1024 * 1024; num_part = s[0 .. s.len - 1]; }, 'g', 'G' => { mult = 1024 * 1024 * 1024; num_part = s[0 .. s.len - 1]; }, else => {}, } const n = std.fmt.parseInt(u64, num_part, 10) catch return null; return n * mult; } pub fn main(init: std.process.Init) !void { var port: u16 = 27017; var bind_ip: []const u8 = "127.0.0.1"; var db_path: []const u8 = "mongo-lite.log"; var ttl_sweep_secs: i64 = 60; var compact_threshold: u64 = 16 * 1024 * 1024; var it = std.process.Args.Iterator.init(init.minimal.args); defer it.deinit(); _ = it.next(); // program name while (it.next()) |arg| { if (std.mem.eql(u8, arg, "--port")) { const v = it.next() orelse return error.MissingValue; port = std.fmt.parseInt(u16, v, 10) catch { std.debug.print("mongo-lite: invalid port '{s}'\n", .{v}); return error.InvalidPort; }; } else if (std.mem.eql(u8, arg, "--bind")) { bind_ip = it.next() orelse return error.MissingValue; } else if (std.mem.eql(u8, arg, "--db")) { db_path = it.next() orelse return error.MissingValue; } else if (std.mem.eql(u8, arg, "--ttl-sweep-secs")) { const v = it.next() orelse return error.MissingValue; // i64 is the width std.Io.Duration.fromSeconds takes, so the // value reaches the sweeper without a cast; negatives are the // only thing parseInt would otherwise let through. ttl_sweep_secs = std.fmt.parseInt(i64, v, 10) catch -1; if (ttl_sweep_secs < 0) { std.debug.print("mongo-lite: invalid ttl sweep interval '{s}'\n", .{v}); return error.InvalidTtlSweepSecs; } } else if (std.mem.eql(u8, arg, "--compact-threshold")) { const v = it.next() orelse return error.MissingValue; const parsed = parse_size_suffix(v) orelse { std.debug.print("mongo-lite: invalid compact threshold '{s}'\n", .{v}); return error.InvalidCompactThreshold; }; if (parsed < 1024 * 1024) { std.debug.print("mongo-lite: compact threshold must be at least 1m\n", .{}); return error.InvalidCompactThreshold; } compact_threshold = parsed; } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { try std.Io.File.writeStreamingAll(.stdout(), init.io, usage); return; } else { std.debug.print("mongo-lite: unknown option '{s}'\n{s}", .{ arg, usage }); return error.UnknownOption; } } const oid_gen = mongo.bson.ObjectIdGen.init(init.io); var engine = try mongo.db.Engine.open(init.gpa, init.io, db_path); defer engine.deinit(); engine.compact_threshold = compact_threshold; std.debug.print("mongo-lite: opened database '{s}' (compact threshold {d})\n", .{ db_path, compact_threshold }); var server = mongo.server.Server{ .gpa = init.gpa, .port = port, .bind_ip = bind_ip, .oid_gen = oid_gen, .connection_counter = .init(1), .engine = &engine, .start_time = std.Io.Timestamp.now(init.io, .real), .ttl_sweep_secs = ttl_sweep_secs, }; try server.run(); }