<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>programming</title>
    <link>https://timeslip2.tistory.com/</link>
    <description>programming</description>
    <language>ko</language>
    <pubDate>Tue, 30 Jun 2026 13:45:24 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>programming-for-us</managingEditor>
    <item>
      <title>6 CI/CD Pipelines for Ruby AI Apps</title>
      <link>https://timeslip2.tistory.com/entry/6-CICD-Pipelines-for-Ruby-AI-Apps</link>
      <description>&lt;div&gt;
&lt;h2 id=&quot;multi-stage-builds-with-gpu-enabled-runners&quot; data-ke-size=&quot;size26&quot;&gt;Multi-stage builds with GPU-enabled runners&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-stage builds with GPU-enabled runners let Ruby AI apps compile native gems, package PyTorch/TensorRT dependencies, and slim the final image while retaining CUDA/cuDNN layers only where needed. Use a builder stage to compile Ruby extensions and a runtime stage based on CUDA images; on CI, attach GPU-enabled runners to run GPU unit tests and inference smoke tests before promotion. On GitHub Actions, GitLab, or Jenkins, multi-stage builds with GPU-enabled runners cut image size and validate kernels reproducibly for Ruby AI apps that serve models on GPUs.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.runpod.io/articles/guides/integrating-runpod-with-ci-cd-pipelines&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;runpod&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cache wheels and gem bundles in the builder stage; multi-stage builds with GPU-enabled runners should mount a deterministic toolchain to keep Ruby AI apps reproducible across nodes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.cscs.ch/services/cicd/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;cscs&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Prefer container-native testing where GPU-enabled runners execute integration specs and micro-benchmarks for Ruby AI apps before merging.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://about.gitlab.com/blog/empowering-modelops-and-hpc-workloads-with-gpu-enabled-runners/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;about.gitlab&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;model-artifact-versioning-and-canary-inference&quot; data-ke-size=&quot;size26&quot;&gt;Model artifact versioning and canary inference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model artifact versioning and canary inference are the guardrails that keep Ruby AI apps safe in production. Use a model registry or DVC/MLflow to track SHA-tagged artifacts, metadata, and evaluation metrics; canary inference routes a small slice of traffic to a new artifact while dashboards compare latency and accuracy. With feature flags or service-mesh routing, model artifact versioning and canary inference allow rapid rollback if Ruby AI apps regress under real traffic.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.qwak.com/post/ci-cd-pipelines-for-machine-learning&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;qwak&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Store model cards, metrics, and lineage with the artifact so model artifact versioning and canary inference remain auditable and reversible for Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.clarifai.com/blog/mlops-best-practices&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;clarifai&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Automate promotion gates where canary inference must meet SLOs before Ruby AI apps adopt the new version cluster-wide.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.qwak.com/post/ci-cd-pipelines-for-machine-learning&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;qwak&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;dataset-diffing-and-reproducible-training&quot; data-ke-size=&quot;size26&quot;&gt;Dataset diffing and reproducible training&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dataset diffing and reproducible training prevent &amp;ldquo;works on my GPU&amp;rdquo; failures by tying code, data, and hyperparameters into a single lineage. DVC and Git LFS track dataset snapshots while CI jobs run hash checks and dataset diffing to explain metric shifts; training pipelines pin seeds, Docker images, and drivers for reproducible training. When Ruby AI apps depend on embeddings or classifiers, reproducible training guarantees that predictions are traceable back to a dataset version and code commit.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://circleci.com/blog/automated-version-control-for-llms-using-dvc-and-ci-cd/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;circleci&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Promote only when dataset diffing shows expected drift and reproducible training reproduces baselines within tolerance for Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://labelyourdata.com/articles/machine-learning/data-versioning&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;labelyourdata&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Archive training config, environment manifests, and artifacts so reproducible training doubles as a compliance pack for regulated Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://labelyourdata.com/articles/machine-learning/data-versioning&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;labelyourdata&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;bluegreen-deploys-with-traffic-shifting&quot; data-ke-size=&quot;size26&quot;&gt;Blue/green deploys with traffic shifting&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blue/green deploys with traffic shifting let Ruby AI apps switch between old and new inference services instantly. Deploy a green environment with the new image and model artifact, then shift 10% to 100% of traffic via the gateway or service mesh; blue/green deploys with traffic shifting provide instant rollback if anomalies appear. Pair this with synthetic probes and shadow traffic so Ruby AI apps validate memory, GPU utilization, and p95 latency before a full cutover.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://devtron.ai/blog/blue-green-deployment-in-kubernetes/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;devtron&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use weighted routes and progressive steps; blue/green deploys with traffic shifting minimize downtime and reduce blast radius for Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://talent500.com/blog/blue-green-deployments-kubernetes-istio/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;talent500&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Keep environments symmetrical and codified; blue/green deploys with traffic shifting are only safe when infra parity is verifiable for Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://devtron.ai/blog/blue-green-deployment-in-kubernetes/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;devtron&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;governance-approvals-traceability-and-rollback&quot; data-ke-size=&quot;size26&quot;&gt;Governance: approvals, traceability, and rollback&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Governance&amp;mdash;approvals, traceability, and rollback&amp;mdash;makes CI/CD for Ruby AI apps enterprise-ready. Define change-control steps so a model update requires peer review, a risk sign-off, and a documented rollback plan; traceability ties model decisions to datasets, features, and artifact versions. With approvals, traceability, and rollback embedded in pipelines, Ruby AI apps meet audit expectations without slowing iteration.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://consensuslabs.ch/blog/mlops-regulated-industries-audit-ready-pipelines&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;consensuslabs&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Capture approver identity, rationale, and time windows; governance with approvals, traceability, and rollback should emit artifacts for every release of Ruby AI apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.altrum.ai/blog/ai-lifecycle-governance-a-comprehensive-guide-for-enterprise-executives-f8xur&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;altrum&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Monitor drift and fairness metrics; governance with approvals, traceability, and rollback must halt promotion when Ruby AI apps violate thresholds.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://consensuslabs.ch/blog/mlops-regulated-industries-audit-ready-pipelines&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;consensuslabs&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;end-to-end-pipeline-example&quot; data-ke-size=&quot;size26&quot;&gt;End-to-end pipeline example&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;An end-to-end CI/CD flow for Ruby AI apps starts with dataset diffing and reproducible training on GPU-enabled runners, persists artifacts in a registry, and gates merges with canary inference. Multi-stage builds with GPU-enabled runners package the service and model together, then blue/green deploys with traffic shifting promote safely under observability. Throughout, governance with approvals, traceability, and rollback ensures Ruby AI apps can be audited and reverted quickly when behavior drifts.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://semaphore.io/blog/continuous-blue-green-deployments-with-kubernetes&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;semaphore&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Treat pipelines as code; encode model artifact versioning and canary inference checks directly in CI so Ruby AI apps advance only when SLOs hold.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://circleci.com/blog/automated-version-control-for-llms-using-dvc-and-ci-cd/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;circleci&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Prefer hosted GPU backends or on-demand fleets; multi-stage builds with GPU-enabled runners combined with automated provisioning keeps Ruby AI apps cost-effective at scale.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.runpod.io/articles/guides/integrating-runpod-with-ci-cd-pipelines&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;runpod&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.runpod.io/articles/guides/integrating-runpod-with-ci-cd-pipelines&quot;&gt;https://www.runpod.io/articles/guides/integrating-runpod-with-ci-cd-pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cscs.ch/services/cicd/&quot;&gt;https://docs.cscs.ch/services/cicd/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://about.gitlab.com/blog/empowering-modelops-and-hpc-workloads-with-gpu-enabled-runners/&quot;&gt;https://about.gitlab.com/blog/empowering-modelops-and-hpc-workloads-with-gpu-enabled-runners/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.qwak.com/post/ci-cd-pipelines-for-machine-learning&quot;&gt;https://www.qwak.com/post/ci-cd-pipelines-for-machine-learning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.clarifai.com/blog/mlops-best-practices&quot;&gt;https://www.clarifai.com/blog/mlops-best-practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://circleci.com/blog/automated-version-control-for-llms-using-dvc-and-ci-cd/&quot;&gt;https://circleci.com/blog/automated-version-control-for-llms-using-dvc-and-ci-cd/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://labelyourdata.com/articles/machine-learning/data-versioning&quot;&gt;https://labelyourdata.com/articles/machine-learning/data-versioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dvc.org/doc/use-cases/ci-cd-for-machine-learning&quot;&gt;https://dvc.org/doc/use-cases/ci-cd-for-machine-learning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devtron.ai/blog/blue-green-deployment-in-kubernetes/&quot;&gt;https://devtron.ai/blog/blue-green-deployment-in-kubernetes/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://talent500.com/blog/blue-green-deployments-kubernetes-istio/&quot;&gt;https://talent500.com/blog/blue-green-deployments-kubernetes-istio/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://semaphore.io/blog/continuous-blue-green-deployments-with-kubernetes&quot;&gt;https://semaphore.io/blog/continuous-blue-green-deployments-with-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://consensuslabs.ch/blog/mlops-regulated-industries-audit-ready-pipelines&quot;&gt;https://consensuslabs.ch/blog/mlops-regulated-industries-audit-ready-pipelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.altrum.ai/blog/ai-lifecycle-governance-a-comprehensive-guide-for-enterprise-executives-f8xur&quot;&gt;https://www.altrum.ai/blog/ai-lifecycle-governance-a-comprehensive-guide-for-enterprise-executives-f8xur&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://celestialsys.com/blogs/the-intersection-of-ai-governance-and-mlops/&quot;&gt;https://celestialsys.com/blogs/the-intersection-of-ai-governance-and-mlops/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/77155680/gitlab-shared-runner-docker-does-not-support-multi-stage-build&quot;&gt;https://stackoverflow.com/questions/77155680/gitlab-shared-runner-docker-does-not-support-multi-stage-build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab-docs.infograb.net/ee/ci/pipelines/&quot;&gt;https://gitlab-docs.infograb.net/ee/ci/pipelines/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://galileo.ai/blog/building-first-mlops-pipeline-practical-roadmap&quot;&gt;https://galileo.ai/blog/building-first-mlops-pipeline-practical-roadmap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://roboticape.com/2024/03/28/a-working-ci-cd-workflow-with-github-actions-for-ai-applications/&quot;&gt;https://roboticape.com/2024/03/28/a-working-ci-cd-workflow-with-github-actions-for-ai-applications/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/blog/building-real-world-ai-faster-a-practical-guide-to-hiring-and-working-with-pytorch-developers/&quot;&gt;https://ruby-doc.org/blog/building-real-world-ai-faster-a-practical-guide-to-hiring-and-working-with-pytorch-developers/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cloudzero.com/blog/cicd-tools/&quot;&gt;https://www.cloudzero.com/blog/cicd-tools/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/41</guid>
      <comments>https://timeslip2.tistory.com/entry/6-CICD-Pipelines-for-Ruby-AI-Apps#entry41comment</comments>
      <pubDate>Sat, 22 Nov 2025 21:58:22 +0900</pubDate>
    </item>
    <item>
      <title>Rails 3.4 Job Systems: 8 Patterns for Background Work</title>
      <link>https://timeslip2.tistory.com/entry/Rails-34-Job-Systems-8-Patterns-for-Background-Work</link>
      <description>&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rails 3.4 job systems must balance throughput, correctness, and operability by choosing between Sidekiq, Resque, and Que, enforcing idempotency keys and retry backoff strategies, orchestrating distributed cron with leader election, handling out-of-order completion and result aggregation, and building observability with job latency and failure heatmaps. These patterns keep background work predictable under bursty traffic while aligning with modern Rails 3.4 and Active Job capabilities.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Choosing between Sidekiq, Resque, and Que&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Choosing between Sidekiq, Resque, and Que depends on concurrency model, dependencies, and operational constraints. Choosing between Sidekiq, Resque, and Que often favors Sidekiq for high-throughput multithreaded processing over Redis with strong tooling and dashboards, while Resque uses forked processes for isolation at the cost of higher overhead and Que relies on PostgreSQL for queueing to avoid Redis entirely. Choosing between Sidekiq, Resque, and Que should also consider emerging Rails-native options like Solid Queue when avoiding external services, though Sidekiq remains the default for many production apps due to mature retries and middleware.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.scoutapm.com/blog/resque-v-sidekiq-for-ruby-background-jobs-processing&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;scoutapm&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Choosing between Sidekiq, Resque, and Que also means aligning job definitions with Active Job adapters, ensuring that queue names, priorities, and retry behavior are mapped consistently in configuration. Choosing between Sidekiq, Resque, and Que is ultimately about workload fit: CPU-bound tasks may benefit from process isolation while I/O-bound tasks shine with threaded workers and larger concurrency. Choosing between Sidekiq, Resque, and Que should be validated with canary environments and representative traffic.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://skilldlabs.com/background-jobs-in-rails-explore-efficiency-sidekiq-and-resque/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;skilldlabs&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Idempotency keys and retry backoff strategies&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Idempotency keys and retry backoff strategies prevent duplication and collapse transient failure spikes into controlled retries. Idempotency keys and retry backoff strategies in Sidekiq can leverage job arguments as natural keys or store explicit keys in Redis/DB to deduplicate enqueues and executions. Idempotency keys and retry backoff strategies should tune retry counts and classify errors, never retrying programmer errors while allowing exponential backoff for network or rate-limit failures.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/sidekiq/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Idempotency keys and retry backoff strategies benefit from Sidekiq&amp;rsquo;s built-in exponential schedule&amp;mdash;up to 25 retries across ~21 days by default&amp;mdash;while queue-specific policies can shorten or lengthen windows based on SLA. Idempotency keys and retry backoff strategies must account for ordering: a later job can succeed before an earlier retry, so jobs must be designed to tolerate out-of-order application. Idempotency keys and retry backoff strategies should log dedupe hits and last-attempt outcomes to support audits.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/alex_aslam/the-art-of-the-resilient-worker-a-sidekiq-masters-guide-to-idempotency-retries-and-the-1jim&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Distributed cron with leader election&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Distributed cron with leader election replaces single-host crontabs with queue-native schedules that continue working through failovers. Distributed cron with leader election can use Sidekiq Scheduler or CRON expressions in the job system, guarded by a leader-elected process so only one instance enqueues at a time. Distributed cron with leader election in Kubernetes can piggyback on native leader election primitives or external locks for fencing to prevent split-brain scheduling.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Distributed cron with leader election should emit metrics for on-time, delayed, and skipped schedules, and record the elected leader identity for traceability. Distributed cron with leader election must handle clock skew and process pauses; using lease-based locks with expirations and jitter helps avoid duplicate enqueues. Distributed cron with leader election is a reliability upgrade over host-level cron because it aligns scheduling with application rollouts and autoscaling.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/16055973/distributed-system-leader-election&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Out-of-order completion and result aggregation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Out-of-order completion and result aggregation are the norm in parallel job systems and must be planned into workflows. Out-of-order completion and result aggregation require idempotent reducers that accept partial results in any sequence, storing progress markers and combining results with commutative operations to avoid double counting. Out-of-order completion and result aggregation can be modeled as map-reduce steps: mappers emit keyed outputs, reducers perform associative merges, and a final compactor materializes the result.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/sidekiq/sidekiq/wiki/Best-Practices&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Out-of-order completion and result aggregation should use per-unit fencing tokens or version checks so late-arriving updates with stale versions are rejected. Out-of-order completion and result aggregation must document invariants&amp;mdash;what happens on partial failure, and how finalization is retried&amp;mdash;so operators can reason about correctness during incidents. Out-of-order completion and result aggregation pair naturally with job saga patterns to ensure eventual consistency with explicit compensation steps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/sidekiq/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Observability: job latency and failure heatmaps&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observability: job latency and failure heatmaps provide the feedback loop required to keep SLAs in shape and detect regressions. Observability: job latency and failure heatmaps should capture end-to-end latency (enqueue to success), execution time, queue wait, retries, and dead-letter counts across queues and worker types. Observability: job latency and failure heatmaps become actionable with dashboards that show P50/P95/P99 per queue and with alerting when retries spike or when execution skews by shard or tenant.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://railsdrop.com/2025/06/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;railsdrop&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observability: job latency and failure heatmaps for Sidekiq can leverage the Web UI and custom middleware logging to tag jobs with request IDs and tenants, while for Resque/Que similar metrics can be emitted via wrappers. Observability: job latency and failure heatmaps should also track saturation signals&amp;mdash;Redis or DB connection pools, thread counts, and memory&amp;mdash;to correlate infrastructure pressure with job slowdowns. Observability: job latency and failure heatmaps are critical for detecting stuck queues or poison messages quickly.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concurrency limits and backpressure&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrency limits and backpressure keep systems stable when downstream dependencies degrade. Concurrency limits and backpressure can use separate queues per dependency with max concurrency caps, ensuring one flaky API does not starve all workers. Concurrency limits and backpressure apply circuit breakers and rate limits at the worker boundary to preempt cascading failures during incidents. Concurrency limits and backpressure must also respect database pool sizes so job threads do not exhaust connections needed by web traffic.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.scoutapm.com/blog/resque-v-sidekiq-for-ruby-background-jobs-processing&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;scoutapm&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrency limits and backpressure can dynamically reduce concurrency based on error rates or latency, shedding load gracefully to maintain partial service. Concurrency limits and backpressure should expose current concurrency and queue depths, providing SREs with levers to pause, drain, or reroute work during maintenance windows. Concurrency limits and backpressure align job throughput with real capacity as environments autoscale.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/sidekiq/sidekiq/wiki/Best-Practices&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Exactly-once illusions and effective-once delivery&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exactly-once illusions and effective-once delivery recognize that true exactly-once is infeasible; the goal is idempotent processing that yields the same final state even with duplicates. Exactly-once illusions and effective-once delivery rely on idempotency keys, upserts, and dedupe tables so retries and redeliveries do not create side effects. Exactly-once illusions and effective-once delivery means each job should be safe to run multiple times and safe to time out midway, resuming to the same end state.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/alex_aslam/the-art-of-the-resilient-worker-a-sidekiq-masters-guide-to-idempotency-retries-and-the-1jim&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exactly-once illusions and effective-once delivery should include outbox patterns for cross-service messaging, ensuring messages are persisted atomically with state changes to avoid lost updates. Exactly-once illusions and effective-once delivery pairs with result aggregation to reconcile late or duplicate events deterministically. Exactly-once illusions and effective-once delivery are cornerstone disciplines for financial or quota-sensitive operations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/sidekiq/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Safe shutdown, draining, and disaster drills&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Safe shutdown, draining, and disaster drills ensure reliability during deploys and outages. Safe shutdown, draining, and disaster drills configure graceful stop timeouts so workers finish in-flight jobs or requeue safely, with SIGTERM handlers that stop fetching new work while letting current jobs complete. Safe shutdown, draining, and disaster drills include runbooks for pausing queues, rebalancing shards, and promoting leaders for distributed cron when nodes roll.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dalibornasevic.com/posts/83-distributed-cron-for-rails-apps-with-sidekiq-scheduler&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dalibornasevic&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Safe shutdown, draining, and disaster drills should be practiced: simulate Redis outages or DB failovers and confirm that retry backoff prevents stampedes while dashboards expose failures clearly. Safe shutdown, draining, and disaster drills close the loop on every other pattern&amp;mdash;idempotency, backoff, leader election&amp;mdash;by proving correctness in adverse conditions. Safe shutdown, draining, and disaster drills build confidence that background work will recover without manual data repair.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Putting the patterns to work&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rails 3.4 job systems thrive when choosing between Sidekiq, Resque, and Que matches workload characteristics, idempotency keys and retry backoff strategies enforce correctness under failure, distributed cron with leader election keeps schedules reliable, out-of-order completion and result aggregation ensure accurate outcomes at scale, and observability with job latency and failure heatmaps keeps operators informed. Combine these with concurrency limits, effective-once delivery, and disciplined shutdown drills to keep background work robust as traffic and complexity grow.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html&quot;&gt;https://guides.rubyonrails.org/active_job_basics.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scoutapm.com/blog/resque-v-sidekiq-for-ruby-background-jobs-processing&quot;&gt;https://www.scoutapm.com/blog/resque-v-sidekiq-for-ruby-background-jobs-processing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.codeminer42.com/introducing-solid-queue-for-background-jobs/&quot;&gt;https://blog.codeminer42.com/introducing-solid-queue-for-background-jobs/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://skilldlabs.com/background-jobs-in-rails-explore-efficiency-sidekiq-and-resque/&quot;&gt;https://skilldlabs.com/background-jobs-in-rails-explore-efficiency-sidekiq-and-resque/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/development/sidekiq/&quot;&gt;https://docs.gitlab.com/development/sidekiq/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sidekiq/sidekiq/wiki/Best-Practices&quot;&gt;https://github.com/sidekiq/sidekiq/wiki/Best-Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/alex_aslam/the-art-of-the-resilient-worker-a-sidekiq-masters-guide-to-idempotency-retries-and-the-1jim&quot;&gt;https://dev.to/alex_aslam/the-art-of-the-resilient-worker-a-sidekiq-masters-guide-to-idempotency-retries-and-the-1jim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://compmath.korea.ac.kr/gitlab/help/development/sidekiq_style_guide.md&quot;&gt;https://compmath.korea.ac.kr/gitlab/help/development/sidekiq_style_guide.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh&quot;&gt;https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dalibornasevic.com/posts/83-distributed-cron-for-rails-apps-with-sidekiq-scheduler&quot;&gt;https://dalibornasevic.com/posts/83-distributed-cron-for-rails-apps-with-sidekiq-scheduler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/16055973/distributed-system-leader-election&quot;&gt;https://stackoverflow.com/questions/16055973/distributed-system-leader-election&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://railsdrop.com/2025/06/&quot;&gt;https://railsdrop.com/2025/06/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/24886371/how-to-clear-all-the-jobs-from-sidekiq&quot;&gt;https://stackoverflow.com/questions/24886371/how-to-clear-all-the-jobs-from-sidekiq&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://elitedev.in/ruby/ruby-on-rails-sidekiq-job-patterns-building-bulle/&quot;&gt;https://elitedev.in/ruby/ruby-on-rails-sidekiq-job-patterns-building-bulle/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/tooleroid/what-are-some-popular-background-job-processing-libraries-for-rails-eg-sidekiq-delayed-job-35i8&quot;&gt;https://dev.to/tooleroid/what-are-some-popular-background-job-processing-libraries-for-rails-eg-sidekiq-delayed-job-35i8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kbs4674.tistory.com/85&quot;&gt;https://kbs4674.tistory.com/85&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://remesch.com/2011/01/23/officer-the-ruby-lock-server-and-client/&quot;&gt;http://remesch.com/2011/01/23/officer-the-ruby-lock-server-and-client/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/rails/comments/d2ljre/your_opinion_on_best_practices_for_sidekiqredis/&quot;&gt;https://www.reddit.com/r/rails/comments/d2ljre/your_opinion_on_best_practices_for_sidekiqredis/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/ruby/comments/174rh3q/reflections_on_goodjob_for_solid_queue/&quot;&gt;https://www.reddit.com/r/ruby/comments/174rh3q/reflections_on_goodjob_for_solid_queue/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/toptal/active-job-style-guide&quot;&gt;https://github.com/toptal/active-job-style-guide&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/40</guid>
      <comments>https://timeslip2.tistory.com/entry/Rails-34-Job-Systems-8-Patterns-for-Background-Work#entry40comment</comments>
      <pubDate>Fri, 21 Nov 2025 21:55:00 +0900</pubDate>
    </item>
    <item>
      <title>5 Ways to Build Real-Time Apps with Ruby and NoSQL</title>
      <link>https://timeslip2.tistory.com/entry/5-Ways-to-Build-Real-Time-Apps-with-Ruby-and-NoSQL</link>
      <description>&lt;div&gt;
&lt;h2 id=&quot;websockets-with-action-cable-and-redis-backing&quot; data-ke-size=&quot;size26&quot;&gt;WebSockets with Action Cable and Redis backing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSockets with Action Cable and Redis backing remain the most straightforward way to build real-time apps with Ruby and NoSQL, wiring persistent connections to channels that broadcast updates with minimal latency. Action Cable integrates WebSockets into Rails with connections, channels, and streams, while Redis backing fans out messages across multiple app instances for horizontal scale in real-time apps. In newer stacks, Rails 8&amp;rsquo;s Solid Cable and database-backed pub/sub can reduce Redis dependency, but Redis backing is still a proven backbone for WebSockets and Action Cable in production real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;youtube&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;To stabilize WebSockets with Action Cable, configure heartbeats, connection limits, and back-off on reconnects; Redis backing keeps broadcasts consistent when pods autoscale in real-time apps with Ruby.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stanko.io/monitoring-actioncable-GdeaeHfIU4Yk&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stanko&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;For high fan-out, shard channels by key and use Redis stream patterns so WebSockets with Action Cable sustain throughput in real-time apps with NoSQL backends.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/05/01/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;appsignal&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;change-streams-in-mongodb-for-live-updates&quot; data-ke-size=&quot;size26&quot;&gt;Change streams in MongoDB for live updates&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Change streams in MongoDB for live updates let servers subscribe to inserts, updates, and deletes and push events over WebSockets to power real-time apps with Ruby and NoSQL. MongoDB change streams expose a watch cursor at collection, database, or deployment scope, filtering via aggregation so only relevant live updates reach subscribers. The official Ruby driver supports change streams, making change streams in MongoDB a first-class trigger for Action Cable or AnyCable broadcasts in real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://leocode.com/development/4-ways-to-get-real-time-database-updates-from-mongodb/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;leocode&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Ensure replica sets and proper read concern to use change streams in MongoDB for live updates at scale, especially on sharded clusters feeding real-time apps with Ruby.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.xuchao.org/docs/mongodb/changeStreams.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;xuchao&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Map each change stream event to a domain event before broadcasting so live updates remain stable even as schemas evolve in NoSQL-backed real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.mongodb.com/docs/ruby-driver/current/logging-and-monitoring/change-streams/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;mongodb&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;event-sourcing-with-append-only-logs&quot; data-ke-size=&quot;size26&quot;&gt;Event sourcing with append-only logs&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Event sourcing with append-only logs stores every fact as an immutable event, allowing real-time apps with Ruby and NoSQL to rebuild state and stream projections efficiently. An append-only log aligns with Kafka, Redis Streams, or NoSQL collections, and Ruby services can append and replay events to publish consistent updates to WebSockets or APIs. By treating the log as the source of truth, event sourcing with append-only logs simplifies auditing and temporal queries in real-time apps with Ruby.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.kurrent.io/blog/event-sourcing-audit&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kurrent&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keep event schemas versioned and add projection rebuilders so event sourcing with append-only logs survives refactors in NoSQL-centric real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.kurrent.io/blog/event-sourcing-audit&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kurrent&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;When throughput is extreme, partition the append-only log by aggregate to avoid hot shards in real-time apps with Ruby and NoSQL.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/26843088/append-only-log-event-database&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;backpressure-handling-on-bursty-workloads&quot; data-ke-size=&quot;size26&quot;&gt;Backpressure handling on bursty workloads&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backpressure handling on bursty workloads is critical for real-time apps with Ruby and NoSQL because producers can overwhelm WebSockets or databases. Implement bounded queues per connection, apply server-side pause/resume, and monitor WebSocket bufferedAmount to detect client lag as part of backpressure handling on bursty workloads. Rate-limit publish loops and drop/summarize low-priority messages so backpressure handling on bursty workloads preserves tail latency for high-value streams in real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://skylinecodes.substack.com/p/backpressure-in-websocket-streams&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;skylinecodes.substack&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use circuit breakers and load shedding to protect Redis/Mongo during spikes; coordinated backpressure handling on bursty workloads prevents cascading failures in real-time apps with Ruby.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/ably/challenges-of-scaling-websockets-3493&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Expose metrics and alarms around queue depth and send buffer saturation to tune backpressure handling on bursty workloads continuously.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://skylinecodes.substack.com/p/backpressure-in-websocket-streams&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;skylinecodes.substack&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sla-aware-fallbacks-during-nosql-outages&quot; data-ke-size=&quot;size26&quot;&gt;SLA-aware fallbacks during NoSQL outages&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SLA-aware fallbacks during NoSQL outages keep real-time apps with Ruby responsive when a NoSQL dependency degrades or fails. Combine timeouts, retries with jitter, and circuit breakers to trigger SLA-aware fallbacks during NoSQL outages, serving cached or approximate data within SLOs rather than timing out. Document priority paths that must return deterministic responses, and route non-critical updates to queues to drain later as part of SLA-aware fallbacks during NoSQL outages.&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;youtube&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;​&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://daily.dev/blog/dbaas-slas-what-to-know-in-2024&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;daily&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Maintain read-only modes, stale-while-revalidate caches, and graceful degradation banners so SLA-aware fallbacks during NoSQL outages are predictable to users of real-time apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;youtube&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Track fallback invocations and error budgets; SLA-aware fallbacks during NoSQL outages are only effective when observability guides when to fail open vs. fail closed in Ruby services.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://daily.dev/blog/dbaas-slas-what-to-know-in-2024&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;daily&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;youtube&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By combining WebSockets with Action Cable and Redis backing, change streams in MongoDB for live updates, event sourcing with append-only logs, backpressure handling on bursty workloads, and SLA-aware fallbacks during NoSQL outages, teams can deliver robust real-time apps with Ruby and NoSQL. These five ways complement each other: Redis-backed WebSockets distribute updates, MongoDB change streams trigger live events, append-only logs ensure auditability, backpressure keeps pipelines stable, and SLA-aware fallbacks keep SLAs during outages for real-time apps at scale.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;youtube&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html&quot;&gt;https://guides.rubyonrails.org/action_cable_overview.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/shettigarc/building-real-time-apps-with-rails-8-hotwire-actioncable-in-production-19fa&quot;&gt;https://dev.to/shettigarc/building-real-time-apps-with-rails-8-hotwire-actioncable-in-production-19fa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=bOlrHbhLzZE&quot;&gt;https://www.youtube.com/watch?v=bOlrHbhLzZE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.heroku.com/blog/real_time_rails_implementing_websockets_in_rails_5_with_action_cable/&quot;&gt;https://www.heroku.com/blog/real_time_rails_implementing_websockets_in_rails_5_with_action_cable/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/05/01/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable.html&quot;&gt;https://blog.appsignal.com/2024/05/01/anycable-for-ruby-on-rails-how-does-it-improve-over-action-cable.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dazralsky/actioncable&quot;&gt;https://github.com/dazralsky/actioncable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stanko.io/monitoring-actioncable-GdeaeHfIU4Yk&quot;&gt;https://stanko.io/monitoring-actioncable-GdeaeHfIU4Yk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://leocode.com/development/4-ways-to-get-real-time-database-updates-from-mongodb/&quot;&gt;https://leocode.com/development/4-ways-to-get-real-time-database-updates-from-mongodb/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xuchao.org/docs/mongodb/changeStreams.html&quot;&gt;https://www.xuchao.org/docs/mongodb/changeStreams.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/docs/ruby-driver/current/logging-and-monitoring/change-streams/&quot;&gt;https://www.mongodb.com/docs/ruby-driver/current/logging-and-monitoring/change-streams/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kurrent.io/blog/event-sourcing-audit&quot;&gt;https://www.kurrent.io/blog/event-sourcing-audit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://oierud.net/bliki/EventSourcingInRuby.html&quot;&gt;http://oierud.net/bliki/EventSourcingInRuby.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/26843088/append-only-log-event-database&quot;&gt;https://stackoverflow.com/questions/26843088/append-only-log-event-database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://skylinecodes.substack.com/p/backpressure-in-websocket-streams&quot;&gt;https://skylinecodes.substack.com/p/backpressure-in-websocket-streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/ably/challenges-of-scaling-websockets-3493&quot;&gt;https://dev.to/ably/challenges-of-scaling-websockets-3493&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=xTpjg2Q2NC4&quot;&gt;https://www.youtube.com/watch?v=xTpjg2Q2NC4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://daily.dev/blog/dbaas-slas-what-to-know-in-2024&quot;&gt;https://daily.dev/blog/dbaas-slas-what-to-know-in-2024&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://revs.runtime-revolution.com/implementing-websockets-in-ruby-on-rails-with-action-cable-056ca78cf555&quot;&gt;https://revs.runtime-revolution.com/implementing-websockets-in-ruby-on-rails-with-action-cable-056ca78cf555&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/blog/ruby-meets-high-frequency-data-handling-real-time-streams/&quot;&gt;https://ruby-doc.org/blog/ruby-meets-high-frequency-data-handling-real-time-streams/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/in-a-hurry/core-concepts&quot;&gt;https://www.hellointerview.com/learn/system-design/in-a-hurry/core-concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/38</guid>
      <comments>https://timeslip2.tistory.com/entry/5-Ways-to-Build-Real-Time-Apps-with-Ruby-and-NoSQL#entry38comment</comments>
      <pubDate>Thu, 20 Nov 2025 21:54:18 +0900</pubDate>
    </item>
    <item>
      <title>12 Ways to Hardening Rails Against Data Leaks</title>
      <link>https://timeslip2.tistory.com/entry/12-Ways-to-Hardening-Rails-Against-Data-Leaks</link>
      <description>&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Hardening Rails against data leaks requires a layered program that starts with PII discovery and field-level encryption, chooses tokenization vs hashing for identifiers, enforces access logging and immutable audit trails, applies differential privacy for analytics exports, and automates data retention and deletion workflows. These 12 ways combine preventive controls, detective signals, and compliant processes to reduce exposure while preserving developer velocity.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;1) PII discovery and field-level encryption&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Begin with PII discovery and field-level encryption by inventorying models, columns, and logs that may contain sensitive fields like emails, names, and tokens. Use modern field-level encryption in Rails (for example, Lockbox) to encrypt columns with per-environment keys and rotate keys safely, avoiding legacy patterns that are unmaintained. Store keys in a KMS or environment secrets and ensure deterministic encryption only where exact-match queries are required.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;2) Tokenization vs hashing for identifiers&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Choose tokenization vs hashing for identifiers based on reversibility and utility. Tokenization maps original values to random tokens via a vault for reversible lookups and PCI-like scope reduction, while hashing is one-way and best for integrity checks and deduplication without recoverability. Reserve encryption for frequent access to plaintext, tokenization for referential use, and hashing with salt or pepper for privacy-preserving joins.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;3) Access logging and immutable audit trails&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Enforce access logging and immutable audit trails so reads and writes on PII leave verifiable traces. Append-only, tamper-evident logs with cryptographic sealing and WORM storage strengthen forensics and compliance, making audit trails trustworthy during incident response. Centralize access logs and correlate with application user IDs and roles to detect anomalous access patterns.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;4) Differential privacy for analytics exports&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Apply differential privacy for analytics exports to prevent re-identification while keeping aggregate utility. Add calibrated noise to counts, rates, and histograms, tune epsilon budgets, and bound sensitivity with clipping or bucketing. Run DP mechanisms in pipelines that export to BI tools so analysts work with privacy-safe datasets by default.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;5) Data retention and deletion workflows&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Implement data retention and deletion workflows that codify maximum lifetimes and automated deletion paths. Model-level policies should remove or redact PII when accounts close or legal bases expire, with cascade-delete or wipe libraries for GDPR-compliant removal. Record proofs of deletion and support subject access and erasure requests with time-boxed SLAs.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;6) Secrets and key management&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Harden secrets and key management by extracting keys from app configs into a KMS, rotating regularly, and separating duties for decryption. Use envelope encryption, short-lived credentials, and scope-limited roles so compromises don&amp;rsquo;t lead to universal decryption. Log all key usage to the immutable audit trail to catch misuse.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;7) Least privilege and ABAC/RBAC&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Adopt least privilege with role- or attribute-based access control that denies PII reads by default. Fence off production data with break-glass flows requiring approvals and time-limited grants, and block risky queries at the ORM or service layer. Apply row- and column-level filters so only necessary scopes are exposed to each service or user.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;8) Structured logging without PII&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Normalize structured logging and scrub PII at emission to prevent leaks into logs. Favor event IDs, user IDs, and token references over raw values, and add allowlists to serializers to avoid accidental inclusion. Route JSON logs to a central sink and add redaction middleware to catch strays before ingestion.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;9) Encryption in transit and at rest&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Enforce TLS with modern ciphers for all service edges and database connections, and enable database-at-rest encryption with managed keys. For files, encrypt objects client-side or with server-side KMS keys and strict bucket policies. Verify cipher suites and TLS versions in CI to avoid regressions.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;10) Data minimization and pseudonymization&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Practice data minimization by dropping nonessential PII at ingestion and replacing direct identifiers with pseudonyms. Keep mapping tables in hardened vaults and expose only pseudonymized values to analytics and non-critical services. This reduces breach impact and narrows compliance scope.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;11) Testing, red-teaming, and DLP&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Continuously test with fake PII in staging, run red-team exercises that simulate exfiltration, and deploy data loss prevention rules on egress points. Scan S3, backups, and analytics exports for unsafe columns and revoke public ACLs or presigned URLs that exceed policy. Treat backups as first-class: encrypt, rotate, and verify restore paths honor deletion requests.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;12) Runbooks, training, and culture&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Publish runbooks for suspected data leaks, including containment, revocation, customer comms, and regulator notifications. Train engineers on tokenization vs hashing, field-level encryption, and differential privacy design choices, and run periodic drills. Make privacy by design a default by gating risky changes in code review with PII checklists.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Practical Rails patterns and tools&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;In Rails, prefer Lockbox for field-level encryption, store LOCKBOX_MASTER_KEY in a secure vault, and avoid logging params that include PII. Use background jobs to tokenize or hash identifiers before persistence, and keep audit logs append-only with cryptographic chaining. For analytics exports, run DP noise addition in ETL and mark datasets with privacy metadata to prevent accidental raw exports.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Conclusion&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;By combining PII discovery and field-level encryption, robust choices around tokenization vs hashing for identifiers, strict access logging and immutable audit trails, differential privacy for analytics exports, and disciplined data retention and deletion workflows, Rails teams can harden against data leaks without sacrificing speed. These 12 ways make privacy concrete&amp;mdash;operational, measurable, and resilient in the face of incidents.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://discuss.rubyonrails.org/t/security-encryption-and-privacy-pii-related-additions-to-activerecord/73485&quot;&gt;https://discuss.rubyonrails.org/t/security-encryption-and-privacy-pii-related-additions-to-activerecord/73485&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/17407984/how-to-generate-encryption-key-for-use-with-attr-encrypted&quot;&gt;https://stackoverflow.com/questions/17407984/how-to-generate-encryption-key-for-use-with-attr-encrypted&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ankane/lockbox&quot;&gt;https://github.com/ankane/lockbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ankane.org/sensitive-data-rails&quot;&gt;https://ankane.org/sensitive-data-rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/mikerogers0/how-to-encrypt-fields-in-ruby-on-rails-with-lockbox-58g6&quot;&gt;https://dev.to/mikerogers0/how-to-encrypt-fields-in-ruby-on-rails-with-lockbox-58g6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.protecto.ai/blog/tokenization-vs-hashing-which-one-is-better-for-data-security/&quot;&gt;https://www.protecto.ai/blog/tokenization-vs-hashing-which-one-is-better-for-data-security/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dl.acm.org/doi/full/10.1145/3698322.3698351&quot;&gt;https://dl.acm.org/doi/full/10.1145/3698322.3698351&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thinkaicorp.com/privacy-preserving-analytics-using-differential-privacy-in-data-pipelines/&quot;&gt;https://thinkaicorp.com/privacy-preserving-analytics-using-differential-privacy-in-data-pipelines/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.globalapptesting.com/engineering/activerecord-models-how-to-remove-data-in-gdpr-compliant-way&quot;&gt;https://www.globalapptesting.com/engineering/activerecord-models-how-to-remove-data-in-gdpr-compliant-way&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=sEOLtIGkDeM&quot;&gt;https://www.youtube.com/watch?v=sEOLtIGkDeM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cryptomathic.com/blog/what-is-banking-grade-tokenization-according-to-pci-dss&quot;&gt;https://www.cryptomathic.com/blog/what-is-banking-grade-tokenization-according-to-pci-dss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hoop.dev/blog/immutable-audit-logs-in-github-ci-cd-the-backbone-of-trust/&quot;&gt;https://hoop.dev/blog/immutable-audit-logs-in-github-ci-cd-the-backbone-of-trust/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2311.16104&quot;&gt;https://arxiv.org/abs/2311.16104&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/alex_aslam/event-sourcing-for-gdpr-how-to-forget-data-without-breaking-history-4013&quot;&gt;https://dev.to/alex_aslam/event-sourcing-for-gdpr-how-to-forget-data-without-breaking-history-4013&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/rails/comments/fq49wk/has_any_of_you_worked_on_a_hipaa_codebase_advice/&quot;&gt;https://www.reddit.com/r/rails/comments/fq49wk/has_any_of_you_worked_on_a_hipaa_codebase_advice/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.skyflow.com/post/does-hashing-sensitive-customer-data-protect-privacy&quot;&gt;https://www.skyflow.com/post/does-hashing-sensitive-customer-data-protect-privacy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/startups/comments/1g427cf/validate_my_idea_immutable_audit_log_api/&quot;&gt;https://www.reddit.com/r/startups/comments/1g427cf/validate_my_idea_immutable_audit_log_api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/html/2411.04710v1&quot;&gt;https://arxiv.org/html/2411.04710v1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://growth-onomics.com/gdpr-data-retention-rules-what-marketers-need/&quot;&gt;https://growth-onomics.com/gdpr-data-retention-rules-what-marketers-need/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/rbglod/sensitive-data-encryption-in-rails-1f1&quot;&gt;https://dev.to/rbglod/sensitive-data-encryption-in-rails-1f1&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/39</guid>
      <comments>https://timeslip2.tistory.com/entry/12-Ways-to-Hardening-Rails-Against-Data-Leaks#entry39comment</comments>
      <pubDate>Wed, 19 Nov 2025 21:54:38 +0900</pubDate>
    </item>
    <item>
      <title>6 Patterns for Zero-Downtime Rails Migrations</title>
      <link>https://timeslip2.tistory.com/entry/6-Patterns-for-Zero-Downtime-Rails-Migrations</link>
      <description>&lt;div&gt;
&lt;h2 id=&quot;online-index-creation-and-concurrent-operations&quot; data-ke-size=&quot;size26&quot;&gt;Online index creation and concurrent operations&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Online index creation and concurrent operations are the backbone of zero-downtime Rails migrations because creating indexes synchronously can lock writes and stall production traffic. In PostgreSQL, use CREATE INDEX CONCURRENTLY or, in Rails, add_index with algorithm: :concurrently and disable_ddl_transaction! to keep online index creation running without long blocking locks. These concurrent operations take longer and add I/O load, but online index creation trades duration for availability, which is the correct default for zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://semaphore.io/blog/2017/06/21/faster-rails-indexing-large-database-tables.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;semaphore&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Online index creation should be paired with low lock timeouts and retries to survive transient locks during concurrent operations in zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/database/avoiding_downtime_in_migrations/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Prefer add_concurrent_foreign_key or split add and validate steps to emulate online index creation semantics for foreign keys under concurrent operations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/migration_style_guide/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;backfill-jobs-with-throttling-and-checkpoints&quot; data-ke-size=&quot;size26&quot;&gt;Backfill jobs with throttling and checkpoints&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backfill jobs with throttling and checkpoints decouple data movement from DDL so zero-downtime Rails migrations don&amp;rsquo;t spike load. Use Sidekiq iteration, cursors, and batch sizes, and add kill switches and backoff to throttle backfill jobs when the database is hot; checkpoints let backfill jobs resume safely after interruptions. With feature-flagged parameters, backfill jobs can raise concurrency gradually, ensuring zero-downtime Rails migrations remain safe while catching up historical data.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/bajena/mastering-large-backfill-migrations-in-rails-and-sidekiq-2i21&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Run backfill jobs outside the migration transaction, and don&amp;rsquo;t combine schema DDL with backfill in a single step, preserving zero-downtime Rails migrations under peak load.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/ankane/strong_migrations&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Checkpoints plus metrics help you tune batch sizes dynamically so backfill jobs maintain latency budgets while zero-downtime Rails migrations proceed.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/bajena/mastering-large-backfill-migrations-in-rails-and-sidekiq-2i21&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;dual-writedual-read-phases-for-schema-transitions&quot; data-ke-size=&quot;size26&quot;&gt;Dual-write/dual-read phases for schema transitions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dual-write/dual-read phases for schema transitions allow new columns or tables to be introduced while old ones continue serving traffic. Start with dual-write on every code path, validate integrity, then gradually shift reads (dual-read) to the new schema; this sequence keeps zero-downtime Rails migrations reversible during validation windows. For cross-DB transitions, dual-write/dual-read phases can be gated by flags and auditing queries, making schema transitions predictable even when multiple databases are involved.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_multiple_databases.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Design idempotent writers and compensating jobs to reconcile drift discovered during dual-read, ensuring schema transitions remain safe for zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57260123/performing-writes-to-tables-in-two-separate-databases-from-a-single-monolithic-s&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Keep observability on both sources during dual-write/dual-read phases to verify parity before cutting over, then retire the old path without risk to zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_multiple_databases.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;feature-flags-to-decouple-deploy-from-release&quot; data-ke-size=&quot;size26&quot;&gt;Feature flags to decouple deploy from release&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feature flags to decouple deploy from release let teams ship the code that supports zero-downtime Rails migrations before enabling the user-visible change. With feature flags, risky toggles like enabling dual-read or activating a new index path can be rolled out to small cohorts and rolled back instantly if needed, without redeploys. This decoupling makes zero-downtime Rails migrations routine by separating infrastructure readiness from customer exposure.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://devcycle.com/blog/decoupling-releases-from-deployments-with-feature-flags&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;devcycle&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use gradual rollouts, segmentation, and A/B tests under feature flags to validate performance of schema transitions, keeping zero-downtime Rails migrations uneventful.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.flagsmith.com/blog/deployment-is-not-a-release&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;flagsmith&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Maintain a global kill switch for migration-related features so a single toggle can pause the rollout if error budgets are threatened during zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://devcycle.com/blog/decoupling-releases-from-deployments-with-feature-flags&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;devcycle&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;rollback-safe-ddl-strategies-and-canary-checks&quot; data-ke-size=&quot;size26&quot;&gt;Rollback-safe DDL strategies and canary checks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rollback-safe DDL strategies and canary checks transform dangerous operations into staged, observable steps. Prefer additive changes first (add columns nullable, backfill, then add constraints), and use with_lock_retries, lock timeouts, and validate: false to reduce blocking risk; canary checks run the change on a small partition or table sample before globalizing. This keeps zero-downtime Rails migrations controlled, with safe escape hatches if anomalies appear.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/braintree/pg_ha_migrations&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use tools that flag unsafe steps&amp;mdash;like strong_migrations or pg_ha_migrations&amp;mdash;to enforce rollback-safe DDL and guide canary checks for zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/ankane/strong_migrations&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Avoid destructive DDL (drop or type change) until code is migrated away and canary checks pass, preserving the rollback path in zero-downtime Rails migrations.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/database/avoiding_downtime_in_migrations/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;operational-playbooks-and-incident-drills&quot; data-ke-size=&quot;size26&quot;&gt;Operational playbooks and incident drills&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Operational playbooks and incident drills institutionalize these patterns so teams execute zero-downtime Rails migrations consistently. Document online index creation runbooks, backfill jobs schedules with throttling, dual-write/dual-read cutovers, feature flag matrices, and rollback-safe DDL steps with canary checks. By rehearsing incident drills&amp;mdash;like failing forward by disabling flags or pausing backfill jobs&amp;mdash;zero-downtime Rails migrations become a muscle memory rather than a gamble.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.cloudbees.com/blog/rails-migrations-zero-downtime&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;cloudbees&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Integrate dashboards for migration KPIs (lock waits, replication lag, queue depth) so operators can pause or proceed during zero-downtime Rails migrations with confidence.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://docs.gitlab.com/development/database/avoiding_downtime_in_migrations/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;gitlab&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;After-action reviews should refine playbooks for online index creation, backfill jobs, schema transitions, and canary checks so future zero-downtime Rails migrations run even smoother.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://github.com/braintree/pg_ha_migrations&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://semaphore.io/blog/2017/06/21/faster-rails-indexing-large-database-tables.html&quot;&gt;https://semaphore.io/blog/2017/06/21/faster-rails-indexing-large-database-tables.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jacopretorius.net/2017/05/zero-downtime-migrations-in-rails.html&quot;&gt;https://jacopretorius.net/2017/05/zero-downtime-migrations-in-rails.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/development/database/avoiding_downtime_in_migrations/&quot;&gt;https://docs.gitlab.com/development/database/avoiding_downtime_in_migrations/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitlab.com/development/migration_style_guide/&quot;&gt;https://docs.gitlab.com/development/migration_style_guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/bajena/mastering-large-backfill-migrations-in-rails-and-sidekiq-2i21&quot;&gt;https://dev.to/bajena/mastering-large-backfill-migrations-in-rails-and-sidekiq-2i21&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ankane/strong_migrations&quot;&gt;https://github.com/ankane/strong_migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.appsignal.com/2024/03/20/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations.html&quot;&gt;https://blog.appsignal.com/2024/03/20/good-database-migration-practices-for-your-ruby-on-rails-app-using-strong-migrations.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_multiple_databases.html&quot;&gt;https://guides.rubyonrails.org/active_record_multiple_databases.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57260123/performing-writes-to-tables-in-two-separate-databases-from-a-single-monolithic-s&quot;&gt;https://stackoverflow.com/questions/57260123/performing-writes-to-tables-in-two-separate-databases-from-a-single-monolithic-s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/rails/comments/1f9yd25/is_it_possible_to_writeupdate_to_2_databases_at/&quot;&gt;https://www.reddit.com/r/rails/comments/1f9yd25/is_it_possible_to_writeupdate_to_2_databases_at/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devcycle.com/blog/decoupling-releases-from-deployments-with-feature-flags&quot;&gt;https://devcycle.com/blog/decoupling-releases-from-deployments-with-feature-flags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.flagsmith.com/blog/deployment-is-not-a-release&quot;&gt;https://www.flagsmith.com/blog/deployment-is-not-a-release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://boringrails.com/articles/feature-flags-simplest-thing-that-could-work/&quot;&gt;https://boringrails.com/articles/feature-flags-simplest-thing-that-could-work/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/braintree/pg_ha_migrations&quot;&gt;https://github.com/braintree/pg_ha_migrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cloudbees.com/blog/rails-migrations-zero-downtime&quot;&gt;https://www.cloudbees.com/blog/rails-migrations-zero-downtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://planetscale.com/blog/zero-downtime-rails-migrations-planetscale-rails-gem&quot;&gt;https://planetscale.com/blog/zero-downtime-rails-migrations-planetscale-rails-gem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/PostgreSQL/comments/svxc23/zerodowntime_postgresql_migrations_for_ruby_on/&quot;&gt;https://www.reddit.com/r/PostgreSQL/comments/svxc23/zerodowntime_postgresql_migrations_for_ruby_on/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1395672/how-do-i-run-a-migration-without-starting-a-transaction-in-rails&quot;&gt;https://stackoverflow.com/questions/1395672/how-do-i-run-a-migration-without-starting-a-transaction-in-rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57242929/how-do-i-roll-back-this-rails-migration&quot;&gt;https://stackoverflow.com/questions/57242929/how-do-i-roll-back-this-rails-migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/LendingHome/zero_downtime_migrations&quot;&gt;https://github.com/LendingHome/zero_downtime_migrations&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/37</guid>
      <comments>https://timeslip2.tistory.com/entry/6-Patterns-for-Zero-Downtime-Rails-Migrations#entry37comment</comments>
      <pubDate>Tue, 18 Nov 2025 21:53:33 +0900</pubDate>
    </item>
    <item>
      <title>Redis at Scale: 8 Patterns for Ruby Applications</title>
      <link>https://timeslip2.tistory.com/entry/Redis-at-Scale-8-Patterns-for-Ruby-Applications</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Redis at scale shines when patterns are chosen deliberately for event propagation, coordination, approximate analytics, atomicity, and durability. Pub/Sub vs Streams for event propagation, distributed locks with Redlock and contention handling, HyperLogLog and Bloom filters for cardinality/exists checks, Lua scripting for atomic multi-key operations, and snapshotting and AOF strategies for durability together form a practical toolkit for high-throughput Ruby applications.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;kanado2000.tistory+1&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pub/Sub vs Streams for event propagation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pub/Sub vs Streams for event propagation is the first decision point: Redis Pub/Sub blasts messages to active subscribers without persistence, while Redis Streams persist messages with IDs, retention policies, and consumer groups. Pub/Sub vs Streams for event propagation favors Pub/Sub for fire-and-forget real-time fan-out like live notifications, whereas Streams enable durable queues, replay, and backpressure handling for pipelines and jobs.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pub/Sub vs Streams for event propagation compares delivery semantics and durability; Pub/Sub loses messages if no subscriber is listening, while Streams store entries and let consumer groups acknowledge processing. Pub/Sub vs Streams for event propagation also affects scaling choices&amp;mdash;Streams fit offline delivery, retries, and consumer parallelism, while Pub/Sub excels at ultra-low-latency transient broadcasts.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pub/Sub vs Streams for event propagation in Ruby often starts with Redis clients that implement XADD, XREADGROUP, and XACK for Streams, and SUBSCRIBE/PUBLISH for Pub/Sub, wiring background workers to consume reliably. Pub/Sub vs Streams for event propagation should be selected per use case, sometimes pairing Pub/Sub for instant UI pushes with Streams for durable processing behind the scenes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Distributed locks with Redlock and contention handling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Distributed locks with Redlock and contention handling coordinate exclusive access to scarce resources across Ruby processes and nodes. Distributed locks with Redlock and contention handling use multiple Redis masters or a clustered setup to acquire a majority of locks with TTLs, mitigating single-node failures and clock drift.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Distributed locks with Redlock and contention handling must implement jittered backoff and deadlines to avoid stampedes under contention, and they should be reserved for short, critical sections. Distributed locks with Redlock and contention handling should not replace database constraints for core invariants; use them as advisory locks to limit throughput while persisting truth in a transactional store.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Distributed locks with Redlock and contention handling in Ruby are typically wrapped in ensure blocks to guarantee release, with renewal (&amp;ldquo;lock keepalive&amp;rdquo;) only when the critical section is provably safe to extend. Distributed locks with Redlock and contention handling also benefit from metrics&amp;mdash;lock wait time, acquisition failure rate, and TTL expirations&amp;mdash;to spot hotspots.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HyperLogLog and Bloom filters for cardinality/exists checks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HyperLogLog and Bloom filters for cardinality/exists checks provide memory-efficient approximations for large sets. HyperLogLog and Bloom filters for cardinality/exists checks let Ruby apps count unique users or events (PFADD/PFCOUNT) and test membership with controllable false-positive rates (Bloom filters), trading exactness for speed and footprint.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HyperLogLog and Bloom filters for cardinality/exists checks are ideal ahead of expensive work, e.g., skipping costly deduplication when the Bloom filter says &amp;ldquo;not present,&amp;rdquo; or estimating reach without allocating gigabytes for exact sets. HyperLogLog and Bloom filters for cardinality/exists checks should be tuned for error bounds and periodically reset or merged to manage drift over time windows.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HyperLogLog and Bloom filters for cardinality/exists checks integrate well with event ingestion paths and dashboards, providing near-real-time metrics with tiny memory overhead compared to hash sets. HyperLogLog and Bloom filters for cardinality/exists checks also play nicely with sharded keys to spread load across Redis Cluster slots.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lua scripting for atomic multi-key operations&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lua scripting for atomic multi-key operations turns sequences of Redis commands into a single, atomic execution on the server. Lua scripting for atomic multi-key operations eliminates race conditions in counters, inventory reservations, and composite cache updates by evaluating scripts with EVAL/EVALSHA.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lua scripting for atomic multi-key operations supports validation-then-set patterns, multi-read/multi-write updates, and conditional invalidation, all without exposing intermediate states to other clients. Lua scripting for atomic multi-key operations requires careful key passing and time limits; keep scripts deterministic, small, and side-effect-free outside Redis to preserve latency.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lua scripting for atomic multi-key operations in Ruby typically preloads scripts and calls them by SHA for performance, with error handling that falls back gracefully when scripts are flushed. Lua scripting for atomic multi-key operations should also include telemetry on script runtimes and failures to prevent tail-latency surprises.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Snapshotting and AOF strategies for durability&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snapshotting and AOF strategies for durability determine how Redis persists data, balancing performance with recovery guarantees. Snapshotting and AOF strategies for durability include RDB snapshots for point-in-time saves and AOF for append-only command logs, which can be fsynced on every write, every second, or left to the OS.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snapshotting and AOF strategies for durability often combine both: periodic RDB for fast, compact backups and AOF for minimizing data loss between snapshots. Snapshotting and AOF strategies for durability should consider rewrite policies, background save overhead, and AOF rewrite thresholds to avoid blocking under heavy write loads.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Snapshotting and AOF strategies for durability must be paired with replication and cluster failover realities; asynchronous replication risks last-second loss, so critical systems may choose stricter fsync or multi-region redundancy. Snapshotting and AOF strategies for durability also require restore drills and version pinning so crash recovery behaves predictably in production.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hot key mitigation and client-side sharding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hot key mitigation and client-side sharding address uneven load when a single key receives disproportionate traffic. Hot key mitigation and client-side sharding can duplicate the same value across multiple keys with a random suffix and read from a random replica to spread QPS.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hot key mitigation and client-side sharding can add a small in-process cache in Ruby for extremely hot items, reducing round trips to Redis. Hot key mitigation and client-side sharding should be observable&amp;mdash;track per-key hit rates and latencies to detect skew early and rebalance.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rate limiting with sliding windows and tokens&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rate limiting with sliding windows and tokens relies on Redis atomic operations to enforce fair use on APIs and background jobs. Rate limiting with sliding windows and tokens uses INCR with TTL, sorted sets, or Lua scripts to implement token buckets or sliding windows with accurate per-identity enforcement.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rate limiting with sliding windows and tokens must define fail-open or fail-closed modes during Redis outages, along with sensible key TTLs to prevent unbounded growth of counters. Rate limiting with sliding windows and tokens benefits from sharding keys by user or region to reduce lock contention in clusters.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Caching patterns and versioned keys&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Caching patterns and versioned keys keep data fresh without stampeding backend stores. Caching patterns and versioned keys use cache-aside for most dynamic data, write-through for critical consistency, and negative caching for common misses.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Caching patterns and versioned keys employ version suffixes and &amp;ldquo;generational keys&amp;rdquo; to invalidate safely on deploys or content updates, avoiding stale reads across Ruby processes and regions. Caching patterns and versioned keys should track hit ratio, evictions, and per-command latency to tune TTLs and memory policy in production.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;hellointerview&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bringing the patterns together&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis at scale in Ruby succeeds by using Pub/Sub vs Streams for event propagation based on delivery semantics, relying on distributed locks with Redlock and contention handling only for short critical sections, applying HyperLogLog and Bloom filters for cardinality/exists checks in analytics-heavy paths, harnessing Lua scripting for atomic multi-key operations where consistency matters, and choosing snapshotting and AOF strategies for durability that match business RPO/RTO. Redis at scale then rounds out with hot key mitigation and client-side sharding, rate limiting with sliding windows and tokens, and caching patterns and versioned keys to stabilize latency and cost as throughput grows.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kanado2000.tistory&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kanado2000.tistory.com/138&quot;&gt;https://kanado2000.tistory.com/138&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&quot;&gt;https://www.hellointerview.com/learn/system-design/deep-dives/redis?dslateid=cmhb20nc3000n08ad9shxeset&amp;amp;dslateposition=1&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/33</guid>
      <comments>https://timeslip2.tistory.com/entry/Redis-at-Scale-8-Patterns-for-Ruby-Applications#entry33comment</comments>
      <pubDate>Mon, 17 Nov 2025 21:51:47 +0900</pubDate>
    </item>
    <item>
      <title>11 Database Tuning Tactics for Active Record</title>
      <link>https://timeslip2.tistory.com/entry/11-Database-Tuning-Tactics-for-Active-Record</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Active Record performance scales when indexing, query shape, and maintenance cadence are tuned together, combining composite indexes and covering index strategies, N+1 elimination with includes/preload/eager_load, partial and functional indexes for selective filters, deferred constraints and bulk import pipelines, and a disciplined vacuum/analyze cadence with tuned autovacuum thresholds. These database tuning tactics for Active Record reduce I/O, shrink tail latency, and keep write throughput predictable as data grows into the millions and billions of rows.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot; data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&quot;&gt;elitedev+1&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Composite indexes and covering index strategies&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composite indexes and covering index strategies are cornerstone tactics in Active Record because most production queries filter on multiple attributes and often sort by time. Composite indexes and covering index strategies should order columns by selectivity and sort keys, for example add_index :orders, [:user_id, :status, :created_at] to satisfy WHERE and ORDER BY in one index scan while avoiding a table hit.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;nonstopio&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composite indexes and covering index strategies can use PostgreSQL&amp;rsquo;s INCLUDE clause to add non-key columns so a query becomes index-only, reducing random I/O dramatically on read-heavy endpoints that select a small set of fields. Composite indexes and covering index strategies require periodic audits of pg_stat_all_indexes to drop unused or redundant indexes that slow down writes and bloat storage.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;elitedev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composite indexes and covering index strategies also interact with pagination; aligning indexes with ORDER BY created_at DESC and a leading filter reduces sort buffers and enables keyset pagination for consistent performance at scale. Composite indexes and covering index strategies should be validated with EXPLAIN (ANALYZE, BUFFERS) to ensure the planner actually uses the intended index paths under real parameter values.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;nonstopio&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;N+1 elimination with includes/preload/eager_load&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N+1 elimination with includes/preload/eager_load cuts query counts from N+1 to a small constant by fetching associations in bulk. N+1 elimination with includes/preload/eager_load uses includes by default, which behaves like preload unless the association is referenced in WHERE/ORDER, in which case it switches to eager_load and a LEFT OUTER JOIN.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/69242991/when-is-better-to-use-preload-or-eager-load-or-includes&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N+1 elimination with includes/preload/eager_load prefers preload when joining would multiply rows or apply filters incorrectly, issuing one query per association while keeping the parent result set stable. N+1 elimination with includes/preload/eager_load turns to eager_load when filtering or ordering on association columns is needed, letting the database execute a single joined query that Active Record de-duplicates.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.tencentcloud.com/techpedia/129781&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;tencentcloud&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N+1 elimination with includes/preload/eager_load should be enforced with the Bullet or Prosopite gems during development to fail builds on regressions, and code reviews should check views and serializers where implicit association access often sneaks in. N+1 elimination with includes/preload/eager_load pairs well with select to narrow columns and avoid large object materialization, especially on API endpoints.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.scoutapm.com/blog/activerecord-includes-vs-joins-vs-preload-vs-eager-load-when-and-where&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;scoutapm&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Partial and functional indexes for selective filters&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial and functional indexes for selective filters keep index sizes small and scans targeted, which improves both read performance and write throughput. Partial and functional indexes for selective filters index only rows matching common predicates, such as active = true or deleted_at IS NULL, aligning with scope usage in Active Record.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;elitedev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial and functional indexes for selective filters shine when a query touches a minority slice of a very large table, allowing the planner to avoid full-index scans across cold data. Partial and functional indexes for selective filters also include expression indexes, like indexing LOWER(email) or JSONB fields, mapping directly to case-insensitive or document queries used in Rails apps.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.mintbit.com/blog/custom-database-indexes-in-rails/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;mintbit&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Partial and functional indexes for selective filters should be documented next to the scope definitions they serve, and migrations must ensure the predicate exactly matches the query text so the index is picked. Partial and functional indexes for selective filters reduce autovacuum pressure and storage overhead, which matters at terabyte scale.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://rizqimulki.com/postgresql-autovacuum-tuning-maintaining-performance-in-secure-systems-69e9e2ca6188&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;rizqimulki&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Deferred constraints and bulk import pipelines&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deferred constraints and bulk import pipelines help Active Record ingest large batches without thrashing on FK checks and unique validation per row. Deferred constraints and bulk import pipelines rely on setting DEFERRABLE INITIALLY DEFERRED on foreign keys where correct-by-transaction is acceptable, allowing constraint checks at commit rather than per statement.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deferred constraints and bulk import pipelines combine with COPY FROM or activerecord-import to load millions of rows while keeping indexes hot and minimizing lock contention. Deferred constraints and bulk import pipelines also benefit from disabling triggers or secondary indexes temporarily for one-off backfills, then rebuilding them once, which is significantly faster than updating them incrementally.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://railsdrop.com/2025/04/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;railsdrop&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deferred constraints and bulk import pipelines should include retryable chunks and idempotent staging tables to resume failed loads, plus post-load ANALYZE to refresh statistics before production traffic hits the new data. Deferred constraints and bulk import pipelines reduce application-level validation overhead by moving integrity checks to the database boundary at the right time.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Vacuum/analyze cadence and autovacuum thresholds&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vacuum/analyze cadence and autovacuum thresholds keep bloat under control and statistics fresh so the planner chooses the right indexes. Vacuum/analyze cadence and autovacuum thresholds often need per-table tuning beyond defaults; raising autovacuum_work_mem avoids multiple index passes during vacuum, and adjusting autovacuum_vacuum_scale_factor for hot tables prevents runaway bloat.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://pganalyze.com/blog/introducing-vacuum-advisor-postgres&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;pganalyze&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vacuum/analyze cadence and autovacuum thresholds should be monitored with tools that surface skipped autovacuums due to locks, worker saturation, and long xmin horizons that block cleanup. Vacuum/analyze cadence and autovacuum thresholds also benefit from explicit manual VACUUM (ANALYZE) after massive bulk imports or deletes, ensuring the planner sees the new data distribution promptly.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/37261697/how-to-efficiently-vacuum-analyze-tables-in-postgres&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vacuum/analyze cadence and autovacuum thresholds tie directly to performance; frequent vacuuming helps keep active pages in memory and index tuple maps lean, which shows up as lower buffer hits and more stable latency. Vacuum/analyze cadence and autovacuum thresholds should be part of SRE runbooks, with alerting on table bloat indicators and age toward wraparound.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://wiki.postgresql.org/wiki/Introduction_to_VACUUM,_ANALYZE,_EXPLAIN,_and_COUNT&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;wiki.postgresql&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Query refactoring and covering selects&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query refactoring and covering selects go hand-in-hand with composite indexes and covering index strategies to avoid table hits. Query refactoring and covering selects means selecting only the columns needed by the response and ensuring those columns are included in the covering index to enable index-only scans.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;nonstopio&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query refactoring and covering selects should replace OFFSET/LIMIT pagination with keyset pagination using the indexed sort key, avoiding large OFFSET skips that degrade as tables grow. Query refactoring and covering selects may also employ materialized views for expensive aggregates, refreshing them on schedules aligned with business SLAs.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/rails-query-optimization-part-1-introduction-best-practices-lqsxf&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;linkedin&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concurrency-aware connection and pool sizing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrency-aware connection and pool sizing ensures that Active Record doesn&amp;rsquo;t oversubscribe the database, which would turn CPU wait into queueing delays. Concurrency-aware connection and pool sizing aligns Puma threads, Sidekiq concurrency, and database pool size so that each worker has a connection without starving others.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_querying.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concurrency-aware connection and pool sizing should use statement timeouts and query killers for runaway requests, protecting shared resources during incident conditions. Concurrency-aware connection and pool sizing also interacts with autovacuum&amp;mdash;too many long transactions can stall cleanup, so keep transactions short in request handlers and jobs.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://pganalyze.com/blog/introducing-vacuum-advisor-postgres&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;pganalyze&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hot path diagnostics with EXPLAIN and stats&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hot path diagnostics with EXPLAIN and stats confirm that the chosen indexes and tactics actually trigger the desired plans. Hot path diagnostics with EXPLAIN and stats should include BUFFERS to see heap vs index I/O, and track plan instability across parameter sets to decide on extended statistics or plan hints.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://wiki.postgresql.org/wiki/Introduction_to_VACUUM,_ANALYZE,_EXPLAIN,_and_COUNT&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;wiki.postgresql&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hot path diagnostics with EXPLAIN and stats ought to be part of CI for critical queries; snapshotting plans prevents unintentional regressions after ORM refactors. Hot path diagnostics with EXPLAIN and stats combine with pg_stat_statements to find the top time sinks and guide indexing priorities.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;elitedev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Archival, partitioning, and BRIN aids&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Archival, partitioning, and BRIN aids keep hot data small and cold data cheap, which simplifies indexing and vacuuming. Archival, partitioning, and BRIN aids use time-based partitions with local indexes so maintenance tasks run faster and queries prune partitions efficiently.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://railsdrop.com/2025/04/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;railsdrop&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Archival, partitioning, and BRIN aids apply BRIN indexes to append-only, time-ordered tables to accelerate range scans with minimal index size. Archival, partitioning, and BRIN aids also reduce autovacuum pressure by isolating churn to hot partitions while leaving cold partitions mostly static.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://railsdrop.com/2025/04/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;railsdrop&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Safe migrations and lock-aware changes&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Safe migrations and lock-aware changes ensure tuning doesn&amp;rsquo;t harm availability while adding composite indexes and covering index strategies or adjusting constraints. Safe migrations and lock-aware changes use CONCURRENTLY for index creation on PostgreSQL and break large DDL into reversible, deploy-step-safe phases.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;guides.rubyonrails&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Safe migrations and lock-aware changes coordinate with traffic windows, throttle backfills, and enable feature flags so application code switches to new indexes only after creation. Safe migrations and lock-aware changes conclude with post-deploy checks&amp;mdash;EXPLAIN to verify usage, and pg_stat_all_indexes to see scans rising on the new index.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;nonstopio&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bringing the tactics together&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database tuning tactics for Active Record are most effective when applied as a system: composite indexes and covering index strategies shape the primary access paths, N+1 elimination with includes/preload/eager_load keeps query counts in check, partial and functional indexes for selective filters narrow I/O, deferred constraints and bulk import pipelines enable safe high‑volume ingest, and a vacuum/analyze cadence with tuned autovacuum thresholds preserves planner accuracy and storage health. With these 11 database tuning tactics for Active Record, teams can sustain predictable performance as datasets and concurrency grow.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://pganalyze.com/blog/introducing-vacuum-advisor-postgres&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;pganalyze&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&quot;&gt;https://elitedev.in/ruby/advanced-rails-database-indexing-strategies-for-hi/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&quot;&gt;https://blog.nonstopio.com/rails-query-optimization-part-2-advanced-indexing-and-query-refactoring-2cbc07e16aa0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/69242991/when-is-better-to-use-preload-or-eager-load-or-includes&quot;&gt;https://stackoverflow.com/questions/69242991/when-is-better-to-use-preload-or-eager-load-or-includes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scoutapm.com/blog/activerecord-includes-vs-joins-vs-preload-vs-eager-load-when-and-where&quot;&gt;https://www.scoutapm.com/blog/activerecord-includes-vs-joins-vs-preload-vs-eager-load-when-and-where&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tencentcloud.com/techpedia/129781&quot;&gt;https://www.tencentcloud.com/techpedia/129781&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bhserna.com/when-is-better-to-use-preload-or-eager-load-vs-includes&quot;&gt;https://bhserna.com/when-is-better-to-use-preload-or-eager-load-vs-includes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mintbit.com/blog/custom-database-indexes-in-rails/&quot;&gt;https://www.mintbit.com/blog/custom-database-indexes-in-rails/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rizqimulki.com/postgresql-autovacuum-tuning-maintaining-performance-in-secure-systems-69e9e2ca6188&quot;&gt;https://rizqimulki.com/postgresql-autovacuum-tuning-maintaining-performance-in-secure-systems-69e9e2ca6188&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_migrations.html&quot;&gt;https://guides.rubyonrails.org/active_record_migrations.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://railsdrop.com/2025/04/&quot;&gt;https://railsdrop.com/2025/04/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pganalyze.com/blog/introducing-vacuum-advisor-postgres&quot;&gt;https://pganalyze.com/blog/introducing-vacuum-advisor-postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://americanopeople.tistory.com/370&quot;&gt;https://americanopeople.tistory.com/370&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/37261697/how-to-efficiently-vacuum-analyze-tables-in-postgres&quot;&gt;https://stackoverflow.com/questions/37261697/how-to-efficiently-vacuum-analyze-tables-in-postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.postgresql.org/wiki/Introduction_to_VACUUM,_ANALYZE,_EXPLAIN,_and_COUNT&quot;&gt;https://wiki.postgresql.org/wiki/Introduction_to_VACUUM,_ANALYZE,_EXPLAIN,_and_COUNT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/rails-query-optimization-part-1-introduction-best-practices-lqsxf&quot;&gt;https://www.linkedin.com/pulse/rails-query-optimization-part-1-introduction-best-practices-lqsxf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_record_querying.html&quot;&gt;https://guides.rubyonrails.org/active_record_querying.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1048909/index-for-multiple-columns-in-activerecord&quot;&gt;https://stackoverflow.com/questions/1048909/index-for-multiple-columns-in-activerecord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyeyul-k.tistory.com/3&quot;&gt;https://hyeyul-k.tistory.com/3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.devops.dev/lessons-learned-avoiding-performance-pitfalls-with-smart-indexing-in-rails-253f02438297&quot;&gt;https://blog.devops.dev/lessons-learned-avoiding-performance-pitfalls-with-smart-indexing-in-rails-253f02438297&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shivrajbadu.com.np/posts/rails-query-optimization-guide/&quot;&gt;https://shivrajbadu.com.np/posts/rails-query-optimization-guide/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/32</guid>
      <comments>https://timeslip2.tistory.com/entry/11-Database-Tuning-Tactics-for-Active-Record#entry32comment</comments>
      <pubDate>Sun, 16 Nov 2025 21:49:24 +0900</pubDate>
    </item>
    <item>
      <title>7 Steps to Optimize WASM-Ruby Interop</title>
      <link>https://timeslip2.tistory.com/entry/7-Steps-to-Optimize-WASM-Ruby-Interop</link>
      <description>&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Optimizing WASM-Ruby interop requires disciplined control over ABI boundaries and minimizing host calls overhead, careful memory sharing models and buffer reuse strategies, selectively compiling hot loops into WASM modules, enforcing deterministic builds and reproducible artifacts, and designing progressive enhancement on browsers without WASM. These seven steps make WASM-Ruby interop fast, predictable, and portable across runtimes.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;1) ABI boundaries and minimizing host calls overhead&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Define narrow ABI boundaries and minimize host calls overhead by batching cross-boundary invocations and using coarse-grained entry points. In ruby.wasm, add imports once, initialize the VM, and expose a small number of exported functions that accept packed arguments to avoid chatty host calls. Prefer passing offsets/lengths into shared linear memory over frequent small calls; every host call has a fixed overhead that grows noticeable in tight loops.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;2) Memory sharing models and buffer reuse strategies&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Adopt memory sharing models and buffer reuse strategies that pass pointers and lengths into the WASM module&amp;rsquo;s linear memory from the host, copying data only once. Reuse preallocated ArrayBuffers and grow WebAssembly.Memory in larger pages to amortize allocations, avoiding transient garbage and memcpy storms. For complex payloads, serialize to a contiguous byte buffer (e.g., MessagePack) and pass a single pointer/size pair to reduce boundary churn.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;3) Compiling hot loops into WASM modules&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Compiling hot loops into WASM modules yields predictable speedups when loops are CPU-bound and data-resident. Identify hotspots with profiling, then isolate numeric kernels or parsing/tokenization loops into a tiny WASM module with a stable ABI. Keep the interface scalar and flat&amp;mdash;i32/i64/f32/f64 plus pointers&amp;mdash;and let Ruby pass buffers once, so hot loops remain in WASM without paying per-iteration host calls overhead.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;4) Deterministic builds and reproducible artifacts&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Strive for deterministic builds and reproducible artifacts by pinning compilers, base images, and dependencies, stripping timestamps, and normalizing file ordering in archives. Produce signed digests for each WASM artifact and prove reproducibility by rebuilding in clean environments. Determinism reduces &amp;ldquo;works on my machine&amp;rdquo; drift and simplifies supply-chain attestation and cache reuse in CI.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;5) Progressive enhancement on browsers without WASM&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Practice progressive enhancement on browsers without WASM by offering a baseline Ruby or JavaScript implementation that matches behavior, then upgrading to WASM when supported. Feature-detect WebAssembly at runtime, lazy-load the module, and keep critical UX functional without WASM to protect accessibility and SEO. Use the same test vectors for both code paths to guarantee equivalent outputs and to simplify fallbacks in case of WASM initialization failures.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;6) Pragmatic WASM-Ruby integration via ruby.wasm&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Leverage ruby.wasm and the RubyVM in WASI environments to run Ruby alongside WASM with a controlled ABI. Initialize the VM once, add required imports, and evaluate Ruby code or call exported functions that operate on buffers. Keep interop layers thin and prefer a small number of high-value calls that process batches to keep ABI boundaries efficient.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;7) Interop testing, profiling, and guardrails&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Build a regression harness that feeds fixtures through both pure Ruby and WASM-accelerated paths to confirm identical outputs. Profile host calls overhead, memory growth, and GC pauses; add budgets for max calls per second and maximum memory pages. If budgets are exceeded, switch to a fallback path or re-chunk inputs to keep WASM-Ruby interop under latency SLOs.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Putting it all together&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;By tightening ABI boundaries and minimizing host calls overhead, choosing efficient memory sharing models and buffer reuse strategies, compiling hot loops into WASM modules, enforcing deterministic builds and reproducible artifacts, and applying progressive enhancement on browsers without WASM, teams can turn WASM-Ruby interop into a reliable performance lever. With ruby.wasm as the bridge and a robust test-and-profile loop, WASM-Ruby interop remains fast, safe, and portable across edge, browser, and server environments.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/ruby-head-wasm-wasi&quot;&gt;https://www.npmjs.com/package/ruby-head-wasm-wasi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ruby/ruby.wasm&quot;&gt;https://github.com/ruby/ruby.wasm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://itnext.io/final-report-webassembly-wasi-support-in-ruby-4aface7d90c9&quot;&gt;https://itnext.io/final-report-webassembly-wasi-support-in-ruby-4aface7d90c9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=38278690&quot;&gt;https://news.ycombinator.com/item?id=38278690&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/AmbientRun/wasm-interop/&quot;&gt;https://github.com/AmbientRun/wasm-interop/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hansschnedlitz.com/writing/2024/03/25/porting-a-ruby-gem-to-the-browser-with-ruby-wasm&quot;&gt;https://hansschnedlitz.com/writing/2024/03/25/porting-a-ruby-gem-to-the-browser-with-ruby-wasm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://radu-matei.com/blog/practical-guide-to-wasm-memory/&quot;&gt;https://radu-matei.com/blog/practical-guide-to-wasm-memory/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/web3/establishing-verifiable-security-reproducible-builds-and-aws-nitro-enclaves/&quot;&gt;https://aws.amazon.com/blogs/web3/establishing-verifiable-security-reproducible-builds-and-aws-nitro-enclaves/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.pixelfreestudio.com/the-role-of-progressive-enhancement-in-cross-browser-compatibility/&quot;&gt;https://blog.pixelfreestudio.com/the-role-of-progressive-enhancement-in-cross-browser-compatibility/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://evilmartians.com/events/assembling-the-future-ruby-on-wasm-puzzle-euruko&quot;&gt;https://evilmartians.com/events/assembling-the-future-ruby-on-wasm-puzzle-euruko&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://evilmartians.com/chronicles/first-steps-with-ruby-wasm-or-building-ruby-next-playground&quot;&gt;https://evilmartians.com/chronicles/first-steps-with-ruby-wasm-or-building-ruby-next-playground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/napi-rs/napi-rs/issues/1502&quot;&gt;https://github.com/napi-rs/napi-rs/issues/1502&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://internals.rust-lang.org/t/pre-rfc-sandboxed-deterministic-reproducible-efficient-wasm-compilation-of-proc-macros/19359&quot;&gt;https://internals.rust-lang.org/t/pre-rfc-sandboxed-deterministic-reproducible-efficient-wasm-compilation-of-proc-macros/19359&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement&quot;&gt;https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/vaib/the-webassembly-component-model-a-new-era-of-interoperability-and-composition-4am5&quot;&gt;https://dev.to/vaib/the-webassembly-component-model-a-new-era-of-interoperability-and-composition-4am5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/60419437/webassembly-compilation-of-loops-with-return-values-not-behaving-as-expected&quot;&gt;https://stackoverflow.com/questions/60419437/webassembly-compilation-of-loops-with-return-values-not-behaving-as-expected&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/60255133/whats-the-correct-way-to-share-memory-between-my-assemblyscript-module-and-my-j&quot;&gt;https://stackoverflow.com/questions/60255133/whats-the-correct-way-to-share-memory-between-my-assemblyscript-module-and-my-j&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forum.dfinity.org/t/lets-discuss-reproducible-builds-and-code-verification-once-again/41918&quot;&gt;https://forum.dfinity.org/t/lets-discuss-reproducible-builds-and-code-verification-once-again/41918&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/javascript/comments/57axlu/progressive_enhancement_isnt_dead_but_it_smells/&quot;&gt;https://www.reddit.com/r/javascript/comments/57axlu/progressive_enhancement_isnt_dead_but_it_smells/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/72723327/how-to-perform-wasm-host-call-from-a-go-guest&quot;&gt;https://stackoverflow.com/questions/72723327/how-to-perform-wasm-host-call-from-a-go-guest&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/36</guid>
      <comments>https://timeslip2.tistory.com/entry/7-Steps-to-Optimize-WASM-Ruby-Interop#entry36comment</comments>
      <pubDate>Sat, 15 Nov 2025 21:53:12 +0900</pubDate>
    </item>
    <item>
      <title>5 Ways to Integrate Ruby with Spark and Hadoop</title>
      <link>https://timeslip2.tistory.com/entry/5-Ways-to-Integrate-Ruby-with-Spark-and-Hadoop</link>
      <description>&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Integrating Ruby with Spark and Hadoop is practical when teams standardize around JRuby shims for Spark DataFrame operations, orchestrate batch ETL with Airflow and Ruby clients, manage Parquet/ORC file handling and schema evolution, enforce fault tolerance with speculative execution and retries, and pursue cost optimization on cloud compute/storage tiers. These five approaches let Ruby applications participate in big data pipelines without giving up developer ergonomics.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Using JRuby shims for Spark DataFrame operations&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Using JRuby shims for Spark DataFrame operations enables Ruby code to invoke the Spark JVM APIs directly, minimizing serialization overhead and exposing Catalyst-optimized transformations. A thin JRuby shim can wrap SparkSession, DataFrame, and SQL functions, so Ruby developers can express joins, window functions, and aggregations while Spark handles distributed execution. For legacy Ruby apps, an HTTP or gRPC sidecar can proxy DataFrame jobs to a Spark driver, but JRuby offers the lowest-latency path to Spark DataFrame operations.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Batch ETL orchestration with Airflow and Ruby clients&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Batch ETL orchestration with Airflow and Ruby clients separates control from execution. Author DAGs in Airflow to schedule extract-transform-load jobs, then call Ruby clients that submit Spark jobs, trigger Hadoop DistCp, or run metadata validations. This pattern keeps the critical path observable while letting Ruby own business rules, and Airflow handles retries, SLAs, and lineage for predictable batch ETL orchestration.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Parquet/ORC file handling and schema evolution&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Parquet/ORC file handling and schema evolution are central to stable data lakes. Parquet embeds schema in file metadata and tolerates adding columns with null backfills, while ORC supports evolution with strict type rules and predicate pushdown. Enforce partitioning, small-file compaction, and schema registries or manifests so Ruby readers and Spark writers agree on schemas, ensuring Parquet/ORC file handling and schema evolution don&amp;rsquo;t break downstream jobs.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Fault tolerance: speculative execution and retries&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Fault tolerance with speculative execution and retries protects long-running Spark jobs from stragglers and transient failures. Enable speculative execution for skewed tasks, tune spark.task.maxFailures and retry backoffs, and checkpoint streaming state for rapid recovery. In Hadoop-based pipelines, coordinate retries with YARN and HDFS semantics to ensure fault tolerance via speculative execution and retries remains deterministic and auditable.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Cost optimization on cloud compute/storage tiers&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Cost optimization on cloud compute/storage tiers demands right-sizing clusters, using spot/preemptible capacity, and tiering object storage between hot and cold classes. Push persistent data to S3, GCS, or ADLS with lifecycle policies, compress with Parquet/ORC, and cache frequently accessed datasets in cluster memory or SSD. Tag jobs with cost attribution and autoscale executors so cost optimization on cloud compute/storage tiers becomes continuous rather than reactive.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Recipe 1: JRuby + Spark DataFrame&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Package a JRuby runtime with Spark submit scripts, expose a Ruby DSL for DataFrame operations, and validate plans with EXPLAIN to confirm predicate pushdown and partition pruning. This keeps Using JRuby shims for Spark DataFrame operations efficient and maintainable.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Recipe 2: Airflow + Ruby ETL clients&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Implement idempotent Ruby clients invoked by Airflow operators, parameterize run dates, and write checkpoints to a metadata store. Batch ETL orchestration with Airflow and Ruby clients benefits from centralized retries and SLA alerts.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Recipe 3: Parquet/ORC and schema evolution&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Adopt a schema evolution policy: add columns with defaults, avoid destructive rewrites, and maintain table manifests. Parquet/ORC file handling and schema evolution remain consistent across Spark writers and Ruby readers.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Recipe 4: Speculative execution and retries&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Enable speculative execution for stages with heavy skew, cap retries to prevent cluster churn, and log per-attempt metrics. Fault tolerance via speculative execution and retries reduces tail latency and job flakiness.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Recipe 5: Cloud cost optimization&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Use autoscaling, spot capacity, and storage tiering; compact small files and prune partitions to cut scan costs. Cost optimization on cloud compute/storage tiers aligns engineering habits with finance guardrails.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Conclusion&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;These five ways to integrate Ruby with Spark and Hadoop&amp;mdash;Using JRuby shims for Spark DataFrame operations, Batch ETL orchestration with Airflow and Ruby clients, Parquet/ORC file handling and schema evolution, Fault tolerance with speculative execution and retries, and Cost optimization on cloud compute/storage tiers&amp;mdash;let Ruby teams deliver scalable data platforms without abandoning familiar tooling. With careful schemas, resilient retries, and thoughtful cost controls, Ruby can be a first-class citizen in Spark and Hadoop ecosystems.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57238006/spark-api-get-post-request-data-using-ruby&quot;&gt;https://stackoverflow.com/questions/57238006/spark-api-get-post-request-data-using-ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ondra-m/ruby-spark&quot;&gt;https://github.com/ondra-m/ruby-spark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://statkclee.github.io/bigdata/spark-hadoop-install.html&quot;&gt;https://statkclee.github.io/bigdata/spark-hadoop-install.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/arbox/data-science-with-ruby&quot;&gt;https://github.com/arbox/data-science-with-ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cdata.com/kb/tech/spark-odbc-ruby.rst&quot;&gt;https://www.cdata.com/kb/tech/spark-odbc-ruby.rst&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://labs.flinters.vn/workflow-orchestration/applying-airflow-to-build-a-simple-etl-workflow/&quot;&gt;https://labs.flinters.vn/workflow-orchestration/applying-airflow-to-build-a-simple-etl-workflow/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/alexmercedcoder/all-about-parquet-part-04-schema-evolution-in-parquet-57l3&quot;&gt;https://dev.to/alexmercedcoder/all-about-parquet-part-04-schema-evolution-in-parquet-57l3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sparkcodehub.com/spark/configurations/task-max-failures&quot;&gt;https://www.sparkcodehub.com/spark/configurations/task-max-failures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arxiv.org/pdf/2305.14818.pdf&quot;&gt;https://arxiv.org/pdf/2305.14818.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://allofdater.tistory.com/87&quot;&gt;https://allofdater.tistory.com/87&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pawl/awesome-etl&quot;&gt;https://github.com/pawl/awesome-etl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/54447893/orc-schema-evolution&quot;&gt;https://stackoverflow.com/questions/54447893/orc-schema-evolution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lists.apache.org/thread/1r7hp7fxzx7fn2k2twb05k836vmsy6oh&quot;&gt;https://lists.apache.org/thread/1r7hp7fxzx7fn2k2twb05k836vmsy6oh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spot.io/resources/cloud-cost/cloud-cost-optimization-15-ways-to-optimize-your-cloud/&quot;&gt;https://spot.io/resources/cloud-cost/cloud-cost-optimization-15-ways-to-optimize-your-cloud/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=34423221&quot;&gt;https://news.ycombinator.com/item?id=34423221&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/dataengineering/comments/pbaw2f/what_etl_tool_do_you_use/&quot;&gt;https://www.reddit.com/r/dataengineering/comments/pbaw2f/what_etl_tool_do_you_use/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/schema-evolution-avro-orc-parquet-detailed-approach-aniket-kulkarni-z7zpf&quot;&gt;https://www.linkedin.com/pulse/schema-evolution-avro-orc-parquet-detailed-approach-aniket-kulkarni-z7zpf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cloudera.com/runtime/7.3.1/developing-spark-applications/topics/spark-streaming-fault-tolerance.html&quot;&gt;https://docs.cloudera.com/runtime/7.3.1/developing-spark-applications/topics/spark-streaming-fault-tolerance.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.github.io/aws-emr-best-practices/docs/bestpractices/Cost%20Optimizations/best_practices/&quot;&gt;https://aws.github.io/aws-emr-best-practices/docs/bestpractices/Cost%20Optimizations/best_practices/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ondra-m.github.io/ruby-spark/&quot;&gt;http://ondra-m.github.io/ruby-spark/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/31</guid>
      <comments>https://timeslip2.tistory.com/entry/5-Ways-to-Integrate-Ruby-with-Spark-and-Hadoop#entry31comment</comments>
      <pubDate>Fri, 14 Nov 2025 21:49:00 +0900</pubDate>
    </item>
    <item>
      <title>7 Strategies for Rails API Scalability on Kubernetes</title>
      <link>https://timeslip2.tistory.com/entry/7-Strategies-for-Rails-API-Scalability-on-Kubernetes</link>
      <description>&lt;div&gt;
&lt;h2 id=&quot;hpa-with-cpumemory-and-custom-latency-metrics&quot; data-ke-size=&quot;size26&quot;&gt;HPA with CPU/memory and custom latency metrics&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Horizontal Pod Autoscaling on Kubernetes should start with CPU and memory, then advance to custom latency metrics to scale a Rails API predictably under spiky workloads. Using autoscaling/v2, define multiple metrics so HPA considers CPU, memory, and SLO‑aligned latency or RPS, avoiding blind spots that hurt tail latency at scale. Expose Rails API latency via Prometheus and configure the Prometheus Adapter so HPA can act on http_request_duration_seconds and RPS alongside CPU/memory for resilient decisions under traffic surges.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/rubixkube/scaling-applications-in-kubernetes-with-horizontal-pod-autoscaling-a-deep-dive-3c57&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Target percentile latency in HPA to protect p99 even when CPU looks fine, combining object/pods metrics with fallback CPU/memory thresholds for safety. This multi‑metric HPA strategy keeps Rails API scalability predictable on Kubernetes during flash crowds.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kubernetes&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Tune scale‑down stabilization windows and cool‑downs to prevent oscillation; HPA stability directly improves user‑visible latency under Kubernetes rollouts for the Rails API.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/abhishek_gautam-01/mastering-kubernetes-scaling-a-comprehensive-guide-for-high-traffic-applications-part-1-577j&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pod-disruption-budgets-and-zero-downtime-deploys&quot; data-ke-size=&quot;size26&quot;&gt;Pod disruption budgets and zero-downtime deploys&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod Disruption Budgets limit concurrent evictions so zero‑downtime deploys preserve capacity while new Rails API pods pass readiness probes. Combine rolling updates, PDBs, and readiness probes to ensure a steady pool of healthy replicas during migrations and image rollouts on Kubernetes. Align surge/availability parameters so old pods drain only after new pods report ready, preventing connection storms on the database during deploys.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/vaibhavhariaramani/how-can-u-ensure-zero-downtime-deployment-in-kubernetes-20m6&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Validate preStop hooks and graceful termination so Rails API workers finish in‑flight requests; this matters for zero‑downtime deploys behind Kubernetes Services and Ingress.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://thoughtbot.com/blog/zero-downtime-rails-deployments-with-kubernetes&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;thoughtbot&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Keep PDB budgets realistic relative to min replicas; too‑strict PDBs can block cluster maintenance and delay Rails API updates on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/run-application/configure-pdb/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kubernetes&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sticky-sessions-versus-stateless-jwt-tokens&quot; data-ke-size=&quot;size26&quot;&gt;Sticky sessions versus stateless JWT tokens&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sticky sessions on Kubernetes Services or Ingress provide session affinity but constrain elasticity; stateless JWT tokens enable horizontal scaling at the cost of immediate revocation complexity. Use sessionAffinity: ClientIP or Ingress annotations for sticky sessions when in‑memory session state is required, keeping requests on the same Rails API pod for consistency. Prefer stateless JWT tokens when aiming for true stateless Rails API scalability on Kubernetes, shifting state to signed tokens or Redis and decoupling traffic from pod stickiness.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/ops/kubernetes-cluster-sticky-session&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;baeldung&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;For sticky sessions, document failure modes where client IP changes break affinity; consider external session stores to reduce coupling and improve Kubernetes rescheduling flexibility for the Rails API.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/ops/kubernetes-cluster-sticky-session&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;baeldung&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;For JWT, plan revocation lists and short TTLs to mitigate token invalidation trade‑offs while preserving stateless throughput on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/node/comments/1aox0au/whats_the_ultimate_resource_for_jwt_vs_session/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;reddit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;connection-pooling-for-db-and-message-brokers&quot; data-ke-size=&quot;size26&quot;&gt;Connection pooling for DB and message brokers&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connection pooling must match Puma threads, Sidekiq concurrency, and process counts so the Rails API avoids exhausting Postgres or Redis under Kubernetes scaling. Calculate database pool size per process: pool equals max threads per Puma worker; add Sidekiq concurrency separately and ensure global connections remain below DB limits. For message brokers and Redis, size client/server pools independently for web and worker pods; consider PgBouncer for transaction pooling as replicas scale.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/40600760/heroku-sidekiq-is-my-understanding-of-how-connection-pooling-works-correct&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;stackoverflow&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;When HPA adds Rails API pods, total DB connections grow linearly; cap WEB_CONCURRENCY and RAILS_MAX_THREADS to stay within Postgres max_connections on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/amree/rails-connection-pool-vs-pgbouncer-2map&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Monitor pool wait time and timeouts; sustained queuing signals pool mis‑sizing relative to HPA behavior for the Rails API.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/amree/rails-connection-pool-vs-pgbouncer-2map&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sidecar-patterns-for-logging-tracing-and-caching&quot; data-ke-size=&quot;size26&quot;&gt;Sidecar patterns for logging, tracing, and caching&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sidecar patterns on Kubernetes bundle logging, tracing, and caching without polluting application code, improving Rails API operability at scale. Attach a Fluent Bit sidecar for log shipping, an Envoy or service‑mesh sidecar for tracing, and a lightweight caching sidecar where edge caching is beneficial inside the Pod. Sidecars share the Pod network and volumes, enabling transparent log collection and request tracing that help troubleshoot the Rails API during Kubernetes incidents.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.plural.sh/blog/kubernetes-sidecar-guide/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;plural&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keep sidecars versioned and configurable independently to iterate on logging and tracing without redeploying the Rails API container on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.plural.sh/blog/kubernetes-sidecar-guide/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;plural&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Validate startup/shutdown ordering so sidecars flush logs and spans during rolling updates on Kubernetes for accurate Rails API observability.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://www.plural.sh/blog/kubernetes-sidecar-guide/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;plural&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;rate-limits-backpressure-and-graceful-degradation&quot; data-ke-size=&quot;size26&quot;&gt;Rate limits, backpressure, and graceful degradation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For scalable Rails API behavior on Kubernetes, enforce per‑pod and global rate limits with backpressure to avoid thundering herds during HPA scale‑up. Integrate Envoy rate limiting or gateway policies so overload degrades gracefully, protecting databases and brokers while HPA and PDBs preserve capacity for core endpoints. Backpressure plus circuit breaking prevents retries from amplifying latency, helping the Rails API keep SLOs on Kubernetes even under incident conditions.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/abhishek_gautam-01/mastering-kubernetes-scaling-a-comprehensive-guide-for-high-traffic-applications-part-1-577j&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Combine request queues with timeouts to bound work per Rails API pod so HPA can react while Kubernetes reschedules capacity.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/abhishek_gautam-01/mastering-kubernetes-scaling-a-comprehensive-guide-for-high-traffic-applications-part-1-577j&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Expose overload signals as custom metrics to inform HPA decisions beyond CPU/memory for better tail‑latency control on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://engineering.workable.com/kubernetes-hpa-optimization-based-on-any-metric-9b3c9a693971&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;engineering.workable&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;observability-slos-and-autoscaling-feedback-loops&quot; data-ke-size=&quot;size26&quot;&gt;Observability SLOs and autoscaling feedback loops&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prometheus metrics, tracing, and structured logs build a feedback loop where SLOs drive HPA targets for the Rails API on Kubernetes. Export p95/p99 latency, error rates, queue depth, and saturation to guide autoscaling thresholds and validate zero‑downtime deploys with PDBs in place. Drive dashboards that correlate HPA replica counts, connection pool usage, and sticky sessions versus JWT choices to explain behavior during traffic spikes for the Rails API on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://dev.to/rubixkube/scaling-applications-in-kubernetes-with-horizontal-pod-autoscaling-a-deep-dive-3c57&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;&lt;span&gt;+2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Alert on HPA thrash, pending pods, and DB pool exhaustion to preempt cascading failures as Kubernetes scales the Rails API.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;kubernetes&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;li&gt;Review postmortems to refine SLOs and custom metrics so the autoscaler tracks what users feel, not just CPU and memory, sustaining Rails API scalability on Kubernetes.&lt;span data-state=&quot;closed&quot;&gt;&lt;a href=&quot;https://engineering.workable.com/kubernetes-hpa-optimization-based-on-any-metric-9b3c9a693971&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;engineering.workable&lt;/span&gt;&lt;span&gt;&lt;span&gt;+1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;​&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/rubixkube/scaling-applications-in-kubernetes-with-horizontal-pod-autoscaling-a-deep-dive-3c57&quot;&gt;https://dev.to/rubixkube/scaling-applications-in-kubernetes-with-horizontal-pod-autoscaling-a-deep-dive-3c57&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/&quot;&gt;https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.scatterlab.co.kr/kubernetes-hpa-custom-metric&quot;&gt;https://blog.scatterlab.co.kr/kubernetes-hpa-custom-metric&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/abhishek_gautam-01/mastering-kubernetes-scaling-a-comprehensive-guide-for-high-traffic-applications-part-1-577j&quot;&gt;https://dev.to/abhishek_gautam-01/mastering-kubernetes-scaling-a-comprehensive-guide-for-high-traffic-applications-part-1-577j&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.workable.com/kubernetes-hpa-optimization-based-on-any-metric-9b3c9a693971&quot;&gt;https://engineering.workable.com/kubernetes-hpa-optimization-based-on-any-metric-9b3c9a693971&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.okd.io/latest/nodes/pods/nodes-pods-autoscaling.html&quot;&gt;https://docs.okd.io/latest/nodes/pods/nodes-pods-autoscaling.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/vaibhavhariaramani/how-can-u-ensure-zero-downtime-deployment-in-kubernetes-20m6&quot;&gt;https://dev.to/vaibhavhariaramani/how-can-u-ensure-zero-downtime-deployment-in-kubernetes-20m6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thoughtbot.com/blog/zero-downtime-rails-deployments-with-kubernetes&quot;&gt;https://thoughtbot.com/blog/zero-downtime-rails-deployments-with-kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/run-application/configure-pdb/&quot;&gt;https://kubernetes.io/docs/tasks/run-application/configure-pdb/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/ops/kubernetes-cluster-sticky-session&quot;&gt;https://www.baeldung.com/ops/kubernetes-cluster-sticky-session&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.voidmainvoid.net/120&quot;&gt;https://blog.voidmainvoid.net/120&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/node/comments/1aox0au/whats_the_ultimate_resource_for_jwt_vs_session/&quot;&gt;https://www.reddit.com/r/node/comments/1aox0au/whats_the_ultimate_resource_for_jwt_vs_session/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/40600760/heroku-sidekiq-is-my-understanding-of-how-connection-pooling-works-correct&quot;&gt;https://stackoverflow.com/questions/40600760/heroku-sidekiq-is-my-understanding-of-how-connection-pooling-works-correct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/amree/rails-connection-pool-vs-pgbouncer-2map&quot;&gt;https://dev.to/amree/rails-connection-pool-vs-pgbouncer-2map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mperham/sidekiq/issues/5778&quot;&gt;https://github.com/mperham/sidekiq/issues/5778&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.plural.sh/blog/kubernetes-sidecar-guide/&quot;&gt;https://www.plural.sh/blog/kubernetes-sidecar-guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spacelift.io/blog/kubernetes-sidecar-container&quot;&gt;https://spacelift.io/blog/kubernetes-sidecar-container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://treatwell.engineering/automatically-scale-your-rails-application-with-hpa-25506ef04a19&quot;&gt;https://treatwell.engineering/automatically-scale-your-rails-application-with-hpa-25506ef04a19&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.naver.com/ghdalswl77/222391621683&quot;&gt;https://blog.naver.com/ghdalswl77/222391621683&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gain-yoo.github.io/kubernetes/19/&quot;&gt;https://gain-yoo.github.io/kubernetes/19/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>programming-for-us</author>
      <guid isPermaLink="true">https://timeslip2.tistory.com/30</guid>
      <comments>https://timeslip2.tistory.com/entry/7-Strategies-for-Rails-API-Scalability-on-Kubernetes#entry30comment</comments>
      <pubDate>Thu, 13 Nov 2025 21:48:26 +0900</pubDate>
    </item>
  </channel>
</rss>