# Migrating Counter Service storage: Design choices and learnings

DevFeed: [Migrating Counter Service storage: Design choices and learnings](<https://devfeed.tech/articles/migrating-counter-service-storage-design-choices-and-learnings-1245.md>)

Original publisher: [Read original article](<https://engineering.grab.com/counter-service-storage-migration>)

Author: Jia Long Loh

Published: 2026-07-03T00:00:00Z

Content type: article

Language: en

Sources: [Grab Tech](<https://devfeed.tech/sources/grab-tech.md>)

Topics: [Database](<https://devfeed.tech/topics/database.md>), [migration](<https://devfeed.tech/topics/migration.md>), [Latency](<https://devfeed.tech/topics/latency.md>), [real-time](<https://devfeed.tech/topics/real-time.md>), [backends](<https://devfeed.tech/topics/backends.md>)

Tags: [artificial-intelligence](<https://devfeed.tech/tags/artificial-intelligence.md>), [backend](<https://devfeed.tech/tags/backend.md>), [database](<https://devfeed.tech/tags/database.md>), [devsecops](<https://devfeed.tech/tags/devsecops.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [evaluation](<https://devfeed.tech/tags/evaluation.md>), [infrastructure](<https://devfeed.tech/tags/infrastructure.md>), [kubernetes](<https://devfeed.tech/tags/kubernetes.md>), [latency](<https://devfeed.tech/tags/latency.md>), [migration](<https://devfeed.tech/tags/migration.md>), [platform](<https://devfeed.tech/tags/platform.md>), [real-time](<https://devfeed.tech/tags/real-time.md>), [security](<https://devfeed.tech/tags/security.md>), [storage](<https://devfeed.tech/tags/storage.md>)

## AI overview

Grab's Counter Service migrated its storage backend from a wide-column database to Aerospike to support a high-volume, low-latency fraud-detection workload. The article describes separating storage from business logic, revisiting data modeling and access patterns, and designing reader- and writer-side changes for a gradual, observable rollout.

## Source excerpt

Introduction Counter Service is used across Grab's anti-fraud platform to answer time-windowed count questions, such as recent ride requests by a user or failed payment attempts on a card. The service handles tens of thousands of queries per second (QPS) with about a billion requests per day, while maintaining strict requirements around latency and reliability to support real-time fraud rule evaluation. For most of its life, Counter Service was backed by a wide-column database that served the workload reliably as the service scaled. As part of a broader infrastructure review mandated at an organizational level, our database team evaluated alternatives to this storage that many services relied on, including Counter Service. Based on their assessment, Aerospike emerged as a good fit for our use-case. We also used the migration as an opportunity to decouple storage concerns from business logic, a necessary first step for this migration, and one that would reduce the effort required for future storage changes. As part of the same effort, we revisited the data model and access patterns in detail, which helped us identify and apply several straightforward optimizations. This post walks through how we did it. What we built on the reader-side to make the migration safe, how we redesigned the writer-side data model around the new backend, and what we ran into during the gradual rollout. Setting the stage Counter data is stored in three time granularities: 15-minute, hourly, and daily buckets. A typical read would be along the lines of, "give me the count for key X over the last 90 minutes", which the service decomposes into the smallest possible set of buckets, one hourly in the middle, a few 15-minute buckets at the edges, fetches them, and sums. In the original setup, each granularity was stored in a separate table with a composite primary key: TABLE daily_count ( key TEXT, -- partition key day_ts TIMESTAMP, -- clustering key count BIGINT, PRIMARY KEY (key, day_ts) ); The