DEV Community

Cover image for Efficient Reporting: Query Caching and Big Data Report Generation with Queues
Tahsin Abrar
Tahsin Abrar

Posted on

Efficient Reporting: Query Caching and Big Data Report Generation with Queues

Hey devs ๐Ÿ‘‹,

Today, letโ€™s discuss a real-world problem every backend developer faces at some point โ€“ optimizing heavy reports. We will explore:

โœ… How to cache report queries with fixed timelines
โœ… How to generate big data reports efficiently using job queues and handle downloads without overwhelming your client-side app.

Letโ€™s break it down step by step.


1. ๐Ÿ—“๏ธ Report Query Caching with Fixed Timelines

Imagine you have a dashboard where users frequently request a report of, say, the last 30 days sales data. Running the same heavy query every minute is inefficient.

๐Ÿ”‘ Solution: Cache the query result with a fixed timeline.

Why Fixed Timeline?

If you use indefinite cache expiry (like โ€œcache for 60 minutesโ€), the dataโ€™s freshness will differ based on the request time. A fixed timeline ensures:

  • All users see the same cached data within the period.
  • Cache expires at a known time (e.g., every day at 12:00 AM), keeping data consistency predictable.

How to Implement

Hereโ€™s a quick Laravel example using the cache facade:

use Illuminate\Support\Facades\Cache;

public function getReport()
{
    $cacheKey = 'sales_report_daily';
    $expiresAt = now()->endOfDay(); // Cache expires at 11:59 PM today

    return Cache::remember($cacheKey, $expiresAt, function () {
        // Your heavy DB query here
        return DB::table('sales')
                 ->whereDate('created_at', '>=', now()->subDays(30))
                 ->get();
    });
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ Explanation:

  • Cache::remember stores the result until endOfDay().
  • Next requests within the same day will fetch from cache, saving DB processing.
  • At midnight, the cache expires and the first request refreshes it.

๐Ÿ”ง Alternative use cases: weekly expiry (use endOfWeek()), monthly expiry, etc.


2. ๐Ÿ’พ Big Data Report Generation Using Job Queues

When dealing with big reports (e.g. millions of rows), running them in real-time and returning in the same request is impractical. It can crash your server or timeout your API gateway.

Best Practice:

โœ… Offload report generation to a queue job
โœ… Generate a downloadable file (CSV, Excel) in the background
โœ… Notify user when itโ€™s ready to download

Step-by-Step Implementation

Step 1: Trigger Report Job

Create an API endpoint or button action that dispatches a job to queue:

public function requestReport()
{
    $user = auth()->user();

    GenerateBigReport::dispatch($user->id);

    return response()->json(['message' => 'Report generation started. You will be notified once it is ready.']);
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Build The Job

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Storage;

class GenerateBigReport implements ShouldQueue
{
    use Queueable;

    public $userId;

    public function __construct($userId)
    {
        $this->userId = $userId;
    }

    public function handle()
    {
        $data = DB::table('transactions')->get(); // Example: big dataset

        $csv = fopen(storage_path("app/reports/report_{$this->userId}.csv"), 'w');

        // Add headers
        fputcsv($csv, ['id', 'amount', 'date']);

        foreach ($data as $row) {
            fputcsv($csv, [$row->id, $row->amount, $row->created_at]);
        }

        fclose($csv);

        // Optional: Notify user (email, notification)
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Show Download Button Only When File is Ready

Instead of loading the entire data on client side:

  1. Check if report exists:
   public function checkReport()
   {
       $path = "reports/report_" . auth()->id() . ".csv";
       if (Storage::exists($path)) {
           return response()->json([
               'ready' => true,
               'download_url' => Storage::url($path),
           ]);
       }

       return response()->json(['ready' => false]);
   }
Enter fullscreen mode Exit fullscreen mode
  1. In your frontend:
const checkReport = async () => {
    const res = await fetch('/api/check-report');
    const data = await res.json();
    if(data.ready){
        // show download button with data.download_url
    } else {
        // show "Processing..." or disabled button
    }
};
Enter fullscreen mode Exit fullscreen mode

โœ… Benefits:

  • Users do not wait for heavy API responses
  • Server resources are used optimally via queued background processing
  • Client side remains performant without loading all big data in-browser

โœจ Final Thoughts

Using fixed timeline caching and job queues for big data reports are powerful techniques to build scalable applications.

๐Ÿ”ง Quick recap:

  • Cache reports smartly with fixed expiry for consistent freshness.
  • Generate big data reports asynchronously with queues.
  • Serve the final file as a download โ€“ never load huge datasets directly on the client side.

Top comments (0)