{"id":133785,"date":"2024-11-13T14:40:07","date_gmt":"2024-11-13T14:40:07","guid":{"rendered":"https:\/\/showbizztoday.com\/index.php\/2024\/11\/13\/netflixs-distributed-counter-abstraction-by-netflix-technology-blog-nov-2024\/"},"modified":"2024-11-13T14:40:08","modified_gmt":"2024-11-13T14:40:08","slug":"netflixs-distributed-counter-abstraction-by-netflix-technology-blog-nov-2024","status":"publish","type":"post","link":"https:\/\/showbizztoday.com\/index.php\/2024\/11\/13\/netflixs-distributed-counter-abstraction-by-netflix-technology-blog-nov-2024\/","title":{"rendered":"Netflix\u2019s Distributed Counter Abstraction | by Netflix Technology Blog | Nov, 2024"},"content":{"rendered":"<p> [ad_1]<br \/>\n<\/p>\n<div>\n<div>\n<div>\n<div class=\"speechify-ignore ab cp\">\n<div class=\"speechify-ignore bh l\">\n<div class=\"hv hw hx hy hz ab\">\n<div>\n<div class=\"ab ia\">\n<div>\n<div class=\"bm\" aria-hidden=\"false\"><a href=\"https:\/\/netflixtechblog.medium.com\/?source=post_page---byline--8d0c45eb66b2--------------------------------\" rel=\"noopener follow\" target=\"_blank\"><\/p>\n<div class=\"l ib ic by id ie\">\n<div class=\"l fj\"><img decoding=\"async\" alt=\"Netflix Technology Blog\" class=\"l fd by dd de cx\" src=\"https:\/\/miro.medium.com\/v2\/resize:fill:88:88\/1*BJWRqfSMf9Da9vsXG9EBRQ.jpeg\" width=\"44\" height=\"44\" loading=\"lazy\" data-testid=\"authorPhoto\"\/><\/div>\n<\/div>\n<p><\/a><\/div>\n<\/div>\n<div class=\"ih ab fj\">\n<div>\n<div class=\"bm\" aria-hidden=\"false\"><a href=\"https:\/\/netflixtechblog.com\/?source=post_page---byline--8d0c45eb66b2--------------------------------\" rel=\"noopener  ugc nofollow\" target=\"_blank\"><\/p>\n<div class=\"l ii ij by id ik\">\n<div class=\"l fj\"><img decoding=\"async\" alt=\"Netflix TechBlog\" class=\"l fd by br il cx\" src=\"https:\/\/miro.medium.com\/v2\/resize:fill:48:48\/1*ty4NvNrGg4ReETxqU2N3Og.png\" width=\"24\" height=\"24\" loading=\"lazy\" data-testid=\"publicationPhoto\"\/><\/div>\n<\/div>\n<p><\/a><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"bn bh l\">\n<div class=\"l ix\"><span class=\"bf b bg z du\"><\/p>\n<div class=\"ab cn iy iz ja\"><span class=\"bf b bg z du\"><\/p>\n<div class=\"ab ae\"><span data-testid=\"storyReadTime\">18 min learn<\/span><\/p>\n<p><span class=\"l\" aria-hidden=\"true\"><span class=\"bf b bg z du\">\u00b7<\/span><\/span><\/p>\n<p>18 hours in the past<\/p><\/div>\n<p><\/span><\/div>\n<p><\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p id=\"f7ea\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">By: <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/rajiv-shringi\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Rajiv Shringi<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/oleksii-tkachuk-98b47375\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Oleksii Tkachuk<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/kartik894\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Kartik Sathyanarayanan<\/a><\/p>\n<p id=\"41fb\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">In our earlier weblog publish, we launched <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/introducing-netflix-timeseries-data-abstraction-layer-31552f6326f8\">Netflix\u2019s TimeSequence Abstraction<\/a>, a distributed service designed to retailer and question giant volumes of temporal occasion knowledge with low millisecond latencies. Today, we\u2019re excited to current the <strong class=\"my gv\">Distributed Counter Abstraction<\/strong>. This counting service, constructed on prime of the TimeSequence Abstraction, permits distributed counting at scale whereas sustaining related low latency efficiency. As with all our abstractions, we use our <a class=\"af nu\" href=\"https:\/\/netflixtechblog.medium.com\/data-gateway-a-platform-for-growing-and-protecting-the-data-tier-f1ed8db8f5c6\" rel=\"noopener\" target=\"_blank\">Data Gateway Control Plane<\/a> to shard, configure, and deploy this service globally.<\/p>\n<p id=\"aebc\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Distributed counting is a difficult downside in pc science. In this weblog publish, we\u2019ll discover the various counting necessities at Netflix, the challenges of reaching correct counts in close to real-time, and the rationale behind our chosen strategy, together with the required trade-offs.<\/p>\n<p id=\"fb3c\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Note<\/strong>: <em class=\"oy\">When it involves distributed counters, phrases similar to \u2018accurate\u2019 or \u2018precise\u2019 ought to be taken with a grain of salt. In this context, they seek advice from a depend very near correct, offered with minimal delays.<\/em><\/p>\n<p id=\"1e4f\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">At Netflix, our counting use circumstances embrace monitoring thousands and thousands of person interactions, monitoring how usually particular options or experiences are proven to customers, and counting a number of sides of information throughout <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/its-all-a-bout-testing-the-netflix-experimentation-platform-4e1ca458c15\">A\/B take a look at experiments<\/a>, amongst others.<\/p>\n<p id=\"e35a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">At Netflix, these use circumstances could be categorised into two broad classes:<\/p>\n<ol class=\"\">\n<li id=\"fc33\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt oz pa pb bk\"><strong class=\"my gv\">Best-Effort<\/strong>: For this class, the depend doesn\u2019t should be very correct or sturdy. However, this class requires near-immediate entry to the present depend at low latencies, all whereas maintaining infrastructure prices to a minimal.<\/li>\n<li id=\"d9a3\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt oz pa pb bk\"><strong class=\"my gv\">Eventually Consistent<\/strong>: This class wants correct and sturdy counts, and is prepared to tolerate a slight delay in accuracy and a barely greater infrastructure price as a trade-off.<\/li>\n<\/ol>\n<p id=\"7d8e\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Both classes share widespread necessities, similar to excessive throughput and excessive availability. The desk beneath gives an in depth overview of the various necessities throughout these two classes.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi pj\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/1*_Mx2WRBWOfASpK_e2xgoVw.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/1*_Mx2WRBWOfASpK_e2xgoVw.png 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/1*_Mx2WRBWOfASpK_e2xgoVw.png 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/1*_Mx2WRBWOfASpK_e2xgoVw.png 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/1*_Mx2WRBWOfASpK_e2xgoVw.png 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/1*_Mx2WRBWOfASpK_e2xgoVw.png 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/1*_Mx2WRBWOfASpK_e2xgoVw.png 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/1*_Mx2WRBWOfASpK_e2xgoVw.png 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"494\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"16d7\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">To meet the outlined necessities, the Counter Abstraction was designed to be extremely configurable. It permits customers to decide on between completely different counting modes, similar to <strong class=\"my gv\">Best-Effort<\/strong> or <strong class=\"my gv\">Eventually Consistent<\/strong>, whereas contemplating the documented trade-offs of every possibility. After deciding on a mode, customers can work together with APIs while not having to fret concerning the underlying storage mechanisms and counting strategies.<\/p>\n<p id=\"0799\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Let\u2019s take a better take a look at the construction and performance of the API.<\/p>\n<p id=\"0433\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">Counters are organized into separate namespaces that customers arrange for every of their particular use circumstances. Each namespace could be configured with completely different parameters, similar to Type of Counter, Time-To-Live (TTL), and Counter Cardinality, utilizing the service\u2019s Control Plane.<\/p>\n<p id=\"cc02\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">The Counter Abstraction API resembles Java\u2019s <a class=\"af nu\" href=\"https:\/\/docs.oracle.com\/en\/java\/javase\/22\/docs\/api\/java.base\/java\/util\/concurrent\/atomic\/AtomicInteger.html\" rel=\"noopener ugc nofollow\" target=\"_blank\">AtomicInteger<\/a> interface:<\/p>\n<p id=\"d3f4\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">AddCount\/AddAndGetCount<\/strong>: Adjusts the depend for the required counter by the given delta worth inside a dataset. The delta worth could be constructive or damaging. The <em class=\"oy\">AddAndGetCount<\/em> counterpart additionally returns the depend after performing the add operation.<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"537a\" class=\"pz nw gu pw b bg qa qb l qc qd\">{<br\/>\"namespace\": \"my_dataset\",<br\/>\"counter_name\": \"counter123\",<br\/>\"delta\": 2,<br\/>\"idempotency_token\": { <br\/>\"token\": \"some_event_id\",<br\/>\"generation_time\": \"2024-10-05T14:48:00Z\"<br\/>}<br\/>}<\/span><\/pre>\n<p id=\"2e22\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">The idempotency token can be utilized for counter varieties that assist them. Clients can use this token to securely retry or <a class=\"af nu\" href=\"https:\/\/research.google\/pubs\/the-tail-at-scale\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">hedge<\/a> their requests. Failures in a distributed system are a given, and being able to securely retry requests enhances the reliability of the service.<\/p>\n<p id=\"b098\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">GetCount<\/strong>: Retrieves the depend worth of the required counter inside a dataset.<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"690a\" class=\"pz nw gu pw b bg qa qb l qc qd\">{<br\/>\"namespace\": \"my_dataset\",<br\/>\"counter_name\": \"counter123\"<br\/>}<\/span><\/pre>\n<p id=\"a50d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">ClearCount<\/strong>: Effectively resets the depend to 0 for the required counter inside a dataset.<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"f7fa\" class=\"pz nw gu pw b bg qa qb l qc qd\">{<br\/>\"namespace\": \"my_dataset\",<br\/>\"counter_name\": \"counter456\",<br\/>\"idempotency_token\": {...}<br\/>}<\/span><\/pre>\n<p id=\"560d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Now, let\u2019s take a look at the various kinds of counters supported throughout the Abstraction.<\/p>\n<p id=\"0ea6\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">The service primarily helps two varieties of counters: <strong class=\"my gv\">Best-Effort<\/strong> and <strong class=\"my gv\">Eventually Consistent<\/strong>, together with a 3rd experimental sort: <strong class=\"my gv\">Accurate<\/strong>. In the next sections, we\u2019ll describe the completely different approaches for these kinds of counters and the trade-offs related to every.<\/p>\n<p id=\"1497\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">This sort of counter is powered by <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/announcing-evcache-distributed-in-memory-datastore-for-cloud-c26a698c27f7\">EVCache<\/a>, Netflix\u2019s distributed caching answer constructed on the broadly fashionable <a class=\"af nu\" href=\"https:\/\/memcached.org\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Memcached<\/a>. It is appropriate to be used circumstances like A\/B experiments, the place many concurrent experiments are run for comparatively brief durations and an approximate depend is ample. Setting apart the complexities of provisioning, useful resource allocation, and management airplane administration, the core of this answer is remarkably simple:<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"5b19\" class=\"pz nw gu pw b bg qa qb l qc qd\">\/\/ counter cache key<br\/>counterCacheKey = &lt;namespace&gt;:&lt;counter_name&gt;<p>\/\/ add operation<br\/>return delta &gt; 0<br\/>? cache.incr(counterCacheKey, delta, TTL)<br\/>: cache.decr(counterCacheKey, Math.abs(delta), TTL);<\/p><p>\/\/ get operation<br\/>cache.get(counterCacheKey);<\/p><p>\/\/ clear counts from all replicas<br\/>cache.delete(counterCacheKey, ReplicaPolicy.ALL);<\/p><\/span><\/pre>\n<p id=\"70af\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">EVCache delivers extraordinarily excessive throughput at low millisecond latency or higher inside a single area, enabling a multi-tenant setup inside a shared cluster, saving infrastructure prices. However, there are some trade-offs: it lacks cross-region replication for the <em class=\"oy\">increment<\/em> operation and doesn&#8217;t present <a class=\"af nu\" href=\"https:\/\/netflix.github.io\/EVCache\/features\/#consistency\" rel=\"noopener ugc nofollow\" target=\"_blank\">consistency ensures<\/a>, which can be vital for an correct depend. Additionally, idempotency will not be natively supported, making it unsafe to retry or hedge requests.<\/p>\n<p id=\"3c43\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">While some customers could settle for the restrictions of a Best-Effort counter, others go for exact counts, sturdiness and world availability. In the next sections, we\u2019ll discover numerous methods for reaching sturdy and correct counts. Our goal is to focus on the challenges inherent in world distributed counting and clarify the reasoning behind our chosen strategy.<\/p>\n<p id=\"e5ff\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Approach 1: Storing a Single Row per Counter<\/strong><\/p>\n<p id=\"f787\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Let\u2019s begin easy by utilizing a single row per counter key inside a desk in a globally replicated datastore.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qe\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*X6k4-4N36IQ5yEPe 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*X6k4-4N36IQ5yEPe 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*X6k4-4N36IQ5yEPe 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*X6k4-4N36IQ5yEPe 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*X6k4-4N36IQ5yEPe 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*X6k4-4N36IQ5yEPe 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*X6k4-4N36IQ5yEPe 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*X6k4-4N36IQ5yEPe 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*X6k4-4N36IQ5yEPe 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*X6k4-4N36IQ5yEPe 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*X6k4-4N36IQ5yEPe 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*X6k4-4N36IQ5yEPe 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*X6k4-4N36IQ5yEPe 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*X6k4-4N36IQ5yEPe 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"578\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"ca59\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Let\u2019s study a number of the drawbacks of this strategy:<\/p>\n<ul class=\"\">\n<li id=\"61e8\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Lack of Idempotency<\/strong>: There is not any idempotency key baked into the storage data-model stopping customers from safely retrying requests. Implementing idempotency would doubtless require utilizing an exterior system for such keys, which may additional degrade efficiency or trigger race circumstances.<\/li>\n<li id=\"2b44\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Heavy Contention<\/strong>: To replace counts reliably, each author should carry out a Compare-And-Swap operation for a given counter utilizing locks or transactions. Depending on the throughput and concurrency of operations, this will result in vital rivalry, closely impacting efficiency.<\/li>\n<\/ul>\n<p id=\"a373\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Secondary Keys<\/strong>: One technique to scale back rivalry on this strategy can be to make use of a secondary key, similar to a <em class=\"oy\">bucket_id<\/em>, which permits for distributing writes by splitting a given counter into <em class=\"oy\">buckets<\/em>, whereas enabling reads to mixture throughout buckets. The problem lies in figuring out the suitable variety of buckets. A static quantity should still result in rivalry with <em class=\"oy\">sizzling keys<\/em>, whereas dynamically assigning the variety of buckets per counter throughout thousands and thousands of counters presents a extra advanced downside.<\/p>\n<p id=\"121f\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Let\u2019s see if we will iterate on our answer to beat these drawbacks.<\/p>\n<p id=\"875d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Approach 2: Per Instance Aggregation<\/strong><\/p>\n<p id=\"ca5b\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">To handle problems with sizzling keys and rivalry from writing to the identical row in real-time, we might implement a method the place every occasion aggregates the counts in reminiscence after which flushes them to disk at common intervals. Introducing ample jitter to the flush course of can additional scale back rivalry.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*6iUKbxJ093jJTiYL 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*6iUKbxJ093jJTiYL 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*6iUKbxJ093jJTiYL 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*6iUKbxJ093jJTiYL 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*6iUKbxJ093jJTiYL 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*6iUKbxJ093jJTiYL 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*6iUKbxJ093jJTiYL 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*6iUKbxJ093jJTiYL 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*6iUKbxJ093jJTiYL 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*6iUKbxJ093jJTiYL 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*6iUKbxJ093jJTiYL 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*6iUKbxJ093jJTiYL 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*6iUKbxJ093jJTiYL 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*6iUKbxJ093jJTiYL 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"336\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"24b1\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">However, this answer presents a brand new set of points:<\/p>\n<ul class=\"\">\n<li id=\"dba3\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Vulnerability to Data Loss<\/strong>: The answer is susceptible to knowledge loss for all in-memory knowledge throughout occasion failures, restarts, or deployments.<\/li>\n<li id=\"c41b\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Inability to Reliably Reset Counts<\/strong>: Due to counting requests being distributed throughout a number of machines, it&#8217;s difficult to ascertain consensus on the precise cut-off date when a counter reset occurred.<\/li>\n<li id=\"2535\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Lack of Idempotency: <\/strong>Similar to the earlier strategy, this methodology doesn&#8217;t natively assure idempotency. One technique to obtain idempotency is by constantly routing the identical set of counters to the identical occasion. However, this strategy could introduce further complexities, similar to chief election, and potential challenges with availability and latency within the write path.<\/li>\n<\/ul>\n<p id=\"038c\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">That stated, this strategy should still be appropriate in situations the place these trade-offs are acceptable. However, let\u2019s see if we will handle a few of these points with a distinct event-based strategy.<\/p>\n<p id=\"f599\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Approach 3: Using Durable Queues<\/strong><\/p>\n<p id=\"7eb6\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">In this strategy, we log counter occasions right into a sturdy queuing system like <a class=\"af nu\" href=\"https:\/\/kafka.apache.org\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Apache Kafka<\/a> to stop any potential knowledge loss. By creating a number of subject partitions and hashing the counter key to a selected partition, we make sure that the identical set of counters are processed by the identical set of shoppers. This setup simplifies facilitating idempotency checks and resetting counts. Furthermore, by leveraging further stream processing frameworks similar to <a class=\"af nu\" href=\"https:\/\/kafka.apache.org\/documentation\/streams\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Kafka Streams<\/a> or <a class=\"af nu\" href=\"https:\/\/flink.apache.org\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Apache Flink<\/a>, we will implement windowed aggregations.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*mQikuGyuzZ_lT7Y4 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*mQikuGyuzZ_lT7Y4 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*mQikuGyuzZ_lT7Y4 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*mQikuGyuzZ_lT7Y4 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*mQikuGyuzZ_lT7Y4 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*mQikuGyuzZ_lT7Y4 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*mQikuGyuzZ_lT7Y4 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*mQikuGyuzZ_lT7Y4 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*mQikuGyuzZ_lT7Y4 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*mQikuGyuzZ_lT7Y4 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*mQikuGyuzZ_lT7Y4 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*mQikuGyuzZ_lT7Y4 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*mQikuGyuzZ_lT7Y4 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*mQikuGyuzZ_lT7Y4 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"437\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"25bf\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">However, this strategy comes with some challenges:<\/p>\n<ul class=\"\">\n<li id=\"708e\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Potential Delays<\/strong>: Having the identical shopper course of all of the counts from a given partition can result in backups and delays, leading to stale counts.<\/li>\n<li id=\"f448\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Rebalancing Partitions<\/strong>: This strategy requires auto-scaling and rebalancing of subject partitions because the cardinality of counters and throughput will increase.<\/li>\n<\/ul>\n<p id=\"5f3d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Furthermore, all approaches that pre-aggregate counts make it difficult to assist two of our necessities for correct counters:<\/p>\n<ul class=\"\">\n<li id=\"5818\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Auditing of Counts<\/strong>: Auditing entails extracting knowledge to an offline system for evaluation to make sure that increments had been utilized accurately to succeed in the ultimate worth. This course of may also be used to trace the provenance of increments. However, auditing turns into infeasible when counts are aggregated with out storing the person increments.<\/li>\n<li id=\"51be\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Potential Recounting<\/strong>: Similar to auditing, if changes to increments are vital and recounting of occasions inside a time window is required, pre-aggregating counts makes this infeasible.<\/li>\n<\/ul>\n<p id=\"ccda\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Barring these few necessities, this strategy can nonetheless be efficient if we decide the correct technique to scale our queue partitions and shoppers whereas sustaining idempotency. However, let\u2019s discover how we will alter this strategy to satisfy the auditing and recounting necessities.<\/p>\n<p id=\"83bd\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Approach 4: Event Log of Individual Increments<\/strong><\/p>\n<p id=\"57ab\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">In this strategy, we log every particular person counter increment together with its <strong class=\"my gv\">event_time<\/strong> and <strong class=\"my gv\">event_id<\/strong>. The event_id can embrace the supply info of the place the increment originated. The mixture of event_time and event_id may function the idempotency key for the write.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qh\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*0wKFK7xyTHnEKIhO 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*0wKFK7xyTHnEKIhO 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*0wKFK7xyTHnEKIhO 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*0wKFK7xyTHnEKIhO 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*0wKFK7xyTHnEKIhO 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*0wKFK7xyTHnEKIhO 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*0wKFK7xyTHnEKIhO 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*0wKFK7xyTHnEKIhO 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*0wKFK7xyTHnEKIhO 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*0wKFK7xyTHnEKIhO 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*0wKFK7xyTHnEKIhO 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*0wKFK7xyTHnEKIhO 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*0wKFK7xyTHnEKIhO 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*0wKFK7xyTHnEKIhO 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"532\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"e421\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">However, <em class=\"oy\">in its easiest kind<\/em>, this strategy has a number of drawbacks:<\/p>\n<ul class=\"\">\n<li id=\"1932\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Read Latency<\/strong>: Each learn request requires scanning all increments for a given counter doubtlessly degrading efficiency.<\/li>\n<li id=\"5891\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Duplicate Work<\/strong>: Multiple threads would possibly duplicate the trouble of aggregating the identical set of counters throughout learn operations, resulting in wasted effort and subpar useful resource utilization.<\/li>\n<li id=\"973d\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Wide Partitions<\/strong>: If utilizing a datastore like <a class=\"af nu\" href=\"https:\/\/cassandra.apache.org\/_\/index.html\" rel=\"noopener ugc nofollow\" target=\"_blank\">Apache Cassandra<\/a>, storing many increments for a similar counter might result in a <a class=\"af nu\" href=\"https:\/\/thelastpickle.com\/blog\/2019\/01\/11\/wide-partitions-cassandra-3-11.html\" rel=\"noopener ugc nofollow\" target=\"_blank\">broad partition<\/a>, affecting learn efficiency.<\/li>\n<li id=\"21ef\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Large Data Footprint<\/strong>: Storing every increment individually might additionally lead to a considerable knowledge footprint over time. Without an environment friendly knowledge retention technique, this strategy could wrestle to scale successfully.<\/li>\n<\/ul>\n<p id=\"e879\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">The mixed influence of those points can result in elevated infrastructure prices that could be troublesome to justify. However, adopting an event-driven strategy appears to be a big step ahead in addressing a number of the challenges we\u2019ve encountered and assembly our necessities.<\/p>\n<p id=\"04e4\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">How can we enhance this answer additional?<\/p>\n<p id=\"0918\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">We use a mixture of the earlier approaches, the place we log every counting exercise as an occasion, and repeatedly mixture these occasions within the background utilizing queues and a sliding time window. Additionally, we make use of a bucketing technique to stop broad partitions. In the next sections, we\u2019ll discover how this strategy addresses the beforehand talked about drawbacks and meets all our necessities.<\/p>\n<p id=\"ff08\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Note<\/strong>: <em class=\"oy\">From right here on, we&#8217;ll use the phrases \u201c<\/em><strong class=\"my gv\"><em class=\"oy\">rollup<\/em><\/strong><em class=\"oy\">\u201d and \u201c<\/em><strong class=\"my gv\"><em class=\"oy\">aggregate<\/em><\/strong><em class=\"oy\">\u201d interchangeably. They primarily imply the identical factor, i.e., amassing particular person counter increments\/decrements and arriving on the last worth.<\/em><\/p>\n<p id=\"68cd\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">TimeSequence Event Store:<\/strong><\/p>\n<p id=\"aa41\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">We selected the <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/introducing-netflix-timeseries-data-abstraction-layer-31552f6326f8\">TimeSequence Data Abstraction<\/a> as our occasion retailer, the place counter mutations are ingested as occasion data. Some of the advantages of storing occasions in TimeSequence embrace:<\/p>\n<p id=\"11da\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">High-Performance<\/strong>: The TimeSequence abstraction already addresses lots of our necessities, together with excessive availability and throughput, dependable and quick efficiency, and extra.<\/p>\n<p id=\"b03a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Reducing Code Complexity<\/strong>: We scale back a number of code complexity in Counter Abstraction by delegating a significant portion of the performance to an present service.<\/p>\n<p id=\"6c3a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">TimeSequence Abstraction makes use of Cassandra because the underlying occasion retailer, however it may be configured to work with any persistent retailer. Here is what it appears like:<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*ge4X7ywSmtizcNE5 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*ge4X7ywSmtizcNE5 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*ge4X7ywSmtizcNE5 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*ge4X7ywSmtizcNE5 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*ge4X7ywSmtizcNE5 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*ge4X7ywSmtizcNE5 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*ge4X7ywSmtizcNE5 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*ge4X7ywSmtizcNE5 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*ge4X7ywSmtizcNE5 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*ge4X7ywSmtizcNE5 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*ge4X7ywSmtizcNE5 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*ge4X7ywSmtizcNE5 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*ge4X7ywSmtizcNE5 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*ge4X7ywSmtizcNE5 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"334\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"2b96\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Handling Wide Partitions<\/strong>: The <em class=\"oy\">time_bucket<\/em> and <em class=\"oy\">event_bucket<\/em> columns play a vital position in breaking apart a large partition, stopping high-throughput counter occasions from overwhelming a given partition. <em class=\"oy\">For extra info concerning this, seek advice from our earlier <\/em><a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/introducing-netflix-timeseries-data-abstraction-layer-31552f6326f8\"><em class=\"oy\">weblog<\/em><\/a>.<\/p>\n<p id=\"3dc8\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">No Over-Counting<\/strong>: The <em class=\"oy\">event_time<\/em>, <em class=\"oy\">event_id<\/em> and <em class=\"oy\">event_item_key<\/em> columns kind the idempotency key for the occasions for a given counter, enabling purchasers to retry safely with out the chance of over-counting.<\/p>\n<p id=\"43a9\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Event Ordering<\/strong>: TimeSequence orders all occasions in descending order of time permitting us to leverage this property for occasions like depend resets.<\/p>\n<p id=\"278b\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Event Retention<\/strong>: The TimeSequence Abstraction contains retention insurance policies to make sure that occasions usually are not saved indefinitely, saving disk area and lowering infrastructure prices. Once occasions have been aggregated and moved to a cheaper retailer for audits, there\u2019s no have to retain them within the major storage.<\/p>\n<p id=\"f647\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Now, let\u2019s see how these occasions are aggregated for a given counter.<\/p>\n<p id=\"5a6c\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Aggregating Count Events:<\/strong><\/p>\n<p id=\"80b9\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">As talked about earlier, amassing all particular person increments for each learn request can be cost-prohibitive by way of learn efficiency. Therefore, a background aggregation course of is critical to repeatedly converge counts and guarantee optimum learn efficiency.<\/p>\n<p id=\"2ed6\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><em class=\"oy\">But how can we safely mixture depend occasions amidst ongoing write operations?<\/em><\/p>\n<p id=\"0a22\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">This is the place the idea of <em class=\"oy\">Eventually Consistent <\/em>counts turns into essential. <em class=\"oy\">By deliberately lagging behind the present time by a secure margin<\/em>, we make sure that aggregation all the time happens inside an immutable window.<\/p>\n<p id=\"5460\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Lets see what that appears like:<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*EOpW-VnA_YZF7KOP 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*EOpW-VnA_YZF7KOP 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*EOpW-VnA_YZF7KOP 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*EOpW-VnA_YZF7KOP 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*EOpW-VnA_YZF7KOP 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*EOpW-VnA_YZF7KOP 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*EOpW-VnA_YZF7KOP 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*EOpW-VnA_YZF7KOP 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*EOpW-VnA_YZF7KOP 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*EOpW-VnA_YZF7KOP 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*EOpW-VnA_YZF7KOP 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*EOpW-VnA_YZF7KOP 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*EOpW-VnA_YZF7KOP 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*EOpW-VnA_YZF7KOP 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"470\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"a980\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Let\u2019s break this down:<\/p>\n<ul class=\"\">\n<li id=\"c8ce\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">finalRollupTs<\/strong>: This represents the newest time when the counter worth was final aggregated. For a counter being operated for the primary time, this timestamp defaults to an affordable time previously.<\/li>\n<li id=\"881b\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Immutable Window and Lag<\/strong>: Aggregation can solely happen safely inside an immutable window that&#8217;s now not receiving counter occasions. The \u201cacceptLimit\u201d parameter of the TimeSequence Abstraction performs a vital position right here, because it rejects incoming occasions with timestamps past this restrict. During aggregations, this window is pushed barely additional again to account for clock skews.<\/li>\n<\/ul>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qi\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*DbtPCHPWoaauUkDr 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*DbtPCHPWoaauUkDr 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*DbtPCHPWoaauUkDr 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*DbtPCHPWoaauUkDr 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*DbtPCHPWoaauUkDr 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*DbtPCHPWoaauUkDr 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*DbtPCHPWoaauUkDr 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*DbtPCHPWoaauUkDr 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*DbtPCHPWoaauUkDr 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*DbtPCHPWoaauUkDr 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*DbtPCHPWoaauUkDr 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*DbtPCHPWoaauUkDr 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*DbtPCHPWoaauUkDr 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*DbtPCHPWoaauUkDr 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"153\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"1fee\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">This does imply that the counter worth will lag behind its most up-to-date replace by some margin (usually within the order of seconds). <em class=\"oy\">This strategy does go away the door open for missed occasions because of cross-region replication points. See \u201cFuture Work\u201d part on the finish.<\/em><\/p>\n<ul class=\"\">\n<li id=\"f593\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Aggregation Process<\/strong>: The rollup course of aggregates all occasions within the aggregation window <em class=\"oy\">for the reason that final rollup <\/em>to reach on the new worth.<\/li>\n<\/ul>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qj\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*oSHneX5BOi5VNGYM 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*oSHneX5BOi5VNGYM 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*oSHneX5BOi5VNGYM 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*oSHneX5BOi5VNGYM 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*oSHneX5BOi5VNGYM 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*oSHneX5BOi5VNGYM 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*oSHneX5BOi5VNGYM 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*oSHneX5BOi5VNGYM 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*oSHneX5BOi5VNGYM 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*oSHneX5BOi5VNGYM 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*oSHneX5BOi5VNGYM 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*oSHneX5BOi5VNGYM 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*oSHneX5BOi5VNGYM 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*oSHneX5BOi5VNGYM 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"129\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"19c8\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Rollup Store:<\/strong><\/p>\n<p id=\"48a1\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">We save the outcomes of this aggregation in a persistent retailer. The subsequent aggregation will merely proceed from this checkpoint.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*93S_a1YJ6zacuBnn 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*93S_a1YJ6zacuBnn 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*93S_a1YJ6zacuBnn 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*93S_a1YJ6zacuBnn 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*93S_a1YJ6zacuBnn 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*93S_a1YJ6zacuBnn 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*93S_a1YJ6zacuBnn 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*93S_a1YJ6zacuBnn 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*93S_a1YJ6zacuBnn 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*93S_a1YJ6zacuBnn 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*93S_a1YJ6zacuBnn 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*93S_a1YJ6zacuBnn 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*93S_a1YJ6zacuBnn 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*93S_a1YJ6zacuBnn 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"318\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"586a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">We create one such Rollup desk <em class=\"oy\">per dataset<\/em> and use Cassandra as our persistent retailer. However, as you&#8217;ll quickly see within the Control Plane part, the Counter service could be configured to work with any persistent retailer.<\/p>\n<p id=\"18db\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">LastWriteTs<\/strong>: Every time a given counter receives a write, we additionally log a <strong class=\"my gv\">last-write-timestamp<\/strong> as a columnar replace on this desk. This is finished utilizing Cassandra\u2019s <a class=\"af nu\" href=\"https:\/\/docs.datastax.com\/en\/cql-oss\/3.x\/cql\/cql_reference\/cqlInsert.html#cqlInsert__timestamp-value\" rel=\"noopener ugc nofollow\" target=\"_blank\">USING TIMESTAMP<\/a> characteristic to predictably apply the Last-Write-Win (LWW) semantics. This timestamp is identical because the <em class=\"oy\">event_time<\/em> for the occasion. In the following sections, we\u2019ll see how this timestamp is used to maintain some counters in lively rollup circulation till they&#8217;ve caught as much as their newest worth.<\/p>\n<p id=\"336a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Rollup Cache<\/strong><\/p>\n<p id=\"25be\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">To optimize learn efficiency, these values are cached in EVCache for every counter. We mix the <strong class=\"my gv\">finalRollupCount<\/strong> and <strong class=\"my gv\">finalRollupTs<\/strong> <em class=\"oy\">right into a single cached worth per counter<\/em> to stop potential mismatches between the depend and its corresponding checkpoint timestamp.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qk\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*giCU1AtWUYMXHZcI 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*giCU1AtWUYMXHZcI 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*giCU1AtWUYMXHZcI 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*giCU1AtWUYMXHZcI 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*giCU1AtWUYMXHZcI 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*giCU1AtWUYMXHZcI 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*giCU1AtWUYMXHZcI 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*giCU1AtWUYMXHZcI 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*giCU1AtWUYMXHZcI 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*giCU1AtWUYMXHZcI 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*giCU1AtWUYMXHZcI 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*giCU1AtWUYMXHZcI 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*giCU1AtWUYMXHZcI 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*giCU1AtWUYMXHZcI 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"496\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"1bbf\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">But, how do we all know which counters to set off rollups for? Let\u2019s discover our Write and Read path to know this higher.<\/p>\n<p id=\"77ab\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Add\/Clear Count:<\/strong><\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*wsxgnWH1yR0gHAEL 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*wsxgnWH1yR0gHAEL 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*wsxgnWH1yR0gHAEL 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*wsxgnWH1yR0gHAEL 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*wsxgnWH1yR0gHAEL 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*wsxgnWH1yR0gHAEL 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*wsxgnWH1yR0gHAEL 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*wsxgnWH1yR0gHAEL 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*wsxgnWH1yR0gHAEL 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*wsxgnWH1yR0gHAEL 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*wsxgnWH1yR0gHAEL 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*wsxgnWH1yR0gHAEL 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*wsxgnWH1yR0gHAEL 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*wsxgnWH1yR0gHAEL 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"359\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"6b09\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">An <em class=\"oy\">add<\/em> or <em class=\"oy\">clear<\/em> depend request writes durably to the TimeSequence Abstraction and updates the last-write-timestamp within the Rollup retailer. If the sturdiness acknowledgement fails, purchasers can retry their requests with the identical idempotency token with out the chance of overcounting.<strong class=\"my gv\"> <\/strong>Upon sturdiness, we ship a <em class=\"oy\">fire-and-forget <\/em>request to set off the rollup for the request counter.<\/p>\n<p id=\"6a87\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">GetCount:<\/strong><\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*76pQR6OISx9yuRmi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*76pQR6OISx9yuRmi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*76pQR6OISx9yuRmi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*76pQR6OISx9yuRmi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*76pQR6OISx9yuRmi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*76pQR6OISx9yuRmi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*76pQR6OISx9yuRmi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*76pQR6OISx9yuRmi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*76pQR6OISx9yuRmi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*76pQR6OISx9yuRmi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*76pQR6OISx9yuRmi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*76pQR6OISx9yuRmi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*76pQR6OISx9yuRmi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*76pQR6OISx9yuRmi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"359\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"23ce\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">We return the final rolled-up depend as<em class=\"oy\"> a fast point-read operation<\/em>, accepting the trade-off of doubtless delivering a barely stale depend. We additionally set off a rollup throughout the learn operation to advance the last-rollup-timestamp, enhancing the efficiency of <em class=\"oy\">subsequent<\/em> aggregations. This course of additionally <em class=\"oy\">self-remediates <\/em>a stale depend if any earlier rollups had failed.<\/p>\n<p id=\"2bdc\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">With this strategy, the counts<em class=\"oy\"> frequently converge<\/em> to their newest worth. Now, let\u2019s see how we scale this strategy to thousands and thousands of counters and hundreds of concurrent operations utilizing our Rollup Pipeline.<\/p>\n<p id=\"9ab5\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Rollup Pipeline:<\/strong><\/p>\n<p id=\"c974\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Each <strong class=\"my gv\">Counter-Rollup<\/strong> server operates a rollup pipeline to effectively mixture counts throughout thousands and thousands of counters. This is the place many of the complexity in Counter Abstraction is available in. In the next sections, we&#8217;ll share key particulars on how environment friendly aggregations are achieved.<\/p>\n<p id=\"d23a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Light-Weight Roll-Up Event: <\/strong>As seen in our Write and Read paths above, each operation on a counter sends a lightweight occasion to the Rollup server:<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"0c58\" class=\"pz nw gu pw b bg qa qb l qc qd\">rollupEvent: {<br\/>\"namespace\": \"my_dataset\",<br\/>\"counter\": \"counter123\"<br\/>}<\/span><\/pre>\n<p id=\"8d93\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Note that this occasion doesn&#8217;t embrace the increment. This is simply a sign to the Rollup server that this counter has been accessed and now must be aggregated. Knowing precisely which particular counters should be aggregated prevents scanning the complete occasion dataset for the aim of aggregations.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*Yusg6kC9Jj9ayjbi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*Yusg6kC9Jj9ayjbi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*Yusg6kC9Jj9ayjbi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*Yusg6kC9Jj9ayjbi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*Yusg6kC9Jj9ayjbi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*Yusg6kC9Jj9ayjbi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*Yusg6kC9Jj9ayjbi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*Yusg6kC9Jj9ayjbi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*Yusg6kC9Jj9ayjbi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*Yusg6kC9Jj9ayjbi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*Yusg6kC9Jj9ayjbi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*Yusg6kC9Jj9ayjbi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*Yusg6kC9Jj9ayjbi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*Yusg6kC9Jj9ayjbi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"284\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"0e5d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">In-Memory Rollup Queues:<\/strong> A given Rollup server occasion runs a set of <em class=\"oy\">in-memory<\/em> queues to obtain rollup occasions and parallelize aggregations. In the primary model of this service, we settled on utilizing in-memory queues to cut back provisioning complexity, save on infrastructure prices, and make rebalancing the variety of queues pretty simple. However, this comes with the trade-off of doubtless lacking rollup occasions in case of an occasion crash. For extra particulars, see the \u201cStale Counts\u201d part in \u201cFuture Work.\u201d<\/p>\n<p id=\"0d6d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Minimize Duplicate Effort<\/strong>: We use a quick non-cryptographic hash like <a class=\"af nu\" href=\"https:\/\/xxhash.com\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">XXHash<\/a> to make sure that the identical set of counters find yourself on the identical queue. Further, we attempt to reduce the quantity of duplicate aggregation work by having a separate rollup stack that chooses to run <em class=\"oy\">fewer<\/em> <em class=\"oy\">beefier<\/em> cases.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi ql\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*u3p0kGfuwvK5mP_j 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*u3p0kGfuwvK5mP_j 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*u3p0kGfuwvK5mP_j 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*u3p0kGfuwvK5mP_j 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*u3p0kGfuwvK5mP_j 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*u3p0kGfuwvK5mP_j 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*u3p0kGfuwvK5mP_j 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*u3p0kGfuwvK5mP_j 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*u3p0kGfuwvK5mP_j 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*u3p0kGfuwvK5mP_j 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*u3p0kGfuwvK5mP_j 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*u3p0kGfuwvK5mP_j 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*u3p0kGfuwvK5mP_j 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*u3p0kGfuwvK5mP_j 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"440\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"223c\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Availability and Race Conditions: <\/strong>Having a single Rollup server occasion can reduce duplicate aggregation work however could create availability challenges for triggering rollups. <em class=\"oy\">If<\/em> we select to horizontally scale the Rollup servers, we enable threads to overwrite rollup values whereas avoiding any type of distributed locking mechanisms to take care of excessive availability and efficiency. This strategy stays secure as a result of aggregation happens inside an immutable window. Although the idea of <em class=\"oy\">now()<\/em> could differ between threads, inflicting rollup values to typically fluctuate, the counts will finally converge to an correct worth inside every immutable aggregation window.<\/p>\n<p id=\"acf2\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Rebalancing Queues<\/strong>: If we have to scale the variety of queues, a easy Control Plane configuration replace adopted by a re-deploy is sufficient to rebalance the variety of queues.<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"845c\" class=\"pz nw gu pw b bg qa qb l qc qd\">      \"eventual_counter_config\": {             <br\/>\"queue_config\": {                    <br\/>\"num_queues\" : 8,  \/\/ change to 16 and re-deploy<br\/>...<\/span><\/pre>\n<p id=\"3a8b\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Handling Deployments<\/strong>: During deployments, these queues shut down gracefully, draining all present occasions first, whereas the brand new Rollup server occasion begins up with doubtlessly new queue configurations. There could also be a short interval when each the previous and new Rollup servers are lively, however as talked about earlier than, this race situation is managed since aggregations happen inside immutable home windows.<\/p>\n<p id=\"a67a\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Minimize Rollup Effort<\/strong>: Receiving a number of occasions for a similar counter doesn\u2019t imply rolling it up a number of instances. We drain these rollup occasions right into a Set, making certain <em class=\"oy\">a given counter is rolled up solely as soon as<\/em> <em class=\"oy\">throughout a rollup window<\/em>.<\/p>\n<p id=\"c500\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Efficient Aggregation: <\/strong>Each rollup shopper processes a batch of counters concurrently. Within every batch, it queries the underlying TimeSequence abstraction in parallel to mixture occasions inside specified time boundaries. The TimeSequence abstraction optimizes these vary scans to realize low millisecond latencies.<\/p>\n<p id=\"fea5\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Dynamic Batching<\/strong>: The Rollup server dynamically adjusts the variety of time partitions that should be scanned primarily based on cardinality of counters with a view to stop overwhelming the underlying retailer with many parallel learn requests.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qm\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*hoPpSmQeScn87q0U 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*hoPpSmQeScn87q0U 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*hoPpSmQeScn87q0U 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*hoPpSmQeScn87q0U 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*hoPpSmQeScn87q0U 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*hoPpSmQeScn87q0U 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*hoPpSmQeScn87q0U 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*hoPpSmQeScn87q0U 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*hoPpSmQeScn87q0U 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*hoPpSmQeScn87q0U 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*hoPpSmQeScn87q0U 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*hoPpSmQeScn87q0U 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*hoPpSmQeScn87q0U 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*hoPpSmQeScn87q0U 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"557\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"9446\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Adaptive Back-Pressure<\/strong>: Each shopper waits for one batch to finish earlier than issuing the rollups for the following batch. It adjusts the wait time between batches primarily based on the efficiency of the earlier batch. This strategy gives back-pressure throughout rollups to stop overwhelming the underlying TimeSequence retailer.<\/p>\n<p id=\"2693\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\"><strong class=\"my gv\">Handling Convergence<\/strong>:<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qn\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*-hlw324cMUaC6pQJ 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*-hlw324cMUaC6pQJ 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*-hlw324cMUaC6pQJ 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*-hlw324cMUaC6pQJ 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*-hlw324cMUaC6pQJ 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*-hlw324cMUaC6pQJ 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*-hlw324cMUaC6pQJ 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*-hlw324cMUaC6pQJ 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*-hlw324cMUaC6pQJ 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*-hlw324cMUaC6pQJ 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*-hlw324cMUaC6pQJ 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*-hlw324cMUaC6pQJ 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*-hlw324cMUaC6pQJ 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*-hlw324cMUaC6pQJ 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"447\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"da2c\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">In order to stop <strong class=\"my gv\">low-cardinality<\/strong> counters from lagging behind an excessive amount of and subsequently scanning too many time partitions, they&#8217;re saved in fixed rollup circulation. For <strong class=\"my gv\">high-cardinality<\/strong> counters, repeatedly circulating them would devour extreme reminiscence in our Rollup queues. This is the place the <strong class=\"my gv\">last-write-timestamp<\/strong> talked about beforehand performs a vital position. The Rollup server inspects this timestamp to find out if a given counter must be re-queued, making certain that we proceed aggregating till it has totally caught up with the writes.<\/p>\n<p id=\"af9d\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Now, let\u2019s see how we leverage this counter sort to supply an up-to-date present depend in near-realtime.<\/p>\n<p id=\"8e14\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">We are experimenting with a barely modified model of the Eventually Consistent counter. Again, take the time period \u2018Accurate\u2019 with a grain of salt. The key distinction between this sort of counter and its counterpart is that the <em class=\"oy\">delta<\/em>, representing the counts for the reason that last-rolled-up timestamp, is computed in real-time.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*FVOlMO0VgrQoVBBi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*FVOlMO0VgrQoVBBi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*FVOlMO0VgrQoVBBi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*FVOlMO0VgrQoVBBi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*FVOlMO0VgrQoVBBi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*FVOlMO0VgrQoVBBi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*FVOlMO0VgrQoVBBi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*FVOlMO0VgrQoVBBi 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*FVOlMO0VgrQoVBBi 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*FVOlMO0VgrQoVBBi 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*FVOlMO0VgrQoVBBi 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*FVOlMO0VgrQoVBBi 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*FVOlMO0VgrQoVBBi 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*FVOlMO0VgrQoVBBi 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"290\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"a925\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Aggregating this delta in real-time can influence the efficiency of this operation, relying on the variety of occasions and partitions that should be scanned to retrieve this delta. The identical precept of rolling up in batches applies right here to stop scanning too many partitions in parallel.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qo\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*M3dbSof98dTfeuNe 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*M3dbSof98dTfeuNe 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*M3dbSof98dTfeuNe 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*M3dbSof98dTfeuNe 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*M3dbSof98dTfeuNe 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*M3dbSof98dTfeuNe 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*M3dbSof98dTfeuNe 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*M3dbSof98dTfeuNe 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*M3dbSof98dTfeuNe 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*M3dbSof98dTfeuNe 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*M3dbSof98dTfeuNe 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*M3dbSof98dTfeuNe 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*M3dbSof98dTfeuNe 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*M3dbSof98dTfeuNe 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"239\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"3b68\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Conversely, if the counters on this dataset are<em class=\"oy\"> <\/em>accessed<em class=\"oy\"> <\/em>incessantly, the time hole for the delta stays slender, making this strategy of fetching present counts fairly efficient.<\/p>\n<p id=\"49a2\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Now, let\u2019s see how all this complexity is managed by having a unified Control Plane configuration.<\/p>\n<p id=\"47f1\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">The <a class=\"af nu\" href=\"https:\/\/netflixtechblog.medium.com\/data-gateway-a-platform-for-growing-and-protecting-the-data-tier-f1ed8db8f5c6\" rel=\"noopener\" target=\"_blank\">Data Gateway Platform Control Plane<\/a> manages management settings for all abstractions and namespaces, together with the Counter Abstraction. Below, is an instance of a management airplane configuration for a namespace that helps finally constant counters with low cardinality:<\/p>\n<pre class=\"pk pl pm pn po pv pw px bp py bb bk\"><span id=\"f298\" class=\"pz nw gu pw b bg qa qb l qc qd\">\"persistence_configuration\": [<br\/>{<br\/>\"id\": \"CACHE\",                             \/\/ Counter cache config<br\/>\"scope\": \"dal=counter\",                                                   <br\/>\"physical_storage\": {<br\/>\"type\": \"EVCACHE\",                       \/\/ type of cache storage<br\/>\"cluster\": \"evcache_dgw_counter_tier1\"   \/\/ Shared EVCache cluster<br\/>}<br\/>},<br\/>{<br\/>\"id\": \"COUNTER_ROLLUP\",<br\/>\"scope\": \"dal=counter\",                    \/\/ Counter abstraction config<br\/>\"physical_storage\": {                     <br\/>\"type\": \"CASSANDRA\",                     \/\/ type of Rollup store<br\/>\"cluster\": \"cass_dgw_counter_uc1\",       \/\/ physical cluster name<br\/>\"dataset\": \"my_dataset_1\"                \/\/ namespace\/dataset   <br\/>},<br\/>\"counter_cardinality\": \"LOW\",              \/\/ supported counter cardinality<br\/>\"config\": {<br\/>\"counter_type\": \"EVENTUAL\",              \/\/ Type of counter<br\/>\"eventual_counter_config\": {             \/\/ eventual counter type<br\/>\"internal_config\": {                  <br\/>\"queue_config\": {                    \/\/ adjust w.r.t cardinality<br\/>\"num_queues\" : 8,                  \/\/ Rollup queues per instance<br\/>\"coalesce_ms\": 10000,              \/\/ coalesce duration for rollups<br\/>\"capacity_bytes\": 16777216         \/\/ allocated memory per queue<br\/>},<br\/>\"rollup_batch_count\": 32             \/\/ parallelization factor<br\/>}<br\/>}<br\/>}<br\/>},<br\/>{<br\/>\"id\": \"EVENT_STORAGE\",<br\/>\"scope\": \"dal=ts\",                         \/\/ TimeSeries Event store<br\/>\"physical_storage\": {<br\/>\"type\": \"CASSANDRA\",                     \/\/ persistent store type<br\/>\"cluster\": \"cass_dgw_counter_uc1\",       \/\/ physical cluster name<br\/>\"dataset\": \"my_dataset_1\",               \/\/ keyspace name<br\/>},<br\/>\"config\": {                              <br\/>\"time_partition\": {                      \/\/ time-partitioning for events<br\/>\"buckets_per_id\": 4,                   \/\/ event buckets within<br\/>\"seconds_per_bucket\": \"600\",           \/\/ smaller width for LOW card<br\/>\"seconds_per_slice\": \"86400\",          \/\/ width of a time slice table<br\/>},<br\/>\"accept_limit\": \"5s\",                    \/\/ boundary for immutability<br\/>},<br\/>\"lifecycleConfigs\": {<br\/>\"lifecycleConfig\": [<br\/>{<br\/>\"type\": \"retention\",                 \/\/ Event retention<br\/>\"config\": {<br\/>\"close_after\": \"518400s\",<br\/>\"delete_after\": \"604800s\"          \/\/ 7 day count event retention<br\/>}<br\/>}<br\/>]<br\/>}<br\/>}<br\/>]<\/span><\/pre>\n<p id=\"9fd9\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Using such a management airplane configuration, we compose a number of abstraction layers utilizing containers deployed on the identical host, with every container fetching configuration particular to its scope.<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qp\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*4MdrlEjWg2MXU9S3 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*4MdrlEjWg2MXU9S3 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*4MdrlEjWg2MXU9S3 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*4MdrlEjWg2MXU9S3 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*4MdrlEjWg2MXU9S3 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*4MdrlEjWg2MXU9S3 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*4MdrlEjWg2MXU9S3 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*4MdrlEjWg2MXU9S3 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*4MdrlEjWg2MXU9S3 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*4MdrlEjWg2MXU9S3 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*4MdrlEjWg2MXU9S3 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*4MdrlEjWg2MXU9S3 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*4MdrlEjWg2MXU9S3 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*4MdrlEjWg2MXU9S3 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"737\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"ec90\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">As with the TimeSequence abstraction, our automation makes use of a bunch of person inputs concerning their workload and cardinalities to reach on the proper set of infrastructure and associated management airplane configuration. You can study extra about this course of in a chat given by considered one of our beautiful colleagues, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/joseph-lynch-9976a431\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Joey Lynch<\/a> : <a class=\"af nu\" href=\"https:\/\/www.youtube.com\/watch?v=Lf6B1PxIvAs\" rel=\"noopener ugc nofollow\" target=\"_blank\">How Netflix optimally provisions infrastructure within the cloud<\/a>.<\/p>\n<p id=\"f469\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">At the time of penning this weblog, this service was processing near <strong class=\"my gv\">75K depend requests\/second<\/strong><em class=\"oy\"> globally<\/em> throughout the completely different API endpoints and datasets:<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*1h_af4Kk3YrZrqlc 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*1h_af4Kk3YrZrqlc 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*1h_af4Kk3YrZrqlc 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*1h_af4Kk3YrZrqlc 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*1h_af4Kk3YrZrqlc 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*1h_af4Kk3YrZrqlc 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*1h_af4Kk3YrZrqlc 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*1h_af4Kk3YrZrqlc 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*1h_af4Kk3YrZrqlc 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*1h_af4Kk3YrZrqlc 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*1h_af4Kk3YrZrqlc 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*1h_af4Kk3YrZrqlc 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*1h_af4Kk3YrZrqlc 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*1h_af4Kk3YrZrqlc 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"357\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"92ef\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">whereas offering<strong class=\"my gv\"> single-digit millisecond<\/strong> latencies for all its endpoints:<\/p>\n<figure class=\"pk pl pm pn po pp ph pi paragraph-image\">\n<div role=\"button\" tabindex=\"0\" class=\"pq pr fj ps bh pt\">\n<div class=\"ph pi qg\"><picture><source srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/format:webp\/0*UnI7eore6gvuqrrF 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/format:webp\/0*UnI7eore6gvuqrrF 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/format:webp\/0*UnI7eore6gvuqrrF 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/format:webp\/0*UnI7eore6gvuqrrF 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/format:webp\/0*UnI7eore6gvuqrrF 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/format:webp\/0*UnI7eore6gvuqrrF 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/format:webp\/0*UnI7eore6gvuqrrF 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\" type=\"image\/webp\"\/><source data-testid=\"og\" srcset=\"https:\/\/miro.medium.com\/v2\/resize:fit:640\/0*UnI7eore6gvuqrrF 640w, https:\/\/miro.medium.com\/v2\/resize:fit:720\/0*UnI7eore6gvuqrrF 720w, https:\/\/miro.medium.com\/v2\/resize:fit:750\/0*UnI7eore6gvuqrrF 750w, https:\/\/miro.medium.com\/v2\/resize:fit:786\/0*UnI7eore6gvuqrrF 786w, https:\/\/miro.medium.com\/v2\/resize:fit:828\/0*UnI7eore6gvuqrrF 828w, https:\/\/miro.medium.com\/v2\/resize:fit:1100\/0*UnI7eore6gvuqrrF 1100w, https:\/\/miro.medium.com\/v2\/resize:fit:1400\/0*UnI7eore6gvuqrrF 1400w\" sizes=\"(min-resolution: 4dppx) and (max-width: 700px) 50vw, (-webkit-min-device-pixel-ratio: 4) and (max-width: 700px) 50vw, (min-resolution: 3dppx) and (max-width: 700px) 67vw, (-webkit-min-device-pixel-ratio: 3) and (max-width: 700px) 65vw, (min-resolution: 2.5dppx) and (max-width: 700px) 80vw, (-webkit-min-device-pixel-ratio: 2.5) and (max-width: 700px) 80vw, (min-resolution: 2dppx) and (max-width: 700px) 100vw, (-webkit-min-device-pixel-ratio: 2) and (max-width: 700px) 100vw, 700px\"\/><img alt=\"\" class=\"bh md pu c\" width=\"700\" height=\"366\" loading=\"lazy\" role=\"presentation\"\/><\/picture><\/div>\n<\/div>\n<\/figure>\n<p id=\"19ec\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">While our system is strong, we nonetheless have work to do in making it extra dependable and enhancing its options. Some of that work contains:<\/p>\n<ul class=\"\">\n<li id=\"bafb\" class=\"mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt qf pa pb bk\"><strong class=\"my gv\">Regional Rollups: <\/strong>Cross-region replication points may end up in missed occasions from different areas. An alternate technique entails establishing a rollup desk for every area, after which tallying them in a worldwide rollup desk. A key problem on this design can be successfully speaking the clearing of the counter throughout areas.<\/li>\n<li id=\"7818\" class=\"mw mx gu my b mz pc nb nc nd pd nf ng nh pe nj nk nl pf nn no np pg nr ns nt qf pa pb bk\"><strong class=\"my gv\">Error Detection and Stale Counts<\/strong>: Excessively stale counts can happen if rollup occasions are misplaced or if a rollups fails and isn\u2019t retried. This isn\u2019t a difficulty for incessantly accessed counters, as they continue to be in rollup circulation. This concern is extra pronounced for counters that aren\u2019t accessed incessantly. Typically, the preliminary learn for such a counter will set off a rollup,<em class=\"oy\"> self-remediating <\/em>the difficulty. However, to be used circumstances that can&#8217;t settle for doubtlessly stale preliminary reads, we plan to implement improved error detection, rollup handoffs, and sturdy queues for resilient retries.<\/li>\n<\/ul>\n<p id=\"64c0\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">Distributed counting stays a difficult downside in pc science. In this weblog, we explored a number of approaches to implement and deploy a Counting service at scale. While there could also be different strategies for distributed counting, our purpose has been to ship blazing quick efficiency at low infrastructure prices whereas sustaining excessive availability and offering idempotency ensures. Along the best way, we make numerous trade-offs to satisfy the various counting necessities at Netflix. We hope you discovered this weblog publish insightful.<\/p>\n<p id=\"a883\" class=\"pw-post-body-paragraph mw mx gu my b mz na nb nc nd ne nf ng nh ni nj nk nl nm nn no np nq nr ns nt gn bk\">Stay tuned for <strong class=\"my gv\">Part 3 <\/strong>of Composite Abstractions at Netflix, the place we\u2019ll introduce our <strong class=\"my gv\">Graph Abstraction<\/strong>, a brand new service being constructed on prime of the <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/introducing-netflixs-key-value-data-abstraction-layer-1ea8a0a11b30\">Key-Value Abstraction<\/a> <em class=\"oy\">and<\/em> the <a class=\"af nu\" rel=\"noopener ugc nofollow\" target=\"_blank\" href=\"https:\/\/netflixtechblog.com\/introducing-netflix-timeseries-data-abstraction-layer-31552f6326f8\">TimeSequence Abstraction<\/a> to deal with high-throughput, low-latency graphs.<\/p>\n<p id=\"78b5\" class=\"pw-post-body-paragraph mw mx gu my b mz ot nb nc nd ou nf ng nh ov nj nk nl ow nn no np ox nr ns nt gn bk\">Special due to our beautiful colleagues who contributed to the Counter Abstraction\u2019s success: <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/joseph-lynch-9976a431\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Joey Lynch<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/vinaychella\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Vinay Chella<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/kaidanfullerton\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Kaidan Fullerton<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/tomdevoe\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Tom DeVoe<\/a>, <a class=\"af nu\" href=\"https:\/\/www.linkedin.com\/in\/mengqingwang\/\" rel=\"noopener ugc nofollow\" target=\"_blank\">Mengqing Wang<\/a><\/p>\n<\/div>\n<p>[ad_2]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>[ad_1] 18 min learn \u00b7 18 hours in the past By: Rajiv Shringi, Oleksii Tkachuk, Kartik Sathyanarayanan In our earlier weblog publish, we launched Netflix\u2019s TimeSequence Abstraction, a distributed service designed to retailer and question giant volumes of temporal occasion knowledge with low millisecond latencies. Today, we\u2019re excited to current the Distributed Counter Abstraction. This [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":133787,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37],"tags":[6269,955,6500,6499,115,889,6501,4337],"class_list":{"0":"post-133785","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-netflix","8":"tag-abstraction","9":"tag-blog","10":"tag-counter","11":"tag-distributed","12":"tag-netflix","13":"tag-netflixs","14":"tag-nov","15":"tag-technology"},"_links":{"self":[{"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/posts\/133785","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/comments?post=133785"}],"version-history":[{"count":0,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/posts\/133785\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/media\/133787"}],"wp:attachment":[{"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/media?parent=133785"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/categories?post=133785"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/showbizztoday.com\/index.php\/wp-json\/wp\/v2\/tags?post=133785"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}