Skip to content

Better rename during rotations - #9

Merged
martinhanzik merged 6 commits into
mainfrom
feat/rotation-rename
Jun 4, 2026
Merged

Better rename during rotations#9
martinhanzik merged 6 commits into
mainfrom
feat/rotation-rename

Conversation

@martinhanzik

Copy link
Copy Markdown
Contributor

No description provided.

Martin Hanzík and others added 4 commits June 3, 2026 21:30
…rename

The agent is now the sole authority on file identity and rotation. The server
no longer decides identity from content; it only appends to an agent-named
`target` and renames it when told to.

- hello: write to `<dir>/<target>`; report {length, prefix_sha1} and never
  rotate. Absent `target` falls back to the legacy rotate-on-mismatch path so a
  v2 server can be deployed ahead of the agent.
- data: keep the explicit offset check (now a clean ProtocolError); lazily
  create a new target on the first append.
- rename: idempotent in-band control frame within the connection dir; the open
  fd survives the rename. fsync the segment file and the parent directory on
  rename so completed segments and their final names are crash-durable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…an path

Replace the per-inode follow_file with a per-path coordinator that owns all
segment connections and is the sole authority on rotation. Each segment binds to
an explicit `target`: in steady state one live segment streams the current inode
into `foo.log`; across a rotation a closing segment drains the old inode into
`foo.log.<iso_dt>` while a new live segment streams the new inode into `foo.log`.
The one ordering constraint -- seal before opening the new live segment -- is a
single awaited rename ack in the coordinator, so the two connections never share
a name and the server has nothing to arbitrate.

- client: explicit `target` in hello; `send_rename` control frame.
- marker_watcher: parrot lh-logrotate's <iso_dt> (.lh-logrotate-waiting) and
  <sha13> (.lh-logrotate-{compressed,uploaded}, uploaded as fallback).
- coordinator: seal -> drain-to-marker -> finalize for lh-logrotate files; an
  orphan path (self-generated <iso_dt> + `.orphan`, EOF-stable completion) for
  markerless rotations; no-write-before-seal recovery for a stale remote target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rewrite the rotation e2e test to drive the real lh-logrotate marker flow
end-to-end (waiting -> sealed -> compressed -> finalized) against the v2 server,
asserting the live mirror name equals the rotated name. Document the v2 protocol
(explicit target, prefix_sha1 reporting, idempotent rename, rotation naming) and
record the design in PLAN_v2.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… target

The server no longer has a pre-target fallback. An explicit, validated
`target` is now required in the handshake; a missing or invalid target is
rejected. With it, the content-based rotate-on-prefix-mismatch path
(`serve_legacy`) is gone -- the agent is the sole authority on file identity
and rotation. `serve_v2` is renamed to `serve_client` accordingly.

Lazy target creation now validates the first `data` frame's metadata and
offset strictly (integer offset 0, rejecting bool) before opening the file,
so a malformed first frame never leaves a stray empty file behind.

Also drop the PLAN_v2.md design doc and de-version the surviving "v2"
references now that there is a single protocol (the `logline-agent-v1` wire
token is unrelated and unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread agent/logline_agent/configuration.py Outdated
# orphan (markerless). lh-logrotate writes the marker as it rotates, so
# the real wait is usually ~0; this is headroom for scan jitter. It also
# bounds how long the new live segment is delayed, so keep it small.
self.seal_marker_grace = 10

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

U takovychto konfiguracnich hodnot by bylo fajn pridat suffix typu _seconds

Martin Hanzík and others added 2 commits June 4, 2026 15:21
The agent now sends the source `directory` and the destination leaf name
`target` as separate header fields instead of a single `path`. The server
is the sole authority on neither: it maps `directory` to the mangled
<dest>/<hostname>/<directory> and writes the agent-chosen `target` as the
leaf. The basename of `path` was only ever computed and then discarded --
the destination filename is `target`, which differs from the source
basename for sealed/finalized segments.

Server: build_destination_path() becomes build_destination_dir(), mapping
the mangled directory under <dest>/<hostname>/ and leaving the leaf to the
caller's `target`. All path-traversal hardening is preserved; the final
resolved-inside-base check now guards the host-only case too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the agent Configuration attributes to carry their units: durations
get a `_seconds` suffix and byte counts a `_bytes` suffix (for example
tail_read_interval -> tail_read_interval_seconds and prefix_length ->
prefix_length_bytes). No behavioural change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@martinhanzik
martinhanzik merged commit 24414cd into main Jun 4, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants