use arena for refresh loop
This commit is contained in:
parent
0897eb850f
commit
dbe487e0f4
1 changed files with 40 additions and 11 deletions
51
src/main.zig
51
src/main.zig
|
|
@ -508,27 +508,31 @@ fn writeNewPortfolio(io: std.Io, allocator: std.mem.Allocator, path: []const u8,
|
||||||
|
|
||||||
// ── Refresh command ──────────────────────────────────────────
|
// ── Refresh command ──────────────────────────────────────────
|
||||||
|
|
||||||
fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process.Environ.Map) !void {
|
fn refresh(
|
||||||
var config = zfin.Config.fromEnv(io, allocator, environ);
|
io: std.Io,
|
||||||
|
gpa: std.mem.Allocator,
|
||||||
|
environ: *const std.process.Environ.Map,
|
||||||
|
) !void {
|
||||||
|
var config = zfin.Config.fromEnv(io, gpa, environ);
|
||||||
defer config.deinit();
|
defer config.deinit();
|
||||||
var svc = zfin.DataService.init(io, allocator, config);
|
var svc = zfin.DataService.init(io, gpa, config);
|
||||||
defer svc.deinit();
|
defer svc.deinit();
|
||||||
|
|
||||||
const portfolio_path = environ.get("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
const portfolio_path = environ.get("ZFIN_PORTFOLIO") orelse "portfolio.srf";
|
||||||
|
|
||||||
const data = std.Io.Dir.cwd().readFileAlloc(io, portfolio_path, allocator, .limited(10 * 1024 * 1024)) catch {
|
const data = std.Io.Dir.cwd().readFileAlloc(io, portfolio_path, gpa, .limited(10 * 1024 * 1024)) catch {
|
||||||
log.err("failed to read portfolio: {s}", .{portfolio_path});
|
log.err("failed to read portfolio: {s}", .{portfolio_path});
|
||||||
return error.ReadFailed;
|
return error.ReadFailed;
|
||||||
};
|
};
|
||||||
defer allocator.free(data);
|
defer gpa.free(data);
|
||||||
|
|
||||||
var portfolio = zfin.cache.deserializePortfolio(allocator, data) catch {
|
var portfolio = zfin.cache.deserializePortfolio(gpa, data) catch {
|
||||||
log.err("failed to parse portfolio", .{});
|
log.err("failed to parse portfolio", .{});
|
||||||
return error.ParseFailed;
|
return error.ParseFailed;
|
||||||
};
|
};
|
||||||
defer portfolio.deinit();
|
defer portfolio.deinit();
|
||||||
|
|
||||||
var symbols = std.StringHashMap(void).init(allocator);
|
var symbols = std.StringHashMap(void).init(gpa);
|
||||||
defer symbols.deinit();
|
defer symbols.deinit();
|
||||||
for (portfolio.lots) |lot| {
|
for (portfolio.lots) |lot| {
|
||||||
if (lot.security_type != .stock and lot.security_type != .watch) continue;
|
if (lot.security_type != .stock and lot.security_type != .watch) continue;
|
||||||
|
|
@ -577,6 +581,25 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process
|
||||||
try stdout.flush();
|
try stdout.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-iteration scratch arena. Wraps the gpa so iteration-scoped
|
||||||
|
// allocations get freed in bulk via `reset(.retain_capacity)` at
|
||||||
|
// the bottom of each loop iteration — no per-allocation defers,
|
||||||
|
// no manual `free` mistakes. Capacity is retained so we're not
|
||||||
|
// re-acquiring pages every symbol.
|
||||||
|
//
|
||||||
|
// What goes through this arena: any per-iteration scratch we
|
||||||
|
// allocate ourselves (CIK dupes, future symbol-derived strings).
|
||||||
|
// NOT the `FetchResult` payloads — those are owned by the
|
||||||
|
// service's gpa and freed via `result.deinit()` at their own
|
||||||
|
// call sites.
|
||||||
|
//
|
||||||
|
// We don't use juicy-main's `init.arena` because that's
|
||||||
|
// process-lifetime; resetting it mid-run would clobber other
|
||||||
|
// startup allocations.
|
||||||
|
var iter_arena_state = std.heap.ArenaAllocator.init(gpa);
|
||||||
|
defer iter_arena_state.deinit();
|
||||||
|
const iter_arena = iter_arena_state.allocator();
|
||||||
|
|
||||||
var it = symbols.iterator();
|
var it = symbols.iterator();
|
||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
const sym = entry.key_ptr.*;
|
const sym = entry.key_ptr.*;
|
||||||
|
|
@ -637,14 +660,16 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process
|
||||||
// had it — used to chain into entity_facts below.
|
// had it — used to chain into entity_facts below.
|
||||||
// NotFound is logged as `n/a` (symbol genuinely has no
|
// NotFound is logged as `n/a` (symbol genuinely has no
|
||||||
// Wikidata entry) and doesn't flip sym_ok.
|
// Wikidata entry) and doesn't flip sym_ok.
|
||||||
var cik_buf: ?[]u8 = null;
|
//
|
||||||
defer if (cik_buf) |b| allocator.free(b);
|
// The CIK dupe goes through `iter_arena`. No defer-free
|
||||||
|
// needed — the arena is reset at end of iteration.
|
||||||
|
var cik_str: ?[]const u8 = null;
|
||||||
try printRateLimitWait(&svc, stdout);
|
try printRateLimitWait(&svc, stdout);
|
||||||
if (svc.getClassification(sym, .{})) |result| {
|
if (svc.getClassification(sym, .{})) |result| {
|
||||||
defer result.deinit();
|
defer result.deinit();
|
||||||
if (result.data.len > 0) {
|
if (result.data.len > 0) {
|
||||||
if (result.data[0].cik) |cik| {
|
if (result.data[0].cik) |cik| {
|
||||||
cik_buf = allocator.dupe(u8, cik) catch null;
|
cik_str = iter_arena.dupe(u8, cik) catch null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try stdout.print(", classification ok", .{});
|
try stdout.print(", classification ok", .{});
|
||||||
|
|
@ -678,7 +703,7 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process
|
||||||
// have an EDGAR CIK from the ticker map (production
|
// have an EDGAR CIK from the ticker map (production
|
||||||
// zfin chains entity_facts off Wikidata's CIK, so the
|
// zfin chains entity_facts off Wikidata's CIK, so the
|
||||||
// server warms the cache the same way).
|
// server warms the cache the same way).
|
||||||
if (cik_buf) |cik| {
|
if (cik_str) |cik| {
|
||||||
try printRateLimitWait(&svc, stdout);
|
try printRateLimitWait(&svc, stdout);
|
||||||
if (svc.getEntityFacts(cik, .{})) |result| {
|
if (svc.getEntityFacts(cik, .{})) |result| {
|
||||||
result.deinit();
|
result.deinit();
|
||||||
|
|
@ -696,6 +721,10 @@ fn refresh(io: std.Io, allocator: std.mem.Allocator, environ: *const std.process
|
||||||
try stdout.flush();
|
try stdout.flush();
|
||||||
|
|
||||||
if (sym_ok) success_count += 1 else fail_count += 1;
|
if (sym_ok) success_count += 1 else fail_count += 1;
|
||||||
|
|
||||||
|
// Reset per-iteration scratch. Retains capacity so the
|
||||||
|
// next iteration's allocations reuse the same pages.
|
||||||
|
_ = iter_arena_state.reset(.retain_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
try stdout.print("\nRefresh complete: {d} ok, {d} failed\n", .{ success_count, fail_count });
|
try stdout.print("\nRefresh complete: {d} ok, {d} failed\n", .{ success_count, fail_count });
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue