init: company-haness 설계
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""Append-only JSONL primitives for the Org OS state kernel."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from contextlib import nullcontext
|
||||
from typing import Any, Callable, ContextManager, Iterable
|
||||
|
||||
|
||||
def read_jsonl(path: str | None, on_error: Callable[[str], None] | None = None) -> list[dict[str, Any]]:
|
||||
if not path or not os.path.exists(path):
|
||||
return []
|
||||
rows: list[dict[str, Any]] = []
|
||||
try:
|
||||
with open(path, encoding="utf-8") as handle:
|
||||
for line in handle:
|
||||
try:
|
||||
value = json.loads(line)
|
||||
except Exception:
|
||||
continue
|
||||
if isinstance(value, dict):
|
||||
rows.append(value)
|
||||
except Exception as exc:
|
||||
if on_error:
|
||||
on_error(f"event 원장 읽기 실패({path}): {exc}")
|
||||
return rows
|
||||
|
||||
|
||||
def append_jsonl(
|
||||
path: str | None,
|
||||
event: dict[str, Any],
|
||||
*,
|
||||
file_lock: bool = False,
|
||||
on_error: Callable[[str], None] | None = None,
|
||||
) -> bool:
|
||||
if not path:
|
||||
return False
|
||||
try:
|
||||
with open(path, "a", encoding="utf-8") as handle:
|
||||
if file_lock:
|
||||
try:
|
||||
import fcntl
|
||||
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
|
||||
except Exception:
|
||||
pass
|
||||
handle.write(json.dumps(event, ensure_ascii=False) + "\n")
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
return True
|
||||
except Exception as exc:
|
||||
if on_error:
|
||||
on_error(f"event append 실패({path}): {exc}")
|
||||
return False
|
||||
|
||||
|
||||
def atomic_append(
|
||||
entries: Iterable[tuple[str | None, dict[str, Any]]],
|
||||
*,
|
||||
transaction_lock: ContextManager[Any] | None = None,
|
||||
) -> None:
|
||||
"""Append a group of events and truncate every participating tail on failure."""
|
||||
normalized = list(entries)
|
||||
handles: list[tuple[Any, int]] = []
|
||||
with (transaction_lock or nullcontext()):
|
||||
try:
|
||||
for path, _event in normalized:
|
||||
if not path:
|
||||
raise OSError("event path 해석 실패")
|
||||
handle = open(path, "a+", encoding="utf-8")
|
||||
try:
|
||||
import fcntl
|
||||
fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
|
||||
except Exception:
|
||||
pass
|
||||
handle.seek(0, os.SEEK_END)
|
||||
handles.append((handle, handle.tell()))
|
||||
try:
|
||||
for (handle, _offset), (_path, event) in zip(handles, normalized):
|
||||
handle.write(json.dumps(event, ensure_ascii=False) + "\n")
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
except Exception:
|
||||
for handle, offset in handles:
|
||||
handle.seek(offset)
|
||||
handle.truncate()
|
||||
handle.flush()
|
||||
raise
|
||||
finally:
|
||||
for handle, _offset in handles:
|
||||
try:
|
||||
handle.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user