Weave Code
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

Rowcast Profiler Laravel Package

ascetic-soft/rowcast-profiler

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require ascetic-soft/rowcast-profiler
    
  2. Wrap your Rowcast connection in a profiler (e.g., in a service provider or bootstrap file):

    use AsceticSoft\Rowcast\Connection;
    use AsceticSoft\RowcastProfiler\ConnectionProfiler;
    use AsceticSoft\RowcastProfiler\InMemoryQueryProfileStore;
    use AsceticSoft\RowcastProfiler\DefaultParameterSanitizer;
    use AsceticSoft\RowcastProfiler\RowcastProfiler;
    
    $innerConnection = Connection::create('sqlite::memory:');
    $store = new InMemoryQueryProfileStore();
    $sanitizer = new DefaultParameterSanitizer();
    $profiler = new RowcastProfiler($store, $sanitizer, slowQueryThresholdMs: 50.0);
    $profiledConnection = new ConnectionProfiler($innerConnection, $profiler);
    
  3. Use the profiled connection as a drop-in replacement for your existing Rowcast connection.


First Use Case: Debugging Slow Queries

  • Scenario: A dashboard query is taking >200ms, causing UI lag.
  • Steps:
    1. Replace the connection in your dashboard service with the profiled connection.
    2. Trigger the dashboard and check the profiler store:
      $profiles = $store->getProfiles();
      foreach ($profiles as $profile) {
          if ($profile->durationMs > 200) {
              echo "Slow query: " . $profile->sql . " (" . $profile->durationMs . "ms)\n";
              print_r($profile->parameters); // Sanitized params
          }
      }
      
    3. Optimize the slowest query (e.g., add indexes, rewrite joins).

Laravel-Specific Quick Start

  1. Register the profiler in a service provider:

    public function register()
    {
        $this->app->singleton(AsceticSoft\Rowcast\ConnectionInterface::class, function ($app) {
            $inner = $app->make(AsceticSoft\Rowcast\Connection::class);
            $profiler = new RowcastProfiler(
                new InMemoryQueryProfileStore(),
                new DefaultParameterSanitizer(),
                slowQueryThresholdMs: 100.0
            );
            return new ConnectionProfiler($inner, $profiler);
        });
    }
    
  2. Access profiles via a command:

    use Illuminate\Console\Command;
    use AsceticSoft\RowcastProfiler\InMemoryQueryProfileStore;
    
    class RowcastProfilerCommand extends Command
    {
        protected $signature = 'rowcast:profile';
        protected $description = 'List slow Rowcast queries';
    
        public function handle(InMemoryQueryProfileStore $store)
        {
            $profiles = $store->getProfiles();
            foreach ($profiles as $profile) {
                if ($profile->durationMs > 50) {
                    $this->line("<comment>Slow:</comment> {$profile->durationMs}ms - {$profile->sql}");
                }
            }
        }
    }
    

Implementation Patterns


Workflow: Profiling in Development

  1. Enable profiling in config/app.php or a service provider:

    $this->app->bind(
        AsceticSoft\Rowcast\ConnectionInterface::class,
        fn($app) => new ConnectionProfiler(
            $app->make(AsceticSoft\Rowcast\Connection::class),
            new RowcastProfiler(
                new InMemoryQueryProfileStore(),
                new DefaultParameterSanitizer(),
                slowQueryThresholdMs: 20.0 // Catch slow queries early
            )
        )
    );
    
  2. Run a command to inspect profiles:

    php artisan rowcast:profile
    
  3. Optimize based on output (e.g., add indexes, denormalize data).


Workflow: CI Integration

  1. Add a CI step to fail builds with slow queries:

    # .github/workflows/ci.yml
    - name: Check Rowcast query performance
      run: |
        php artisan rowcast:profile --fail-if-slow
    
  2. Create a custom command to enforce thresholds:

    // app/Console/Commands/CheckRowcastQueries.php
    public function handle()
    {
        $profiles = $store->getProfiles();
        $slowQueries = collect($profiles)->filter(fn($p) => $p->durationMs > 100);
    
        if ($slowQueries->count() > 0) {
            $this->error("Slow queries detected:");
            foreach ($slowQueries as $profile) {
                $this->line("<error>• {$profile->durationMs}ms: {$profile->sql}</error>");
            }
            exit(1);
        }
    }
    

Integration with Laravel Debugbar

  1. Extend Laravel Debugbar’s data collector:

    use AsceticSoft\RowcastProfiler\InMemoryQueryProfileStore;
    use LaravelDebugbar\ComponentCollector\Collector;
    
    class RowcastProfilerCollector extends Collector
    {
        public function __construct(InMemoryQueryProfileStore $store)
        {
            $this->store = $store;
        }
    
        public function getData()
        {
            return [
                'queries' => $this->store->getProfiles()->map(function ($profile) {
                    return [
                        'sql' => $profile->sql,
                        'duration' => $profile->durationMs,
                        'params' => $profile->parameters,
                    ];
                }),
            ];
        }
    }
    
  2. Register the collector in a service provider:

    Debugbar::collector(new RowcastProfilerCollector($store));
    

Pattern: Custom QueryProfileStore

  1. Implement a database-backed store:

    use AsceticSoft\RowcastProfiler\QueryProfileStore;
    use AsceticSoft\RowcastProfiler\QueryProfile;
    
    class DatabaseQueryProfileStore implements QueryProfileStore
    {
        public function addProfile(QueryProfile $profile)
        {
            DB::table('query_profiles')->insert([
                'sql' => $profile->sql,
                'duration_ms' => $profile->durationMs,
                'parameters' => json_encode($profile->parameters),
                'created_at' => now(),
            ]);
        }
    
        public function getProfiles(): array
        {
            return DB::table('query_profiles')
                ->orderBy('duration_ms', 'desc')
                ->get()
                ->map(function ($record) {
                    return new QueryProfile(
                        $record->sql,
                        $record->duration_ms,
                        json_decode($record->parameters, true),
                        $record->created_at
                    );
                })
                ->toArray();
        }
    }
    
  2. Use it in the profiler:

    $profiler = new RowcastProfiler(
        new DatabaseQueryProfileStore(),
        new DefaultParameterSanitizer(),
        slowQueryThresholdMs: 50.0
    );
    

Pattern: Conditional Profiling

  1. Enable profiling only in specific environments:

    $profiler = config('app.env') === 'local'
        ? new RowcastProfiler(
            new InMemoryQueryProfileStore(),
            new DefaultParameterSanitizer(),
            slowQueryThresholdMs: 50.0
        )
        : null;
    
    $connection = $profiler
        ? new ConnectionProfiler($inner, $profiler)
        : $inner;
    
  2. Toggle via config:

    // config/rowcast.php
    'profiler' => [
        'enabled' => env('ROWCAST_PROFILER_ENABLED', false),
        'slow_threshold_ms' => 50.0,
    ],
    

Gotchas and Tips


Pitfalls

  1. Parameter Sanitization Overhead

    • Issue: The DefaultParameterSanitizer may add noticeable overhead if parameters are complex (e.g., nested arrays, objects).
    • Fix: Use a lighter sanitizer or disable it in production:
      $profiler = new RowcastProfiler($store, null, slowQueryThresholdMs: 50.0);
      
  2. InMemoryQueryProfileStore Limitations

    • Issue: Profiles are lost on request restart. Not suitable for long-running processes or CLI commands.
    • Fix: Use a persistent store (e.g., database, Redis) even in development.
  3. Thread Safety

    • Issue: The default InMemoryQueryProfileStore is not thread-safe. Concurrent writes (e.g., in CLI + web) may corrupt data.
    • Fix: Use a synchronized store or disable profiling in CLI:
      if (app()->runningInConsole()) {
          $connection = $inner; // Skip profiling in CLI
      }
      
  4. Error Handling Gaps

    • Issue: Profiled errors are stored but not automatically logged or surfaced.
    • Fix: Extend the profiler to log errors:
      $profiler = new Row
      
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.
symfony/ai-symfony-mate-extension
aashan/pimcore-mcp-bundle
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin