Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sic Bundle Laravel Package

gepur-it/sic-bundle

gepur-it/sic-bundle is a PHP bundle for integrating SIC functionality into Symfony applications. It packages configuration and service wiring so projects can add SIC-related features with minimal setup and keep integration code organized.

View on GitHub
Deep Wiki
Context7

Getting Started

gepur-it/sic-bundle ("Single Instance Console Command Bundle") is a Symfony bundle — not a Laravel package. It exists to stop a console command from running twice at the same time (the classic "cron fires every minute, the import takes three" problem). Before you install anything, decide whether you actually need it: Laravel's scheduler already ships withoutOverlapping() on the Schedule object, which solves 90% of the use case for free.

If you still want it (e.g., you're running a Symfony-console-based command inside a Laravel app via a bridge, or you want to see the pattern):

  1. composer require gepur-it/sic-bundle — check the constraint graph first. The new v6.0.0 release bumps composer constraints to PHP 8 / Symfony 5. That makes it installable on Laravel 8.x (which uses Symfony 5 components) and on PHP 8.0–8.1. It is not compatible with Laravel 9+ out of the box, because Laravel 9/10 use Symfony 6.x and Laravel 11 uses Symfony 7.x — so on those versions you'll still need --ignore-platform-reqs or a replace/fork. The previous release was April 2021, so treat v6.0.0 as a long-overdue version-bump rather than a sign of active maintenance.
  2. Symfony path: register the bundle in config/bundles.php and read the DependencyInjection/Configuration.php to see the lock options.
  3. Laravel path: there is no bundle registration. Treat it as a reference implementation — read the console event listener (typically a ConsoleEvents::COMMAND subscriber that acquires a lock named after the command) and port the ~30–60 lines into your own Artisan::starting() hook or command base class.
  4. First use case: a cron-driven Artisan command (php artisan reports:rebuild) that must self-skip when the previous invocation is still running. The behaviour you want is: second invocation logs "already running", exits 0, and never touches the data.

Implementation Patterns

The core pattern the bundle encodes is a per-command lock acquired before execute() and released after. Two variants exist in the wild:

  • Flock/semaphore — lock is tied to the file handle or OS semaphore, so it is released by the kernel even on kill -9. Safest.
  • PID file — the process writes its PID; a second process reads it and decides. Fragile (stale PIDs after crashes, PID reuse).

Check which one this bundle uses before trusting it in production.

Symfony usage (if you're in a Symfony 5 / Laravel 8 app):

// config/bundles.php
GepurIt\SicBundle\SicBundle::class => ['all' => true],

Then any command registered in the container is wrapped by the subscriber. Configure per-command lock names/TTLs in config/packages/sic.yaml if the bundle's Configuration exposes them.

Laravel-native equivalents (preferred):

// 1. Scheduler — the default answer
$schedule->command('reports:rebuild')->everyMinute()->withoutOverlapping(10);

// 2. Manual lock inside the command — when it's also invoked by hand or by a job
public function handle(): int
{
    $lock = Cache::lock('cmd:reports:rebuild', 600);

    if (! $lock->get()) {
        $this->warn('Already running, skipping.');
        return self::SUCCESS; // exit 0 so cron/monitoring stays quiet
    }

    try {
        $this->doWork();
    } finally {
        $lock->release();
    }

    return self::SUCCESS;
}

Porting the bundle's listener to Laravel — if you want the "every command is single-instance" behaviour without touching each command:

// AppServiceProvider::boot()
Artisan::starting(function ($artisan) {
    // resolve command name from argv, acquire Cache::lock, abort if held
});

Or decorate: extract an abstract SingleInstanceCommand base class and extend it.

Integration tips:

  • Return exit code 0 (not 1) for the "skipped, already running" case, otherwise cron mailers and Sentry will scream every minute.
  • Log at info/debug for skips, error only for real failures.
  • Use a shared lock store (Redis, database) if you run more than one replica/pod. flock on a local path is per-container and gives you zero protection in Kubernetes.
  • For long tasks, make the lock TTL longer than the worst-case runtime, or use a heartbeat that refreshes it.

Gotchas and Tips

  • Stale project, one late release. The 2021 → v6.0.0 jump is a composer-constraint bump (php 8, symfony 5), not a feature or bugfix release. One star, zero dependents, and a multi-year gap between versions means: assume no active maintainer. Vendor it, fork it, or (better) copy the pattern — it's small enough that owning the code is cheaper than owning the dependency.
  • Framework mismatch. It's a Symfony bundle; a Laravel app cannot composer require it and get autoconfigured behaviour (no bundle registration in Laravel). Most of its value here is as a reading exercise. The new Symfony 5 constraint lands it squarely in Laravel 8.x territory — modern Laravel (9/10/11) pulls Symfony 6/7, so composer will still refuse the install without --ignore-platform-reqs.
  • withoutOverlapping() uses the cache lock, so it silently degrades to "no protection" if CACHE_DRIVER=array or your cache store doesn't support atomic locks. Verify your store (redis, database, memcached, dynamodb — not file on NFS, not array).
  • Stale locks. flock clears on process death; PID files and plain cache locks do not. If your jobs can be OOM-killed, prefer flock/semaphore or set a TTL and accept a window of double-execution.
  • Lock key collisions. Namespacing the lock by command name alone is not enough if you run the same command with different arguments (e.g., import:feed --tenant=5). Include the discriminating arguments in the lock key, or you'll silently serialise unrelated workloads.
  • Exit codes are the contract. Cron and systemd decide what to do based on exit status. Return 0 for "skipped", a distinct non-zero code for "failed", and document it.
  • Debugging. Run with -vvv and check the log line where the listener decides to abort; inspect the lock store directly (redis-cli keys '*cmd:*') or the lock file directory to see who holds it.
  • Extension points worth knowing (if you fork):
    • ConsoleEvents::COMMAND listener priority — you must run before the command's execute(), and other listeners may already have side effects.
    • Custom lock store — swap FlockStore for RedisStore (via symfony/lock, the maintained component) to get cross-host safety.
    • The bundle will not help with SIGTERM handling or graceful shutdown; pair it with a signal handler if the task must finish cleanly.
  • Maintained alternatives: symfony/lock for the primitive, Laravel's Cache::lock() + withoutOverlapping() for the framework-native path, or a simple flock() wrapper — all three are better-supported than this bundle.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib