Class: OllamaAgent::Runtime::AtomicMutator

Inherits:
Object
  • Object
show all
Defined in:
lib/ollama_agent/runtime/atomic_mutator.rb

Overview

POSIX-oriented atomic write (temp → fsync → rename → parent fsync).

Directory fd fsync after rename is required on Linux for crash-safe metadata durability. When File#fsync on a directory file descriptor is unsupported (Errno::EINVAL, ENOTSUP, or NotImplementedError), the step is skipped (see mutation_step payload dir_fsync_status). rubocop:disable Metrics/ClassLength – single orchestrator; helpers are private below

Instance Method Summary collapse

Constructor Details

#initialize(workspace_root:, ownership_index:, fencing_allocator:, wal:, blob_store: nil) ⇒ AtomicMutator

Returns a new instance of AtomicMutator.



22
23
24
25
26
27
28
# File 'lib/ollama_agent/runtime/atomic_mutator.rb', line 22

def initialize(workspace_root:, ownership_index:, fencing_allocator:, wal:, blob_store: nil)
  @workspace_root = File.expand_path(workspace_root)
  @ownership_index = ownership_index
  @fencing_allocator = fencing_allocator
  @wal = wal
  @blob_store = blob_store
end

Instance Method Details

#delete_file(path:, mode:, fencing_token:, expected_pre_hash:, intent_hash:, manifest_id:, logical_stamp:, owner_required: nil, supervisor_lease: false) ⇒ :deleted, ...

rubocop:disable Metrics/ParameterLists

Returns:

  • (:deleted, :duplicate, :forbidden, :stale_token, :precondition_failed, :inode_swapped)


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/ollama_agent/runtime/atomic_mutator.rb', line 56

def delete_file(path:, mode:, fencing_token:, expected_pre_hash:, intent_hash:, manifest_id:,
                logical_stamp:, owner_required: nil, supervisor_lease: false)
  outcome = guard_and_ownership(path, mode, manifest_id, logical_stamp, owner_required,
                                supervisor_lease)
  return outcome if outcome

  absolute = expand_workspace_path(path.to_s)
  outcome = cas_delete_and_wal(absolute, fencing_token, expected_pre_hash, intent_hash, manifest_id,
                               logical_stamp)
  return outcome if outcome != :continue

  two_step_unlink!(absolute, manifest_id, logical_stamp)
  track_step(manifest_id, logical_stamp, "complete")
  :deleted
end

#rename_file(from_path:, to_path:, mode:, fencing_token_from:, fencing_token_to:, expected_pre_hash_from:, intent_hash:, manifest_id:, logical_stamp:, owner_required: nil, supervisor_lease: false) ⇒ :renamed, ...

Returns:

  • (:renamed, :duplicate, :forbidden, :stale_token, :precondition_failed)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ollama_agent/runtime/atomic_mutator.rb', line 73

def rename_file(from_path:, to_path:, mode:, fencing_token_from:, fencing_token_to:,
                expected_pre_hash_from:, intent_hash:, manifest_id:, logical_stamp:,
                owner_required: nil, supervisor_lease: false)
  o1 = guard_and_ownership(from_path, mode, manifest_id, logical_stamp, owner_required,
                           supervisor_lease)
  return o1 if o1

  o2 = guard_and_ownership(to_path, mode, manifest_id, logical_stamp, owner_required,
                           supervisor_lease)
  return o2 if o2

  from_abs = expand_workspace_path(from_path.to_s)
  to_abs = expand_workspace_path(to_path.to_s)
  outcome = cas_rename_and_wal(from_abs, to_abs, fencing_token_from, fencing_token_to,
                               expected_pre_hash_from, intent_hash, manifest_id, logical_stamp)
  return outcome if outcome != :continue

  persist_rename(from_abs, to_abs, manifest_id, logical_stamp)
  :renamed
end

#write(path:, content:, mode:, fencing_token:, expected_pre_hash:, intent_hash:, manifest_id:, logical_stamp:, owner_required: nil, supervisor_lease: false) ⇒ :written, ...

Atomically replace path under the configured workspace root.

rubocop:disable Metrics/ParameterLists – public mutation envelope matches kernel contract

Parameters:

  • fencing_token (Integer)

    last value returned by FencingAllocator#allocate for this path scope (absolute path string). The mutator calls FencingAllocator#allocate again internally to advance the fence; fencing_token must equal current_allocated - 1 where current_allocated is the value just produced by that internal allocate (i.e. the caller-held token is always exactly one behind the mutator’s post-check token).

Returns:

  • (:written, :duplicate, :forbidden, :stale_token, :precondition_failed, :inode_swapped)


39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/ollama_agent/runtime/atomic_mutator.rb', line 39

def write(path:, content:, mode:, fencing_token:, expected_pre_hash:, intent_hash:, manifest_id:,
          logical_stamp:, owner_required: nil, supervisor_lease: false)
  outcome = guard_and_ownership(path, mode, manifest_id, logical_stamp, owner_required,
                                supervisor_lease)
  return outcome if outcome

  absolute = expand_workspace_path(path.to_s)
  outcome = cas_and_wal(absolute, content, fencing_token, expected_pre_hash, intent_hash,
                        manifest_id, logical_stamp)
  return outcome if outcome != :continue

  persist_atomic_swap(absolute, content, manifest_id, logical_stamp)
end