NAME

App::karr::SyncGuard - Push guard with automatic retry on scope exit

VERSION

version 0.500

SYNOPSIS

my $guard = $self->sync_before;  # git pull + return guard
# ... command logic ...
$self->sync_after;               # explicit push
$guard->done;                    # mark guard as done (DESTROY no-ops)
undef $guard;

# If die/croak happens before sync_after:
# Guard DESTROY retries the push 3 times, then warns with a clear error

# Process teardown, from F<bin/karr>'s END block:
App::karr::SyncGuard->flush_armed;

DESCRIPTION

App::karr::SyncGuard is created by "sync_before" in App::karr::Role::SyncLifecycle. It acts as an insurance policy: if the command body dies or croaks before "sync_after" in App::karr::Role::SyncLifecycle is called explicitly, the guard's DESTROY runs sync_after with retry logic, ensuring refs are pushed even on failure.

That works whenever the guard is released while the interpreter is still whole -- a lexical guard going out of scope, an embedder dropping its command object. It does not work on the CLI, where bin/karr wraps the run in an eval and exits from the error handler: the command object stays reachable from the MooX::Cmd command chain, so the guard is first reaped in global destruction. App::karr::Git is not re-entrant in that phase -- pushing from there drove FFI::Platypus's type parser into unbounded recursion until the machine was out of memory -- so DESTROY refuses to push once ${^GLOBAL_PHASE} is DESTRUCT and only reports.

"flush_armed" is what makes the insurance real on that path. Every armed guard registers itself in a process-wide registry, and bin/karr flushes that registry from an END block: END is the last point before global destruction, and it also covers the exit calls inside command bodies. The DESTRUCT branch of DESTROY remains as the last resort for embedders that do not flush.

Both the DESTRUCT report and "flush_armed" gate on $App::karr::Git::WRITES, so a command that died before writing any ref neither pushes nor advises a sync. DESTROY reads that package scalar rather than the git attribute because blessed objects are destroyed in undefined order in this phase. Local refs are untouched either way, so karr sync always completes the push.

METHODS

done

$guard->done;

Marks the guard as spent: its DESTROY becomes a no-op and "flush_armed" skips it. "sync_after" in App::karr::Role::SyncLifecycle calls this both after a successful push and after one that failed all three attempts -- in the failure case the retries are already exhausted and the croak carries the guidance, so repeating them from the flush would only duplicate the noise.

flush_armed

my $count = App::karr::SyncGuard->flush_armed;

Runs the insurance push for every guard that is still armed, then empties the registry; returns the number of guards it pushed for. Called from bin/karr's END block, which is the last moment a push is safe.

Does nothing at all when $App::karr::Git::WRITES is zero: no ref was written in this process, so there is nothing to push.

Never dies. A push that fails warns, exactly as the DESTROY path does, and anything unexpected thrown by one guard is caught and warned so the remaining guards still get their turn. That matters because the only caller is an END block on an already-failing exit path: an exception there would abort perl's END queue and replace karr's documented exit code with perl's own. Each flushed guard is marked done, so DESTROY does not repeat the attempt afterwards.

A push the remote refused ref by ref is not retried, and the warning does not advise one: the far side already gave its answer, so it names what was refused instead of pointing at a karr sync that would be refused identically.

errs

my @errors = $guard->errs;

Returns the list of error messages from retry attempts.

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/karr/issues.

IRC

Join #langertha on irc.perl.org or message Getty directly.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <getty@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.