CINXE.COM
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="ie=edge"/><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/><style id="typography.js">html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}html{font:112.5%/1.722 'Lato',sans-serif;box-sizing:border-box;overflow-y:scroll;}*{box-sizing:inherit;}*:before{box-sizing:inherit;}*:after{box-sizing:inherit;}body{color:hsla(0,0%,0%,0.8);font-family:'Lato',sans-serif;font-weight:400;word-wrap:break-word;font-kerning:normal;-moz-font-feature-settings:"kern", "liga", "clig", "calt";-ms-font-feature-settings:"kern", "liga", "clig", "calt";-webkit-font-feature-settings:"kern", "liga", "clig", "calt";font-feature-settings:"kern", "liga", "clig", "calt";}img{max-width:100%;margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}h1{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:2rem;line-height:1.1;}h2{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:1.51572rem;line-height:1.1;}h3{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:1.31951rem;line-height:1.1;}h4{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:1rem;line-height:1.1;}h5{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:0.87055rem;line-height:1.1;}h6{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;color:hsla(0,0%,0%,0.9);font-family:'Neuton',sans-serif;font-weight:700;text-rendering:optimizeLegibility;font-size:0.81225rem;line-height:1.1;}hgroup{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}ul{margin-left:1.722rem;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;list-style-position:outside;list-style-image:none;}ol{margin-left:1.722rem;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;list-style-position:outside;list-style-image:none;}dl{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}dd{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}p{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}figure{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}pre{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;font-size:0.85rem;line-height:1.722rem;}table{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;font-size:1rem;line-height:1.722rem;border-collapse:collapse;width:100%;}fieldset{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}blockquote{margin-left:0;margin-right:1.722rem;margin-top:0;padding-bottom:0;padding-left:1.93725rem;padding-right:0;padding-top:0;margin-bottom:1.722rem;font-size:1.1487rem;line-height:1.722rem;color:hsla(0,0%,0%,0.59);border-left:0.64575rem solid;border-color:#612423;}form{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}noscript{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}iframe{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}hr{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:calc(1.722rem - 1px);background:hsla(0,0%,0%,0.2);border:none;height:1px;}address{margin-left:0;margin-right:0;margin-top:0;padding-bottom:0;padding-left:0;padding-right:0;padding-top:0;margin-bottom:1.722rem;}b{font-weight:700;}strong{font-weight:700;}dt{font-weight:700;}th{font-weight:700;}li{margin-bottom:calc(1.722rem / 2);}ol li{padding-left:0;}ul li{padding-left:0;}li > ol{margin-left:1.722rem;margin-bottom:calc(1.722rem / 2);margin-top:calc(1.722rem / 2);}li > ul{margin-left:1.722rem;margin-bottom:calc(1.722rem / 2);margin-top:calc(1.722rem / 2);}blockquote *:last-child{margin-bottom:0;}li *:last-child{margin-bottom:0;}p *:last-child{margin-bottom:0;}li > p{margin-bottom:calc(1.722rem / 2);}code{font-size:0.85rem;line-height:1.722rem;}kbd{font-size:0.85rem;line-height:1.722rem;}samp{font-size:0.85rem;line-height:1.722rem;}abbr{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;}acronym{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;}abbr[title]{border-bottom:1px dotted hsla(0,0%,0%,0.5);cursor:help;text-decoration:none;}thead{text-align:left;}td,th{text-align:left;border-bottom:1px solid hsla(0,0%,0%,0.12);font-feature-settings:"tnum";-moz-font-feature-settings:"tnum";-ms-font-feature-settings:"tnum";-webkit-font-feature-settings:"tnum";padding-left:1.148rem;padding-right:1.148rem;padding-top:0.861rem;padding-bottom:calc(0.861rem - 1px);}th:first-child,td:first-child{padding-left:0;}th:last-child,td:last-child{padding-right:0;}a{color:#4665b7;text-decoration:none;}a:hover,a:active{color:hsla(0,0%,0%,0.8);}h1,h2,h3,h4,h5,h6{margin-top:3.444rem;}blockquote > :last-child{margin-bottom:0;}blockquote cite{font-size:1rem;line-height:1.722rem;color:hsla(0,0%,0%,0.8);font-weight:400;}blockquote cite:before{content:"— ";}@media only screen and (max-width:480px){blockquote{margin-left:-1.2915rem;margin-right:0;border-left:0.32288rem solid;border-color:#612423;padding-left:0.96862rem;}}</style><style data-href="/styles.6661cafb0f80a69e6cc3.css">footer{background-color:rgba(0,0,0,.9);padding:20px 0 40px;color:hsla(0,0%,100%,.7)}footer .Footer-module--container--3Dvn1{display:grid;grid-template-columns:50% 50%;margin-left:auto;margin-right:auto;max-width:50rem;padding:20px}footer a{color:#ddd;margin:10px}footer a:hover{color:#fff}footer .Footer-module--copyright--2uglW{text-align:right}header{display:flex;justify-content:space-between;align-items:center;padding:1rem;position:-webkit-sticky;position:sticky;top:0;background:hsla(0,0%,100%,.75);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);z-index:9;margin-left:auto;margin-right:auto;height:70px}header a{text-decoration:none;color:#3c4856;font-size:1.1rem;margin-top:0}header h1{margin-top:5px}header nav a{color:#757575;transition:color .2s ease;text-decoration:none;display:inline-block;position:relative;font-size:16px}header nav a:hover{color:#4f647d}header nav a strong{margin:1px;color:#4f647d;font-size:.8rem}header .Header-module--activeNav--25noA{color:#4f647d;font-weight:700}header .Header-module--we-are-hiring--3Vkey span{color:red}.Header-module--brand--nQIOf{margin-top:40px;display:flex;flex-direction:row;flex-wrap:wrap}.Header-module--main-nav--2C0n8{display:flex;list-style:none;padding:0;margin-top:30px;overflow:auto;overflow:-moz-scrollbars-none;scrollbar-width:none;-ms-overflow-style:none!important}.Header-module--main-nav--2C0n8::-webkit-scrollbar{width:0!important}.Header-module--main-nav--2C0n8 li{font-size:1.3rem}.Header-module--main-nav--2C0n8 li+li{margin-left:1.5rem}@media (max-width:1000px){header{display:block;height:120px}.Header-module--brand--nQIOf{margin-top:0;margin-bottom:0}nav{margin-top:-50px}nav ul{margin:0}}@media (max-width:768px){header strong{display:none}.Header-module--main-nav--2C0n8 li+li{margin-left:1rem}}@media (max-width:520px){header nav a{font-size:.7rem;border:1px solid #eee;padding:2px;border-radius:5px}}body{margin:0;background:#fafbfb}h1,h2,h3,h4,h5,h6{margin-top:1rem}main{margin-left:auto;margin-right:auto;max-width:50rem;display:flex;flex-direction:column;min-height:100vh;padding:20px}article{height:90%}article:hover{box-shadow:0 11px 22px 12px #eee;border-radius:5px}.pill{display:inline-block;height:24px;padding:0 10px;margin-left:5px;margin-right:5px;margin-bottom:10px;color:#929fb3;text-transform:uppercase;font-size:12px;line-height:22px;border-radius:2px;border:1px solid}code.language-text{background:#d4d4d4!important;color:#000!important;font-family:monospace!important;text-shadow:none!important;padding-left:5px!important;padding-right:5px!important}.gatsby-highlight{font-size:14px}.projects{text-align:center}.project-box{float:left;width:50%;padding:20px}.code-latex{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em;color:#f8f8f2;background:#272822}.MJXc-display{text-align:initial!important}@media (max-width:600px){.project-box{width:100%}}.PostsListing-module--article-list--3ReSK{display:grid;grid-template-columns:50% 50%}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I{margin-bottom:30px;display:inline-block;width:auto;transition:all .25s ease;padding-top:20px}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--author-image--1wgum{width:30px;height:30px;display:inline-block;border-radius:15px;margin-bottom:0}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--author-description--uwweV{display:inline-block;margin-left:10px}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--thumbnail-container--c94Mm{height:200px;overflow:hidden;text-align:center;display:flex;align-items:center;justify-content:center;background:#ececec}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--thumbnail-container--c94Mm img{margin-bottom:0}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--author-name--3k7M0{margin-top:0;margin-bottom:0}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I:hover{cursor:pointer}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--right--2MxCO{padding:15px;color:#3c4856;margin-top:-20px}.PostsListing-module--article-list--3ReSK .PostsListing-module--article-box--3M6_I .PostsListing-module--right--2MxCO .PostsListing-module--meta--3cFzL{margin-bottom:20px;font-size:.7rem}@media (max-width:768px){.PostsListing-module--article-list--3ReSK{grid-template-columns:100%}}.Bio-module--avatar--v_AB0{border-radius:50%;width:70px;float:left;margin:5px 20px}.PostTags-module--tag-container--ksSgQ a{text-decoration:none;display:inline-grid}.PostTags-module--tag-container--ksSgQ span{font-size:.8rem;font-weight:500;padding:.3rem .6rem;margin:.3rem;border-radius:3px;background:rgba(141,154,169,.21);color:rgba(0,0,0,.54)}.SocialLinks-module--social-links--1RDpJ{position:fixed;top:100px;left:30px;flex-direction:row;flex-wrap:wrap;justify-content:center;align-content:center;align-items:center;margin:15px 0}.SocialLinks-module--social-links--1RDpJ div{margin:5px 15px;cursor:pointer}.SocialLinks-module--share-count--3wXKp{text-align:center}@media (max-width:1200px){.SocialLinks-module--social-links--1RDpJ{position:relative;top:0}.SocialLinks-module--social-links--1RDpJ div{display:inline-grid;position:relative}}.post-module--post-meta--1ci95{color:#757575;margin-bottom:.5rem;font-size:.7rem}.post-module--pagination--1F-V0{display:flex;flex-wrap:wrap;justify-content:space-between;list-style:none;padding:0;margin-top:4rem}code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:none;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#f8f8f2}.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}</style><meta name="generator" content="Gatsby 2.22.11"/><title data-react-helmet="true">Leveraging Kafka Streams to reduce DB Hits | Wingify Engineering</title><link data-react-helmet="true" rel="icon" type="image/png" href="/images/favicon.png"/><meta data-react-helmet="true" name="description" content="I have been working with Apache Kafka for more than 4 years now and have seen it evolve from a basic distributed commit log service…"/><meta data-react-helmet="true" name="image" content=""/><meta data-react-helmet="true" property="og:url" content="https://engineering.wingify.com//posts/leveraging-kafka-streams-to-reduce-db-hits/"/><meta data-react-helmet="true" property="og:type" content="article"/><meta data-react-helmet="true" property="og:title" content="Leveraging Kafka Streams to reduce DB Hits"/><meta data-react-helmet="true" property="og:description" content="I have been working with Apache Kafka for more than 4 years now and have seen it evolve from a basic distributed commit log service…"/><meta data-react-helmet="true" property="og:image" content=""/><meta data-react-helmet="true" property="fb:app_id" content=""/><meta data-react-helmet="true" name="twitter:card" content="summary_large_image"/><meta data-react-helmet="true" name="twitter:creator" content="wingify_engg"/><meta data-react-helmet="true" name="twitter:title" content="Leveraging Kafka Streams to reduce DB Hits"/><meta data-react-helmet="true" name="twitter:description" content="I have been working with Apache Kafka for more than 4 years now and have seen it evolve from a basic distributed commit log service…"/><meta data-react-helmet="true" name="twitter:image" content=""/><script data-react-helmet="true" type="application/ld+json">[{"@context":"http://schema.org","@type":"WebSite","url":"https://engineering.wingify.com/","name":"Leveraging Kafka Streams to reduce DB Hits","alternateName":"Wingify Engineering - Blog"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://engineering.wingify.com//posts/leveraging-kafka-streams-to-reduce-db-hits/","name":"Leveraging Kafka Streams to reduce DB Hits","image":""}}]},{"@context":"http://schema.org","@type":"BlogPosting","url":"https://engineering.wingify.com/","name":"Leveraging Kafka Streams to reduce DB Hits","alternateName":"Wingify Engineering - Blog","headline":"Leveraging Kafka Streams to reduce DB Hits","image":{"@type":"ImageObject","url":""},"description":"I have been working with Apache Kafka for more than 4 years now and have seen it evolve from a basic distributed commit log service…"}]</script><link href="//fonts.googleapis.com/css?family=Neuton:700|Lato:400,400i,700" rel="stylesheet" type="text/css"/><style type="text/css"> .anchor.before { position: absolute; top: 0; left: 0; transform: translateX(-100%); padding-right: 4px; } .anchor.after { display: inline-block; padding-left: 4px; } h1 .anchor svg, h2 .anchor svg, h3 .anchor svg, h4 .anchor svg, h5 .anchor svg, h6 .anchor svg { visibility: hidden; } h1:hover .anchor svg, h2:hover .anchor svg, h3:hover .anchor svg, h4:hover .anchor svg, h5:hover .anchor svg, h6:hover .anchor svg, h1 .anchor:focus svg, h2 .anchor:focus svg, h3 .anchor:focus svg, h4 .anchor:focus svg, h5 .anchor:focus svg, h6 .anchor:focus svg { visibility: visible; } </style><script> document.addEventListener("DOMContentLoaded", function(event) { var hash = window.decodeURI(location.hash.replace('#', '')) if (hash !== '') { var element = document.getElementById(hash) if (element) { var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop var clientTop = document.documentElement.clientTop || document.body.clientTop || 0 var offset = element.getBoundingClientRect().top + scrollTop - clientTop // Wait for the browser to finish rendering before scrolling. setTimeout((function() { window.scrollTo(0, offset - 0) }), 0) } } }) </script><script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl+'';f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer', 'GTM-T9CS925');</script><link rel="sitemap" type="application/xml" href="/sitemap.xml"/><link rel="manifest" href="/manifest.webmanifest"/><meta name="theme-color" content="#c62828"/><link rel="apple-touch-icon" sizes="48x48" href="/images/favicon.png"/><link rel="alternate" type="application/rss+xml" title="/atom.xml" href="/atom.xml"/><link as="script" rel="preload" href="/webpack-runtime-e67a7c97db3322d008c9.js"/><link as="script" rel="preload" href="/framework-9fe058f4359556db0d38.js"/><link as="script" rel="preload" href="/app-99ec7086382d867c7c8c.js"/><link as="script" rel="preload" href="/styles-823ae8103e36ae8a7f9f.js"/><link as="script" rel="preload" href="/29107295-b32bd7b51275495551b1.js"/><link as="script" rel="preload" href="/6b3c31c87dfb891be24ad1723dc89e8083e2fee1-3aeae6b2992d33209c1a.js"/><link as="script" rel="preload" href="/component---src-templates-post-js-86eb8f5f76ee576dcf22.js"/><link as="fetch" rel="preload" href="/page-data/posts/leveraging-kafka-streams-to-reduce-db-hits/page-data.json" crossorigin="anonymous"/><link as="fetch" rel="preload" href="/page-data/app-data.json" crossorigin="anonymous"/></head><body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-T9CS925" height="0" width="0" style="display: none; visibility: hidden"></iframe></noscript><div id="___gatsby"><div style="outline:none" tabindex="-1" id="gatsby-focus-wrapper"><header><h1><a href="/"><span class="Header-module--brand--nQIOf"><img src="https://wingify.com/wp-content/themes/wingify/images/labs/engg_blog.png" width="30px" height="30px" alt="Wingify Engineering"/><span style="margin-top:5px;margin-left:15px">Wingify Engineering</span></span></a></h1><nav><ul class="Header-module--main-nav--2C0n8"><li><a href="/">Posts</a></li><li><a href="/labs">Labs</a></li><li><a href="/about">About</a></li><li><a href="https://github.com/wingify">Github</a></li><li><a href="/atom.xml">Feed</a></li><li><a href="https://wingify.com/careers/" class="Header-module--we-are-hiring--3Vkey" target="_blank"><span>We are Hiring</span></a></li></ul></nav></header><main><div><h1>Leveraging Kafka Streams to reduce DB Hits</h1><img class="Bio-module--avatar--v_AB0" src="/images/team/amandeep_singh.png" alt="Amandeep Singh"/><p>Written by <strong>Amandeep Singh</strong> <div></div></p><p class="post-module--post-meta--1ci95">October 26, 2017<!-- --> — <!-- -->7<!-- --> Min Read<!-- --> <!-- --> | <span class="disqus-comment-count" data-disqus-url="https://engineering.wingify.com/posts/leveraging-kafka-streams-to-reduce-db-hits/">...</span></p><div><p>I have been working with <a href="https://kafka.apache.org/" title="** Apache Kafka**"><strong>Apache Kafka</strong></a> for more than 4 years now and have seen it evolve from a basic distributed commit log service (Something very similar to Transaction log or Operation log) to a full fledged tool for data pipelining and become the backbone of data collection platforms. For those who don’t know about Kafka, it was developed by LinkedIn, and was open sourced in early 2011. It is a distributed pub-sub messaging system that is designed to be fast, scalable and durable. Like other pub-sub messaging systems, Kafka maintains stream(s) of messages in topic(s). <strong><em>Producers</em></strong> are special processors that write data to <strong><em>Topics</em></strong> while, <strong><em>Consumers</em></strong> read from topics, to store data to extract some meaningful information that might be required at a later stage. Since Kafka is a distributed system, topics are partitioned and replicated across multiple nodes. Kafka lets you store streams of messages in a fault-tolerant way and allows processing these streams in near realtime.</p> <p>Apache Kafka has gone through various design changes since its inception, Kafka 0.9 came out with support of High Level Consumer API, which helped in removing dependency of <a href="http://zookeeper.apache.org/"><strong><em>Apache Zookeeper</em></strong></a>. It is now only used to manage metadata of topics created in Kafka. Also, in case some Kafka node goes down or rebalance is triggered due to addition of new nodes, Zookeeper runs the leader election algorithm in a fair and consistent manner. For versions less than 0.9 Apache Zookeeper was also used for managing the <strong><em>offsets</em></strong> of the consumer group. <strong><em>Offset management</em></strong> is the mechanism, which tracks the number of records that have been consumed from a partition of a topic for a particular consumer group. Kafka 0.10 came out with out of the box support for <strong>Stream Processing</strong>. This streaming platform enables capturing flow of events and changes caused by these events, and store these to other data systems such as RDBMS, key-value stores, or some warehouse depending upon use case. I was really happy and took it for a run by doing some counting aggregations. The aggregation was fast and I hardly had to write 50 lines for it. I was very happy and impressed with results. I streamed around 2 million events in around a minute on my laptop with couple of instances only. But I never got a chance to use it in production for a year or so.</p> <p>Around 3 months back when our team started stress testing backend stores by generating a lot of data, our backend stores started to give up due to the high number of insertion and updates. We didn’t have the choice to add more hardware as we were already using a lot of resources and wanted a solution that fits our current bill. Our data team had lot of discussions and I heard a lot of people talk about things like <a href="http://samza.apache.org/" title="Apache Samza"><strong><em>Apache Samza</em></strong></a>, <a href="https://spark.apache.org/" title="***Apache Spark***"><strong><em>Apache Spark</em></strong></a>, <a href="https://flink.apache.org/" title="***Apache Flink***"><strong><em>Apache Flink</em></strong></a> etc. Because, we have a small team, adding another component in technology stack was not a good idea and I didn’t want team to spend time learning about these technologies with product release around the corner. Since our data pipeline is built around Kafka, I started playing around with data. The idea was to convert multiple updates to the backend stores into a single update/insert to ensure that number of hits that our DB is taking is reduced. Since we process a lot of data, we thought about windowing our events based on time and aggregating them. I started to work on it and in matter of hours my streaming application was ready. We started with 1 minute window and we were surprised with the result. We were able to reduce DB hits by 70%. <strong>YES 70 PERCENT!!!!!!</strong></p> <p>Here are the screenshots from one of our servers that show the impact of window aggregation.</p> <div style="text-align:center; margin: 10px;"> <img src="/images/2017/10/kafka-streams-before-aggregation.png" style="box-shadow: 2px 2px 10px 1px #aaa"> <div style="margin: 10px;"><b>Before Aggregation</b></div> </div> <div style="text-align:center; margin: 10px;"> <img src="/images/2017/10/kafka-streams-after-aggregation.png" style="box-shadow: 2px 2px 10px 1px #aaa"> <div style="margin: 10px;"><b>After Aggregation</b></div> </div> <p>With streaming capabilities built into it, <strong>Apache Kafka</strong> has become one of the most powerful tool that allows you to store and aggregate data at insane speed. And we’ll see a gain in its adoption in coming years.</p> <h3 id="lets-see-how-kafka-streams-work" style="position:relative;"><a href="#lets-see-how-kafka-streams-work" aria-label="lets see how kafka streams work permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Let’s see how Kafka Streams work</h3> <p>Kafka Streams allows us to perform stream processing, hence requires some sort of internal state management. This internal state is managed in <strong>state stores which uses RocksDB</strong>. A state store can be lost on failure or fault-tolerant restored after the failure. The default implementation used by Kafka Streams DSL is a fault-tolerant state store using</p> <ul> <li>An internally created and compacted changelog topic (for fault-tolerance)</li> <li>One (or multiple) RocksDB instances (for cached key-value lookups). Thus, in case of starting/stopping applications and rewinding/reprocessing, this internal data needs to get managed correctly.</li> </ul> <h4 id="kstream-and-ktable" style="position:relative;"><a href="#kstream-and-ktable" aria-label="kstream and ktable permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>KStream and KTable</h4> <p><strong>KStream</strong> is an abstraction of a record stream of Key-Value pairs. So if you have a click stream coming in, and you are trying to aggregate session level information, the key will be session id and the other information will be the value. Similarly for URL level aggregation, a combination of URL and session will be the key.</p> <p><strong>KTable</strong> is an abstraction of a changelog stream from a primary-keyed table. Each record in this stream is an update on the primary-keyed table with the record key as the primary key. The aggregation results are stored in KTable. Intermediate aggregation uses a RocksDB instance as key-value state store that also persists to local disk. Flushing to disk happens asynchronously to keep it fast and non blocking. An internal compacted changelog topic is also created. The state store sends changes to the changelog topic in a batch, either when a default batch size has been reached or when the commit interval is reached. A pictorial representation of what happens under the hood is given below</p> <div style="text-align:center; margin: 10px;"> <img src="/images/2017/10/kafka-streams-internal-functioning.png"> <div style="margin: 10px;"> <b>Kafka Streams Internal Functioning</b><br> <i>*Image is taken from Apache Kafka documentation</i> </div> </div> <p>Kafka Streams commit the current processing progress in regular intervals. If a commit is triggered, all state stores need to flush data to disc, i.e., all internal topics needs to get flushed to Kafka. Finally, all current topic offsets are committed to Kafka. In case of failure and restart, the application can resume processing from its last commit point.</p> <h3 id="lets-understand-this-with-help-of-an-example" style="position:relative;"><a href="#lets-understand-this-with-help-of-an-example" aria-label="lets understand this with help of an example permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Let’s understand this with help of an example</h3> <p>Imagine a stream of such events coming to server for a very high traffic website. Let’s assume there is a big web gaming platform where 50K-80K concurrent users generate about 80K-120K events per second and there is a requirement to find following things:</p> <ul> <li>Number of clicks user has done in a session</li> <li>Total Pages he has viewed in a session</li> <li>Total amount of time user has spent in a session.</li> </ul> <p>Let the JSON structure be as follows:</p> <div class="gatsby-highlight" data-language="json"><pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"uuid"</span><span class="token operator">:</span><span class="token string">"user id"</span><span class="token punctuation">,</span> <span class="token property">"session_id"</span><span class="token operator">:</span> <span class="token string">"some uuid"</span><span class="token punctuation">,</span> <span class="token property">"event"</span><span class="token operator">:</span> <span class="token string">"click/page_view"</span><span class="token punctuation">,</span> <span class="token property">"time_spent"</span><span class="token operator">:</span><span class="token number">14</span> <span class="token punctuation">}</span></code></pre></div> <p>Ingestion at above mentioned pace in a DB or ensuring that these events gets stored in DB in itself is a challenge. A lot of hardware will be required to cope with this traffic as it is. Hence, it doesn’t make sense to store data directly in DB. A streaming application is a very good fit here. A streaming application is going to leverage the fact that for most of the user the clicks and page views will be concentrated in a time window. So it is possible that in 5 minutes a user might be clicking x times and giving y pageviews on an average. We can introduce a 5 minute window and club these request to form a single equivalent DB request. Hence reducing (x+y) hits to 1 hit in a window of 5 minutes. Thus reducing the traffic to 1/(x+y) of what was coming earlier.</p> <p>I have written a <a href="https://github.com/aman1064/kafka-streams-example" title=" Sample Project"><strong>Sample Kafka Streams Project</strong></a> to make it easier for you to understand. Let’s take a look at sequence diagram below. This diagram shows how various components of sample project interact with each other.</p> <div style="text-align:center; margin: 10px;"> <img src="/images/2017/10/kafka-streams-aggregation-sequence.png"> <div style="margin: 10px;"> <b>Kafka Streams Sequence Diagram</b><br> </div> </div> <p>All this flow is defined with the help of Kafka Streams DSL, the code snippet is given below</p> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token comment">//Defining Source Streams from multiple topics.</span> <span class="token class-name">KStream</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">ClickStream</span><span class="token punctuation">></span></span> clickStream <span class="token operator">=</span> kStreamBuilder<span class="token punctuation">.</span><span class="token function">stream</span><span class="token punctuation">(</span>stringSerde<span class="token punctuation">,</span> clickStreamSerde<span class="token punctuation">,</span> <span class="token class-name">Main</span><span class="token punctuation">.</span>TOPIC_PROPERTIES<span class="token punctuation">.</span><span class="token function">getProperty</span><span class="token punctuation">(</span><span class="token string">"topic.click.input"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">","</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//Kafka Streams DSL in action with filtering and cleaning logic and passing it through aggregation collector</span> clickStream <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span>v<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">(</span>v<span class="token operator">!=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token keyword">new</span> <span class="token class-name">KeyValue</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>v<span class="token punctuation">.</span><span class="token function">getSessionId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">through</span><span class="token punctuation">(</span>stringSerde<span class="token punctuation">,</span> clickStreamSerde<span class="token punctuation">,</span> <span class="token class-name">Main</span><span class="token punctuation">.</span>TOPIC_PROPERTIES<span class="token punctuation">.</span><span class="token function">getProperty</span><span class="token punctuation">(</span><span class="token string">"topic.click.output"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">groupBy</span><span class="token punctuation">(</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token operator">-></span> k<span class="token punctuation">,</span> stringSerde<span class="token punctuation">,</span> clickStreamSerde<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">aggregate</span><span class="token punctuation">(</span><span class="token class-name">ClickStreamCollector</span><span class="token operator">::</span><span class="token keyword">new</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>k<span class="token punctuation">,</span> v<span class="token punctuation">,</span> clickStreamCollector<span class="token punctuation">)</span> <span class="token operator">-></span> clickStreamCollector<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">TimeWindows</span><span class="token punctuation">.</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">,</span> collectorSerde<span class="token punctuation">,</span> <span class="token class-name">Main</span><span class="token punctuation">.</span>TOPIC_PROPERTIES<span class="token punctuation">.</span><span class="token function">getProperty</span><span class="token punctuation">(</span><span class="token string">"topic.click.aggregation"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">to</span><span class="token punctuation">(</span>windowedSerde<span class="token punctuation">,</span> collectorSerde<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">ClickStreamPartitioner</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">Main</span><span class="token punctuation">.</span>TOPIC_PROPERTIES<span class="token punctuation">.</span><span class="token function">getProperty</span><span class="token punctuation">(</span><span class="token string">"topic.click.summary"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>It’s worth noting that for each step we need to define a serializer and deserializer. In above code snippet</p> <ul> <li><strong><em>stringSerde: Defines the Serialization and Deserialization for String</em></strong></li> <li><strong><em>clickStreamSerde: Defines the Serialization and Deserialization for Raw click Data</em></strong></li> <li><strong><em>collectorSerde: Defines the Serialization and Deserialization for RocksDB intermediate storage.</em></strong></li> <li><strong><em>windowedSerde: Defines the serialization and Deserialization for Kafka Windowed Aggregation storage</em></strong></li> </ul> <p>Its very easy to implement streams over Kafka and it can be leveraged to reduce the DB traffic and for other applications, where windowing or sessionization makes sense. You can play around with this project and in case you want to reach out to me or have any doubt please drop your queries in comments section.</p> <p><strong>Happy Streaming..!</strong></p></div><hr/><div class="post-module--post-meta--1ci95"><div class="SocialLinks-module--social-links--1RDpJ"><div role="button" tabindex="0" class="SocialMediaShareButton SocialMediaShareButton--reddit"><div style="width:32px;height:32px"><svg viewBox="0 0 64 64" width="32" height="32" class="social-icon social-icon--reddit "><g><circle cx="32" cy="32" r="31" fill="#5f99cf"></circle></g><g><path d="m 52.8165,31.942362 c 0,-2.4803 -2.0264,-4.4965 -4.5169,-4.4965 -1.2155,0 -2.3171,0.4862 -3.128,1.2682 -3.077,-2.0247 -7.2403,-3.3133 -11.8507,-3.4782 l 2.5211,-7.9373 6.8272,1.5997 -0.0102,0.0986 c 0,2.0281 1.6575,3.6771 3.6958,3.6771 2.0366,0 3.6924,-1.649 3.6924,-3.6771 0,-2.0281 -1.6575,-3.6788 -3.6924,-3.6788 -1.564,0 -2.8968,0.9758 -3.4357,2.3443 l -7.3593,-1.7255 c -0.3213,-0.0782 -0.6477,0.1071 -0.748,0.4233 L 32,25.212062 c -4.8246,0.0578 -9.1953,1.3566 -12.41,3.4425 -0.8058,-0.7446 -1.8751,-1.2104 -3.0583,-1.2104 -2.4905,0 -4.5152,2.0179 -4.5152,4.4982 0,1.649 0.9061,3.0787 2.2389,3.8607 -0.0884,0.4794 -0.1462,0.9639 -0.1462,1.4569 0,6.6487 8.1736,12.0581 18.2223,12.0581 10.0487,0 18.224,-5.4094 18.224,-12.0581 0,-0.4658 -0.0493,-0.9248 -0.1275,-1.377 1.4144,-0.7599 2.3885,-2.2304 2.3885,-3.9406 z m -29.2808,3.0872 c 0,-1.4756 1.207,-2.6775 2.6894,-2.6775 1.4824,0 2.6877,1.2019 2.6877,2.6775 0,1.4756 -1.2053,2.6758 -2.6877,2.6758 -1.4824,0 -2.6894,-1.2002 -2.6894,-2.6758 z m 15.4037,7.9373 c -1.3549,1.3481 -3.4816,2.0043 -6.5008,2.0043 l -0.0221,-0.0051 -0.0221,0.0051 c -3.0209,0 -5.1476,-0.6562 -6.5008,-2.0043 -0.2465,-0.2448 -0.2465,-0.6443 0,-0.8891 0.2465,-0.2465 0.6477,-0.2465 0.8942,0 1.105,1.0999 2.9393,1.6337 5.6066,1.6337 l 0.0221,0.0051 0.0221,-0.0051 c 2.6673,0 4.5016,-0.5355 5.6066,-1.6354 0.2465,-0.2465 0.6477,-0.2448 0.8942,0 0.2465,0.2465 0.2465,0.6443 0,0.8908 z m -0.3213,-5.2615 c -1.4824,0 -2.6877,-1.2002 -2.6877,-2.6758 0,-1.4756 1.2053,-2.6775 2.6877,-2.6775 1.4824,0 2.6877,1.2019 2.6877,2.6775 0,1.4756 -1.2053,2.6758 -2.6877,2.6758 z" fill="white"></path></g></svg></div><div class="SocialMediaShareCount"><div class="SocialLinks-module--share-count--3wXKp"></div></div></div><div role="button" tabindex="0" class="SocialMediaShareButton SocialMediaShareButton--twitter"><div style="width:32px;height:32px"><svg viewBox="0 0 64 64" width="32" height="32" class="social-icon social-icon--twitter "><g><circle cx="32" cy="32" r="31" fill="#00aced"></circle></g><g><path d="M48,22.1c-1.2,0.5-2.4,0.9-3.8,1c1.4-0.8,2.4-2.1,2.9-3.6c-1.3,0.8-2.7,1.3-4.2,1.6 C41.7,19.8,40,19,38.2,19c-3.6,0-6.6,2.9-6.6,6.6c0,0.5,0.1,1,0.2,1.5c-5.5-0.3-10.3-2.9-13.5-6.9c-0.6,1-0.9,2.1-0.9,3.3 c0,2.3,1.2,4.3,2.9,5.5c-1.1,0-2.1-0.3-3-0.8c0,0,0,0.1,0,0.1c0,3.2,2.3,5.8,5.3,6.4c-0.6,0.1-1.1,0.2-1.7,0.2c-0.4,0-0.8,0-1.2-0.1 c0.8,2.6,3.3,4.5,6.1,4.6c-2.2,1.8-5.1,2.8-8.2,2.8c-0.5,0-1.1,0-1.6-0.1c2.9,1.9,6.4,2.9,10.1,2.9c12.1,0,18.7-10,18.7-18.7 c0-0.3,0-0.6,0-0.8C46,24.5,47.1,23.4,48,22.1z" fill="white"></path></g></svg></div></div><div role="button" tabindex="0" class="SocialMediaShareButton SocialMediaShareButton--facebook"><div style="width:32px;height:32px"><svg viewBox="0 0 64 64" width="32" height="32" class="social-icon social-icon--facebook "><g><circle cx="32" cy="32" r="31" fill="#3b5998"></circle></g><g><path d="M34.1,47V33.3h4.6l0.7-5.3h-5.3v-3.4c0-1.5,0.4-2.6,2.6-2.6l2.8,0v-4.8c-0.5-0.1-2.2-0.2-4.1-0.2 c-4.1,0-6.9,2.5-6.9,7V28H24v5.3h4.6V47H34.1z" fill="white"></path></g></svg></div><div class="SocialMediaShareCount"><div class="SocialLinks-module--share-count--3wXKp"></div></div></div><div role="button" tabindex="0" class="SocialMediaShareButton SocialMediaShareButton--linkedin"><div style="width:32px;height:32px"><svg viewBox="0 0 64 64" width="32" height="32" class="social-icon social-icon--linkedin "><g><circle cx="32" cy="32" r="31" fill="#007fb1"></circle></g><g><path d="M20.4,44h5.4V26.6h-5.4V44z M23.1,18c-1.7,0-3.1,1.4-3.1,3.1c0,1.7,1.4,3.1,3.1,3.1 c1.7,0,3.1-1.4,3.1-3.1C26.2,19.4,24.8,18,23.1,18z M39.5,26.2c-2.6,0-4.4,1.4-5.1,2.8h-0.1v-2.4h-5.2V44h5.4v-8.6 c0-2.3,0.4-4.5,3.2-4.5c2.8,0,2.8,2.6,2.8,4.6V44H46v-9.5C46,29.8,45,26.2,39.5,26.2z" fill="white"></path></g></svg></div><div class="SocialMediaShareCount"><div class="SocialLinks-module--share-count--3wXKp"></div></div></div><div role="button" tabindex="0" class="SocialMediaShareButton SocialMediaShareButton--telegram"><div style="width:32px;height:32px"><svg viewBox="0 0 64 64" width="32" height="32" class="social-icon social-icon--telegram "><g><circle cx="32" cy="32" r="31" fill="#37aee2"></circle></g><g><path d="m45.90873,15.44335c-0.6901,-0.0281 -1.37668,0.14048 -1.96142,0.41265c-0.84989,0.32661 -8.63939,3.33986 -16.5237,6.39174c-3.9685,1.53296 -7.93349,3.06593 -10.98537,4.24067c-3.05012,1.1765 -5.34694,2.05098 -5.4681,2.09312c-0.80775,0.28096 -1.89996,0.63566 -2.82712,1.72788c-0.23354,0.27218 -0.46884,0.62161 -0.58825,1.10275c-0.11941,0.48114 -0.06673,1.09222 0.16682,1.5716c0.46533,0.96052 1.25376,1.35737 2.18443,1.71383c3.09051,0.99037 6.28638,1.93508 8.93263,2.8236c0.97632,3.44171 1.91401,6.89571 2.84116,10.34268c0.30554,0.69185 0.97105,0.94823 1.65764,0.95525l-0.00351,0.03512c0,0 0.53908,0.05268 1.06412,-0.07375c0.52679,-0.12292 1.18879,-0.42846 1.79109,-0.99212c0.662,-0.62161 2.45836,-2.38812 3.47683,-3.38552l7.6736,5.66477l0.06146,0.03512c0,0 0.84989,0.59703 2.09312,0.68132c0.62161,0.04214 1.4399,-0.07726 2.14229,-0.59176c0.70766,-0.51626 1.1765,-1.34683 1.396,-2.29506c0.65673,-2.86224 5.00979,-23.57745 5.75257,-27.00686l-0.02107,0.08077c0.51977,-1.93157 0.32837,-3.70159 -0.87096,-4.74991c-0.60054,-0.52152 -1.2924,-0.7498 -1.98425,-0.77965l0,0.00176zm-0.2072,3.29069c0.04741,0.0439 0.0439,0.0439 0.00351,0.04741c-0.01229,-0.00351 0.14048,0.2072 -0.15804,1.32576l-0.01229,0.04214l-0.00878,0.03863c-0.75858,3.50668 -5.15554,24.40802 -5.74203,26.96472c-0.08077,0.34417 -0.11414,0.31959 -0.09482,0.29852c-0.1756,-0.02634 -0.50045,-0.16506 -0.52679,-0.1756l-13.13468,-9.70175c4.4988,-4.33199 9.09945,-8.25307 13.744,-12.43229c0.8218,-0.41265 0.68483,-1.68573 -0.29852,-1.70681c-1.04305,0.24584 -1.92279,0.99564 -2.8798,1.47502c-5.49971,3.2626 -11.11882,6.13186 -16.55882,9.49279c-2.792,-0.97105 -5.57873,-1.77704 -8.15298,-2.57601c2.2336,-0.89555 4.00889,-1.55579 5.75608,-2.23009c3.05188,-1.1765 7.01687,-2.7042 10.98537,-4.24067c7.94051,-3.06944 15.92667,-6.16346 16.62028,-6.43037l0.05619,-0.02283l0.05268,-0.02283c0.19316,-0.0878 0.30378,-0.09658 0.35471,-0.10009c0,0 -0.01756,-0.05795 -0.00351,-0.04566l-0.00176,0zm-20.91715,22.0638l2.16687,1.60145c-0.93418,0.91311 -1.81743,1.77353 -2.45485,2.38812l0.28798,-3.98957" fill="white"></path></g></svg></div></div></div></div></div><nav><ul class="post-module--pagination--1F-V0"><li><a rel="prev" href="/posts/sass-inheritance-removal/">← <!-- -->Why we've removed Inheritance/Extend from SASS & you should do the same!</a></li><li><a rel="next" href="/posts/migrating-towards-yarn-webpack/">Migrating towards Yarn and Webpack<!-- -->→</a></li></ul></nav><div id="disqus_thread"></div></main><footer><div class="Footer-module--container--3Dvn1"><div><a href="https://twitter.com/wingify_engg" target="_blank" rel="noopener noreferrer">Twitter</a><a href="https://github.com/wingify" target="_blank" rel="noopener noreferrer">GitHub</a><a href="https://engineering.wingify.com/atom.xml" target="_blank" rel="noopener noreferrer">RSS</a></div><div class="Footer-module--copyright--2uglW">Copyright © Wingify. All rights reserved.</div></div></footer></div><div id="gatsby-announcer" style="position:absolute;top:0;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0" aria-live="assertive" aria-atomic="true"></div></div><script id="gatsby-script-loader">/*<![CDATA[*/window.pagePath="/posts/leveraging-kafka-streams-to-reduce-db-hits/";/*]]>*/</script><script id="gatsby-chunk-mapping">/*<![CDATA[*/window.___chunkMapping={"app":["/app-99ec7086382d867c7c8c.js"],"component---node-modules-gatsby-plugin-offline-app-shell-js":["/component---node-modules-gatsby-plugin-offline-app-shell-js-e385def15e29b6ed02a7.js"],"component---src-pages-404-js":["/component---src-pages-404-js-0c6355b7aa7c186b82f7.js"],"component---src-pages-about-js":["/component---src-pages-about-js-fc892947e3543d68cd59.js"],"component---src-pages-contact-js":["/component---src-pages-contact-js-8f1d647515267d9174b5.js"],"component---src-pages-docs-js":["/component---src-pages-docs-js-75320bb29b557bc73d6f.js"],"component---src-pages-index-js":["/component---src-pages-index-js-8f95e95b8fdd494d939f.js"],"component---src-pages-labs-js":["/component---src-pages-labs-js-ac5b96295395cd153578.js"],"component---src-templates-post-js":["/component---src-templates-post-js-86eb8f5f76ee576dcf22.js"]};/*]]>*/</script><script src="/component---src-templates-post-js-86eb8f5f76ee576dcf22.js" async=""></script><script src="/6b3c31c87dfb891be24ad1723dc89e8083e2fee1-3aeae6b2992d33209c1a.js" async=""></script><script src="/29107295-b32bd7b51275495551b1.js" async=""></script><script src="/styles-823ae8103e36ae8a7f9f.js" async=""></script><script src="/app-99ec7086382d867c7c8c.js" async=""></script><script src="/framework-9fe058f4359556db0d38.js" async=""></script><script src="/webpack-runtime-e67a7c97db3322d008c9.js" async=""></script></body></html>