Production-Safe Scalable Periodic Data Ingest for Rails
Sign Up Free

Preview site — latest 100 public prompts. Sign up free on markmyprompt.com for the full library.

Get started →
R
Rajsekhar B
@rajsekharb4209
8/16/2026

Production-Safe Scalable Periodic Data Ingest for Rails

scalabilitydata-ingestionjob-scheduling
You are a senior backend architect. Design a production-safe way to run a periodic data-ingest job as the system scales. Do not give generic advice like “just use a lock.” Propose a concrete design, failure modes, and what must never happen.

## Context

Rails 6 / Ruby 3 app on **one VM** (4 CPUs, 16 GB RAM). Same host runs:

- Puma web (5 threads, 1 worker) behind nginx
- Puma API
- Sidekiq
- MySQL
- cron every **15 minutes** that boots a **full Rails process** and runs `data_fetch:fetch_and_process`

That rake task:

1. Reads a watermark table `last_end_times` (single row, `end_time` as a string).
2. If watermark is within the last **2 hours**, resume from it. **If it is older than 2 hours, it discards the gap and starts from ****`Time.now`****.** This is a known data-loss bug.
3. Fetches a **20-minute** window from an upstream HTTP API (AQ, life-safety, node state, motion).
4. `upsert_all` into MySQL (`readings` unique on `sensor_id, created_at, agg_type`).
5. **Only then** advances the watermark by +15 minutes.
6. **In the same process**, runs heavy post-processing (high-AQI rollups, LS analytics, notify). That post-work can last **hours**, long after the fetch finished.

There was **no overlap lock**. Cron kept starting a new Rails process every 15 minutes. We saw **50+ concurrent copies**, load \~35, MySQL with 60+ running queries, Puma threads blocked, nginx **504**. Oldest stuck process was \~15 days. The fetch itself was often already done; processes were stuck in post-processing.

We added a temporary `flock -n` in the cron wrapper so a second `data_fetch` exits immediately if one is running. That stops the pile-up. It does **not** fix catch-up or scale.

Puma also crash-looped (`EADDRINUSE` on :3000, 17k restarts) because an orphan held the port and `Restart=always` had no start limit. Every Rails boot also rewrote crontab via an initializer. Treat process supervision as in-scope only if it affects the ingest design.

## Scenario that must be handled

At 10:00 the job starts. Upstream returns a **very large** payload (example: **3 GB** / millions of rows). Saving to MySQL takes **longer than 15 minutes**. At 10:15 cron fires again. Job A is still writing.

We need a design that still works when:

- One window is huge (minutes to hours to persist)
- The host is busy and jobs run longer than the interval
- We scale to **more buildings, more sensors, more frequent pulls, or multiple app servers**
- The process is killed mid-`upsert` (partial batch)
- Upstream is slow or returns 500
- Watermark is hours or days behind
- Post-processing (analytics) is slow or broken
- Two app nodes both have cron/Sidekiq (split-brain)

## Constraints

- Must not lose or silently skip time windows (the 2-hour “jump to now” is unacceptable).
- Must not run two writers for the same job on one host or across hosts.
- Web/API must stay up; ingest must not be allowed to saturate MySQL/CPU the way it did.
- Upserts are idempotent; re-fetching a window is OK.
- A skipped cron tick is better than overlap.
- Catch-up must be **rate-limited** (one window at a time or a low-priority queue), not “replay 3 days in one process.”
- Prefer using existing Sidekiq if that is the right tool; say if cron should only enqueue.
- MySQL is the source of truth for the watermark unless you justify something better (Redis lock + DB watermark, etc.).

## What I want from you

1. **Target architecture** for ingest at scale (who schedules, who locks, who writes, who advances the watermark).
2. **Exact behavior** for the 3 GB / still-writing / next-tick case, step by step, including what the watermark does.
3. **How catch-up works** when we are 2 hours or 2 days behind — without jumping to now and without melting the box.
4. **How to split** “fetch+upsert” from “analytics/notify” so homework cannot block the next fetch forever.
5. **Locking**: local `flock` vs Redis/DB advisory lock vs Sidekiq unique jobs — what survives **multiple servers**.
6. **Timeouts**: what to kill vs what must be allowed to finish (do not kill a healthy large chunked write with a naive 12-minute timeout).
7. **Chunking** the 3 GB write (batch size, watermark granularity, crash recovery).
8. **Observability**: metrics/alerts that would page before 504s.
9. **Migration plan** from current rake+cron+flock with **low risk** to the live site and **no CPU increase** in steady state.
10. **What you would not do** (anti-patterns).

Call out remaining data-loss or dual-writer risks. Prefer a design that stays correct when we add a second worker host.
0 likes0 comments

Want to like, comment or save this prompt?

Sign up free to interact, create and organise your own AI prompts.

Get Started Free
Production-Safe Scalable Periodic Data Ingest for Rails | Mark My Prompt