<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://kvrocks.apache.org/blog</id>
    <title>Apache Kvrocks™ Blog</title>
    <updated>2026-02-27T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://kvrocks.apache.org/blog"/>
    <subtitle>Apache Kvrocks™ Blog</subtitle>
    <icon>https://kvrocks.apache.org/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Apache Kvrocks Release 2.15.0]]></title>
        <id>https://kvrocks.apache.org/blog/release-2-15-0</id>
        <link href="https://kvrocks.apache.org/blog/release-2-15-0"/>
        <updated>2026-02-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This release marks a significant step forward in Redis compatibility: the new redis-databases option closes the gap of SELECT command between Kvrocks and Redis. Applications that rely on Redis's multi-database model can now migrate to Kvrocks without any code changes.]]></summary>
        <content type="html"><![CDATA[<p>This release marks a significant step forward in Redis compatibility: the new <code>redis-databases</code> option closes the gap of SELECT command between Kvrocks and Redis. Applications that rely on Redis's multi-database model can now migrate to Kvrocks without any code changes.</p><p>Beyond that, this release expands TimeSeries and TDigest command coverage and tightens replication reliability — all while upgrading the C++ standard to C++20 and RocksDB to v10.10.1.</p><p><strong>Highlights:</strong></p><ul><li><strong>Support SELECT database</strong> — Kvrocks can now emulate Redis's multi-database model, unblocking migrations from Redis deployments that use <code>SELECT</code> to switch between logical databases</li><li><strong>New TimeSeries commands</strong> — TS.MREVRANGE, TS.QUERYINDEX, and TS.ALTER round out the time-series API surface for analytics-heavy workloads</li><li><strong>New TDigest commands</strong> — TDIGEST.RANK, TDIGEST.REVRANK, TDIGEST.BYRANK, and TDIGEST.BYREVRANK enable percentile and rank queries on streaming data</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="support-select-database">Support SELECT Database<a href="#support-select-database" class="hash-link" aria-label="Direct link to Support SELECT Database" title="Direct link to Support SELECT Database">​</a></h3><p>Kvrocks uses a namespace mechanism as its multi-tenancy primitive, which differs from Redis's numbered logical databases. Before this release, issuing <code>SELECT</code> against Kvrocks was a no-op — the command was accepted but had no effect. v2.15.0 introduces the <code>redis-databases</code> option (<a href="https://github.com/apache/kvrocks/pull/3294" target="_blank" rel="noopener noreferrer">#3294</a>) to bridge this gap and make <code>SELECT</code> behave as it does in Redis.</p><p>The option takes a numeric value:</p><ul><li><code>redis-databases 0</code> — default; <code>SELECT</code> remains a no-op (existing namespace behavior is unchanged)</li><li><code>redis-databases N</code> (N &gt; 0) — enables Redis-style database switching with N logical databases; requires that no namespaces are configured</li></ul><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># kvrocks.conf</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">redis</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">databases 16</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Once enabled, clients can issue <code>SELECT</code> as they would against Redis:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SELECT </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># OK</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:6666</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> SET mykey </span><span class="token string" style="color:#e3116c">"hello"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># OK — stored in database 3</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="new-timeseries-commands">New TimeSeries Commands<a href="#new-timeseries-commands" class="hash-link" aria-label="Direct link to New TimeSeries Commands" title="Direct link to New TimeSeries Commands">​</a></h3><p>Kvrocks's RedisTimeSeries compatibility layer gains three commands that are essential for production time-series pipelines. <code>TS.MREVRANGE</code> lets you query multiple series in reverse chronological order in a single round-trip. <code>TS.QUERYINDEX</code> filters series by label predicates without pulling data. <code>TS.ALTER</code> modifies retention and label metadata on existing series without recreating them.</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Create series with labels</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.CREATE metrics:us-east:1 LABELS region us-east</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.CREATE metrics:us-east:2 LABELS region us-east</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.CREATE temperature:NYC LABELS sensor temperature location NYC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Add samples</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TS.ADD metrics:us-east:1 * </span><span class="token number" style="color:#36acaa">42</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.ADD metrics:us-east:2 * </span><span class="token number" style="color:#36acaa">87</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Query the last 10 samples across all "region=us-east" series, newest first</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.MREVRANGE - + FILTER </span><span class="token assign-left variable" style="color:#36acaa">region</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">us-east COUNT </span><span class="token number" style="color:#36acaa">10</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Find series matching a label filter (no data returned)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.QUERYINDEX </span><span class="token assign-left variable" style="color:#36acaa">sensor</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">temperature </span><span class="token assign-left variable" style="color:#36acaa">location</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">NYC</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Change retention period on an existing series</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TS.ALTER temperature:NYC RETENTION </span><span class="token number" style="color:#36acaa">86400000</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="new-tdigest-commands">New TDigest Commands<a href="#new-tdigest-commands" class="hash-link" aria-label="Direct link to New TDigest Commands" title="Direct link to New TDigest Commands">​</a></h3><p>The TDigest sketch — useful for approximate quantile computation over streaming data — now supports rank-based queries. <code>TDIGEST.RANK</code> returns the estimated rank of a value within the digest. <code>TDIGEST.BYRANK</code> returns the value at a given rank. The <code>REV</code> variants work in descending order. Together these four commands make it practical to answer questions like "what percentile is this latency?" or "what is the p99 value?" directly from Kvrocks.</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Create a TDigest key</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TDIGEST.CREATE latency</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Insert observations</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TDIGEST.ADD latency </span><span class="token number" style="color:#36acaa">120</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">95</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">200</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">340</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">88</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">150</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># What rank is the value 150?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TDIGEST.RANK latency </span><span class="token number" style="color:#36acaa">150</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># What value sits at rank 4?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TDIGEST.BYRANK latency </span><span class="token number" style="color:#36acaa">4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># What value sits at rank 4 counting from the top?</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1:666</span><span class="token operator file-descriptor important" style="color:#393A34">6</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> TDIGEST.BYREVRANK latency </span><span class="token number" style="color:#36acaa">4</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="end">End<a href="#end" class="hash-link" aria-label="Direct link to End" title="Direct link to End">​</a></h2><p>Except for that, this release also includes a number of bug fixes and performance improvements. For the full list of changes, please refer to <a href="https://github.com/apache/kvrocks/releases/tag/v2.15.0" target="_blank" rel="noopener noreferrer">changelog</a> to see all the details. We encourage users to upgrade to v2.15.0 to take advantage of these new features and improvements. As always, we welcome feedback and contributions from the community!</p>]]></content>
        <author>
            <name>Hulk Lin</name>
            <uri>https://github.com/git-hulk</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[KQIR: a query engine for Apache Kvrocks that supports both SQL and RediSearch queries]]></title>
        <id>https://kvrocks.apache.org/blog/kqir-query-engine</id>
        <link href="https://kvrocks.apache.org/blog/kqir-query-engine"/>
        <updated>2024-06-02T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Intro]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="intro">Intro<a href="#intro" class="hash-link" aria-label="Direct link to Intro" title="Direct link to Intro">​</a></h2><p>TL;DR:</p><p><img loading="lazy" alt="demo" src="/assets/images/demo-bf51c4ef55445434fbb4aaef6157ec67.png" width="3116" height="2596" class="img_ev3q"></p><p>Pretty cool, right? Let's dive in!</p><p>(The full example is provided in <a href="/blog/kqir-query-engine/#try-it">the final section</a>.)</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="apache-kvrocks">Apache Kvrocks<a href="#apache-kvrocks" class="hash-link" aria-label="Direct link to Apache Kvrocks" title="Direct link to Apache Kvrocks">​</a></h3><p><a href="https://kvrocks.apache.org/" target="_blank" rel="noopener noreferrer">Apache Kvrocks</a> is a <a href="https://redis.io/" target="_blank" rel="noopener noreferrer">Redis</a>-compatible database built on <a href="https://rocksdb.org/" target="_blank" rel="noopener noreferrer">RocksDB</a>.</p><p>It supports <a href="https://redis.io/docs/latest/develop/reference/protocol-spec/" target="_blank" rel="noopener noreferrer">the RESP protocol</a> (version 2 and 3) and <a href="/docs/supported-commands">a wide range of Redis commands</a>, encompassing core data structures like Strings, Sets, Hashes, Sorted Sets, Stream, GEO, as well as Lua Scripts, Transactions, <a href="https://redis.io/docs/latest/develop/interact/programmability/functions-intro/" target="_blank" rel="noopener noreferrer">Functions</a> and even <a href="https://redis.io/docs/latest/develop/data-types/probabilistic/bloom-filter/" target="_blank" rel="noopener noreferrer">BloomFilter</a>, <a href="https://redis.io/docs/latest/develop/data-types/json/" target="_blank" rel="noopener noreferrer">JSON</a> from the Redis Stack.</p><p>Unlike Redis which stores data in memory, Kvrocks persists data on disk for improved storage capabilities without being constrained by machine memory limit.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="the-capability-to-query">The capability to query<a href="#the-capability-to-query" class="hash-link" aria-label="Direct link to The capability to query" title="Direct link to The capability to query">​</a></h3><p>In recent years, NoSQL databases have become more popular than traditional databases because they perform better, scale easily, and are more flexible for different industries.</p><p>However, many users are unwilling to abandon the essential features of SQL databases just for performance reasons.
These include ACID transactions, expressive query capabilities inherent in SQL, as well as optimization and abstraction possibilities offered by structured data and relational algebra.
Consequently, a new category of databases known as NewSQL has emerged gradually.</p><p>Kvrocks is a NoSQL database.
While not classified as NewSQL, Kvrocks aims to strike a balance between NoSQL and NewSQL paradigms:
It aims to maintain the high performance of NoSQL while also implementing transaction guarantees and supporting more complex queries.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="redisearch">RediSearch?<a href="#redisearch" class="hash-link" aria-label="Direct link to RediSearch?" title="Direct link to RediSearch?">​</a></h3><p><a href="https://github.com/RediSearch/RediSearch" target="_blank" rel="noopener noreferrer">RediSearch</a> is a Redis module that enhances Redis with query, secondary indexing, and full-text search functionalities.
While <a href="https://redis.io/docs/latest/operate/oss_and_stack/stack-with-enterprise/search/commands/" target="_blank" rel="noopener noreferrer">its Redis commands</a> begin with <code>FT.</code> (i.e. full text), it goes beyond just full-text search.</p><p>In fact, it is Redis moving closer to SQL databases:
RediSearch enables users to create structured schemas on existing Redis JSON or HASH data for index building.
Its schema supports <a href="https://redis.io/docs/latest/develop/interact/search-and-query/basic-constructs/field-and-type-options/" target="_blank" rel="noopener noreferrer">various field types</a> such as numeric, tag, geo, text, and vector - the latter two are utilized for full-text and vector searches.
Instead of SQL support, RediSearch provides <a href="https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/query_syntax/" target="_blank" rel="noopener noreferrer">a unique query syntax</a> known as the RediSearch query language.</p><p>RediSearch finds applications in various fields.
One recent application involves utilizing its vector search feature to develop retrieval-augmented generation (RAG). For instance, <a href="https://www.langchain.com/" target="_blank" rel="noopener noreferrer">LangChain</a> utilizes Redis as one of its vector database.
If Kvrocks can be compatible with RediSearch, it could benefit from these ecosystem from RediSearch.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="sql">SQL?<a href="#sql" class="hash-link" aria-label="Direct link to SQL?" title="Direct link to SQL?">​</a></h3><p>RediSearch uses a unique syntax for queries, but there are some issues to consider:</p><p>Firstly, RediSearch's schema (a.k.a. index, created with <code>FT.CREATE</code>) can be regarded as a table in an SQL database. Its query syntax also aligns semantically with SQL queries.
Given this similarity, supporting SQL should not increase significant challenges; why not include SQL support as well?</p><p>Secondly, SQL enjoys broader usage and is familiar to more individuals. It is simpler to learn at the syntax level. While developers may need time to understand RediSearch query syntax, adapting to a new SQL database often requires less effort. Furthermore, SQL offers robust support for various query features, enhanced expressive capabilities (like JOINs, subqueries, aggregations).</p><p>Finally, RediSearch query syntax suffers from some historical designs. For example, the operator precedence of AND and OR (represented by space and <code>|</code> operator in RediSearch queries) <a href="https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/query_syntax/#basic-syntax" target="_blank" rel="noopener noreferrer">varies across different dialect versions</a> (dialect 1 vs. dialect 2). This tribal knowledge might lead users to prefer established query languages.</p><p>To sum up, we believe that supporting SQL as a querying language would be a good decision.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="how-we-support-both">How we support both?<a href="#how-we-support-both" class="hash-link" aria-label="Direct link to How we support both?" title="Direct link to How we support both?">​</a></h2><p><img loading="lazy" alt="KQIR" src="/assets/images/KQIR-8c913ba89d056b0cdd4f5234436a7a56.png" width="1530" height="1814" class="img_ev3q"></p><p>To introduce SQL capability to Kvrocks, we need to design a robust architecture with scalability, maintainability, and strong query planning and optimization features.</p><p>We plan to accomplish this through <a href="https://github.com/apache/kvrocks/tree/unstable/src/search" target="_blank" rel="noopener noreferrer">KQIR</a>. In the context of Kvrocks, KQIR stands for both:</p><ol><li>The complete query engine, covering frontend language parsing, query optimization and execution, etc.</li><li>An intermediate language (IR) that traverses the entire query engine.</li></ol><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="kqir-a-multiple-level-ir">KQIR: a multiple-level IR<a href="#kqir-a-multiple-level-ir" class="hash-link" aria-label="Direct link to KQIR: a multiple-level IR" title="Direct link to KQIR: a multiple-level IR">​</a></h3><p>To support both SQL and RediSearch queries, an intermediate language (IR) is necessary to handle them consistently in subsequent processes without concern for the user's input language.</p><p>We have developed parsers for a subset of MySQL syntax and RediSearch queries, converting the resulting syntax tree into KQIR.</p><p>And KQIR is a multi-level IR that can represent query structures at various levels during optimization.
The initial transformation from the syntax tree results in Syntactical IR, a high-level representation of certain syntactic expressions.
As it undergoes processing by an IR optimizer, KQIR evolves into Planning IR, a low-level representation used to express query execution plans within the query engine.</p><p>Additionally, we will conduct semantic checks on the IR before optimization to ensure that the query is semantically correct.
This includes verifying that it does not include any undefined schemas or fields and uses the appropriate field types.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="ir-optimizer">IR Optimizer<a href="#ir-optimizer" class="hash-link" aria-label="Direct link to IR Optimizer" title="Direct link to IR Optimizer">​</a></h3><p>The KQIR optimizer consists of multiple passes, <a href="https://llvm.org/docs/Passes.html" target="_blank" rel="noopener noreferrer">a concept borrowed from LLVM</a>.
Each pass takes IR as input, conducts analysis and modifications, and generates a new IR.</p><p>Currently, the optimizer's passes are categorized into three main groups:</p><ul><li>expression passes for optimizing logical expressions like <code>AND</code>, <code>OR</code>, <code>NOT</code> operators;</li><li>numeric passes for optimizing numerical comparisons with an interval analysis (i.e. analyze the mathematical properties of numerical comparisons in terms of intervals) to enhance query optimization by eliminating unnecessary comparisons or improving comparison expressions;</li><li>planning passes for converting syntactical IR to planning IR and enhancing query plans through a cost model that selects optimal indexes and removes unnecessary sortings.</li></ul><p>Pass execution order is controlled by the pass manager.
A pass may run multiple times at different stages to simplify individual passes by combining them.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="plan-executor">Plan Executor<a href="#plan-executor" class="hash-link" aria-label="Direct link to Plan Executor" title="Direct link to Plan Executor">​</a></h3><p>The KQIR plan executor is built on the Volcano model.</p><p>Once the IR optimizer finishes all optimizations, the resulting IR becomes a planning IR. This will then be passed to the plan executor to create execution logic based on certain context corresponding to the plan operator.</p><p>Subsequently, Kvrocks retrieves query results through iterative execution.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="on-disk-indexing">On-disk indexing<a href="#on-disk-indexing" class="hash-link" aria-label="Direct link to On-disk indexing" title="Direct link to On-disk indexing">​</a></h3><p>Unlike Redis, which stores index data in memory, Kvrocks requires the construction of indexes on the disk.
This means that for any field type (e.g. tag, numeric), we need an encoding to reduce such index to RocksDB's key-values.</p><p>Furthermore, we incrementally create indexes before and after JSON or HASH commands getting executed to guarantee that query results are in real-time.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="current-status">Current status<a href="#current-status" class="hash-link" aria-label="Direct link to Current status" title="Direct link to Current status">​</a></h2><p>The KQIR functionality is currently available on the <code>unstable</code> branch, supporting commands like <code>FT.CREATE</code>, <code>FT.SEARCH</code>, and <code>FT.SEARCHSQL</code> (an extension for running SQL queries) to encourage user to test.</p><p>However, as KQIR is still in early development, compatibility cannot be guaranteed and many features remain incomplete.
Thus the upcoming release (version 2.9.0) will not include any KQIR component.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="supported-field-types">Supported field types<a href="#supported-field-types" class="hash-link" aria-label="Direct link to Supported field types" title="Direct link to Supported field types">​</a></h3><p>Currently, we only support two field types: tag and numeric.</p><p>Tag fields label each data record with multiple tags for filtering in queries.
And numeric fields hold numerical data within double-precision floating-point ranges. They allow sorting and filtering by specific numerical ranges.</p><p>In the future, we plan to expand support to include vector search and full-text search capabilities alongside other field types.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="transaction-guarantees">Transaction guarantees<a href="#transaction-guarantees" class="hash-link" aria-label="Direct link to Transaction guarantees" title="Direct link to Transaction guarantees">​</a></h3><p>Currently, the transaction guarantee of KQIR is weak, which may lead to unexpected issues during use.</p><p><a href="https://github.com/apache/kvrocks/issues/2331" target="_blank" rel="noopener noreferrer">Another project in the Kvrocks community</a> aims to enhance Kvrocks' transaction support by establishing a structured framework.
We will leverage these efforts to uphold the ACID properties of KQIR and release an official version incorporating KQIR after that.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="limitation-on-ir-optimizer">Limitation on IR optimizer<a href="#limitation-on-ir-optimizer" class="hash-link" aria-label="Direct link to Limitation on IR optimizer" title="Direct link to Limitation on IR optimizer">​</a></h3><p>Currently, KQIR does not use the cost model when optimizing record sorting.
Instead, it relies on specialized logic. This could be an area for improvement soon.</p><p>Furthermore, KQIR does not currently utilize optimizations based on runtime statistics.
Our future focus will be on integrating runtime statistics into the cost model for more precise index selection.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="relationship-with-other-features">Relationship with other features<a href="#relationship-with-other-features" class="hash-link" aria-label="Direct link to Relationship with other features" title="Direct link to Relationship with other features">​</a></h3><p>KQIR integrates well with the <a href="https://kvrocks.apache.org/docs/namespace" target="_blank" rel="noopener noreferrer">namespace</a> feature.
Any index created is restricted to the current namespace and cannot be accessed in other namespaces, aligning with how other data is accessed within the namespace.</p><p>Currently, KQIR cannot be enabled in the <a href="https://kvrocks.apache.org/docs/cluster" target="_blank" rel="noopener noreferrer">cluster mode</a>.
Cluster mode support may not be implemented in the short term, but we encourage anyone to submit discussions, design proposals, or suggestions.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="compliance">Compliance<a href="#compliance" class="hash-link" aria-label="Direct link to Compliance" title="Direct link to Compliance">​</a></h3><p>While KQIR is designed to be compatible with RediSearch at the interface level, it does not include any code from RediSearch.
As previously mentioned, KQIR features a completely new framework, and its query architecture (including parsing, optimization, execution) is independent of RediSearch.</p><p>This distinction is important due to the proprietary license under which RediSearch is released.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="high-experimental">High experimental!<a href="#high-experimental" class="hash-link" aria-label="Direct link to High experimental!" title="Direct link to High experimental!">​</a></h3><p>The current implementation of KQIR is in its early experimental stage.
We advise users to consider carefully when using KQIR functionalities in a production environment, as we do not guarantee compatibility, and there may be unexpected errors.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="future-outlook">Future outlook<a href="#future-outlook" class="hash-link" aria-label="Direct link to Future outlook" title="Direct link to Future outlook">​</a></h2><p>KQIR is currently in development, and all mentioned aspects will continue to evolve.
If you're interested, please stay updated on the progress.</p><p>Developers keen on KQIR are encouraged to get involved in the development process and join the Apache Kvrocks community.</p><p>Note that our community consists entirely of volunteers.
As an ASF community, we strive to offer an open, inclusive, and vendor-neutral environment.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="vector-search">Vector search<a href="#vector-search" class="hash-link" aria-label="Direct link to Vector search" title="Direct link to Vector search">​</a></h3><p>The design and implementation of vector search support are currently underway, which is very exciting.</p><p>In the Kvrocks community, some members have raised discussions and <a href="https://github.com/apache/kvrocks/discussions/2316" target="_blank" rel="noopener noreferrer">proposed an encoding design</a> for implementing vector search on KQIR.</p><p>As per the plan, we will initially implement an on-disk HNSW index and introduce the vector field type.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="full-text-search">Full-text search<a href="#full-text-search" class="hash-link" aria-label="Direct link to Full-text search" title="Direct link to Full-text search">​</a></h3><p>There is currently no design proposal for full-text search.</p><p>However, community members are exploring the potential of incorporating full-text indexing in KQIR via <a href="https://clucene.sourceforge.net/" target="_blank" rel="noopener noreferrer">CLucene</a> or <a href="https://github.com/pisa-engine/pisa" target="_blank" rel="noopener noreferrer">PISA</a>.</p><p>We encourage anyone interested to share their ideas or suggestions and get involved in the development and implementation.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="more-sql-features">More SQL features<a href="#more-sql-features" class="hash-link" aria-label="Direct link to More SQL features" title="Direct link to More SQL features">​</a></h3><p>In the future, we aim to progressively broaden our support for SQL features, potentially encompassing subqueries (including common table expressions), JOIN operations, aggregation functions, and other functionalities.</p><p>Our primary focus will remain on transaction processing rather than analytical tasks.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="try-it">Try it!<a href="#try-it" class="hash-link" aria-label="Direct link to Try it!" title="Direct link to Try it!">​</a></h2><p>First, we can easily set up a Kvrocks instance via Docker images.
You also have the choice to <a href="/docs/getting-started#build-and-run-kvrocks-from-source">manually build executable from the source code</a> in the <code>unstable</code> branch.</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -it -p </span><span class="token number" style="color:#36acaa">6666</span><span class="token plain">:6666 apache/kvrocks:nightly --log-dir stdout</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Then, we can connect to kvrocks locally using <code>redis-cli</code>,
and create an index named <code>testidx</code> consisting a tag field <code>a</code> and numeric field <code>b</code> with the following command:</p><div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token constant" style="color:#36acaa">FT</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">CREATE</span><span class="token plain"> testidx </span><span class="token constant" style="color:#36acaa">ON</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">PREFIX</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'test:'</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">SCHEMA</span><span class="token plain"> a </span><span class="token constant" style="color:#36acaa">TAG</span><span class="token plain"> b </span><span class="token constant" style="color:#36acaa">NUMERIC</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Next, we can add some new data using Redis JSON commands:
(Note that it is also possible to add data before running <code>FT.CREATE</code>.)</p><div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">SET</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">k1 $ </span><span class="token string" style="color:#e3116c">'{"a": "x,y", "b": 11}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">SET</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">k2 $ </span><span class="token string" style="color:#e3116c">'{"a": "y,z", "b": 22}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">SET</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain">k3 $ </span><span class="token string" style="color:#e3116c">'{"a": "x,z", "b": 33}'</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Finally, we can execute some SQL queries to get the desired results:</p><div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token constant" style="color:#36acaa">FT</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">SEARCHSQL</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'select * from testidx where a hastag "z" and b &lt; 30'</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Or an equivalent RediSearch query:</p><div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token constant" style="color:#36acaa">FT</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">SEARCH</span><span class="token plain"> testidx </span><span class="token string" style="color:#e3116c">'@a:{z} @b:[-inf (30]'</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>If you experience any issues with KQIR, please feel free to <a href="https://github.com/apache/kvrocks/issues" target="_blank" rel="noopener noreferrer">report them</a>.</p><p>Enjoy it!</p>]]></content>
        <author>
            <name>PragmaTwice</name>
            <uri>https://github.com/pragmatwice</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Apache Kvrocks 2023 In Review]]></title>
        <id>https://kvrocks.apache.org/blog/kvrocks-2023-in-review</id>
        <link href="https://kvrocks.apache.org/blog/kvrocks-2023-in-review"/>
        <updated>2024-01-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The year 2023 is a small milestone for the community in open source, and it is also a new starting point. Kvrocks successfully graduated from the incubator to become an Apache top-level project in June, which is a great affirmation for the community's health and sustainability. At the same time, there are also some exciting progress in the project and community.]]></summary>
        <content type="html"><![CDATA[<p>The year 2023 is a small milestone for the community in open source, and it is also a new starting point. <strong>Kvrocks successfully graduated from the incubator to become an Apache top-level project in June, which is a great affirmation for the community's health and sustainability</strong>. At the same time, there are also some exciting progress in the project and community.</p><p>Before we start, we would like to thanks all the contributors and users who have been supporting Apache Kvrocks. We can't make it without you!</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="project-retrospective">Project Retrospective<a href="#project-retrospective" class="hash-link" aria-label="Direct link to Project Retrospective" title="Direct link to Project Retrospective">​</a></h3><p>We collected a lot of feedback from users and developers in the discussion: <a href="https://github.com/apache/kvrocks/discussions/1226" target="_blank" rel="noopener noreferrer">2023 Planning(Want your feedback)</a>, some of them have been resolved in 2023, and some of them are still in the plan list.</p><p><strong>The key progresses in 2023:</strong></p><ul><li>JSON data structure</li><li>BloomFilter data structure</li><li>Watch/Unwatch command</li><li>Lua Functions</li><li>Replication with TLS</li><li>Allow to enable 64 bit expiration time (precision is in milliseconds instead of seconds), and the length of List/Hash/Set/ZSET/Stream can be also extended from 32 to 64 bits.</li><li>Allow to use <code>LOAD RDB</code> command to load Redis RDB file</li><li>Allow to dynamically adjust the number of Worker threads</li><li>Enable Namespace replication</li><li>Support RESTORE command</li></ul><p><strong>What we are still working on:</strong></p><ul><li>RESP3 protocol, more information can be found in <a href="https://github.com/apache/kvrocks/issues/1980" target="_blank" rel="noopener noreferrer">Tracking issue: Support RESP3 in Kvrocks</a></li><li>Kvrocks in Kubernetes, <a href="https://github.com/RocksLabs/kvrocks-operator" target="_blank" rel="noopener noreferrer">kvrocks-operator</a> is ready to test now, but still need more work to make it production-ready</li><li>Controller to manage cluster, the API part of <a href="https://github.com/RocksLabs/kvrocks-controller" target="_blank" rel="noopener noreferrer">kvrocks-controller</a> is finished, but we belive it's not enough of users before UI part is ready</li></ul><p><strong>What still in the backlog:</strong></p><ul><li>Allow to use the Raft protocol for replication</li><li>HyperLogLog</li><li>RedisGraph, no plan to support since it's deprecated by the Redis community</li><li>Semi-sync replication</li><li>Client-side caching</li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="community">Community<a href="#community" class="hash-link" aria-label="Direct link to Community" title="Direct link to Community">​</a></h2><p>In the past year, in addition to operating based on the principle of "Community over Code" of the Apache Foundation, the community maintenance also adheres to the attitude of openness, transparency, openness, inclusiveness, freedom and equality towards everyone.</p><p>In 2023, the community has many things worth sharing:</p><ul><li>Successfully graduated from the incubator to become an Apache top-level project in June</li><li>Released a total of <code>6</code> versions from <strong>2.3.0 - 2.7.0</strong></li><li>Voted for <code>5</code> new Committers, they are: xiaobiaozhao / mwish / Aleks Lozovyuk / binbin / yangshixi</li><li>The total number of contributors exceeded <code>100+</code>, which increased by <code>41</code> compared to 2022</li><li>Merged PR total number <a href="https://github.com/RocksLabs/kvrocks-operator" target="_blank" rel="noopener noreferrer">500+</a></li><li>In terms of community communication groups, the number of WeChat groups is <code>450+</code>, and the number of Slack is <code>250+</code></li></ul><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="new-logo">New Logo<a href="#new-logo" class="hash-link" aria-label="Direct link to New Logo" title="Direct link to New Logo">​</a></h2><p>The number of users in the community has increased by <code>7</code> organizations, including Opera / iFlytek / SHOPLAZZA / Netease Hangzhou Research Institute / ZTO Express / Coin Index and AHOY.</p><p><img loading="lazy" alt="New Users" src="/assets/images/new_users-49f4a8c6d21dc447e2fb46a7e7e59ffc.png" width="2220" height="1406" class="img_ev3q"></p><p>Thanks to the above users for their use and active feedback, and welcome more users to leave your usage scenarios in GitHub Issue: <a href="https://github.com/apache/kvrocks/issues/414" target="_blank" rel="noopener noreferrer">Who is using Kvrocks?</a></p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="2024-planning">2024 Planning<a href="#2024-planning" class="hash-link" aria-label="Direct link to 2024 Planning" title="Direct link to 2024 Planning">​</a></h2><p>In the past few years, the annual plan of Kvrocks has also maintained the principle of openness and transparency, and I hope to hear the voices of users and developers. Therefore, the community is open to collect needs and feedback from everyone.</p><p>Please don't hesitate to leave comments in <a href="https://github.com/apache/kvrocks/discussions/1974" target="_blank" rel="noopener noreferrer">2024 Planning(Want your feedback)</a> if you have any ideas or suggestions.</p>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kvrocks graduated as an Apache Top-Level-Project]]></title>
        <id>https://kvrocks.apache.org/blog/kvrocks-graduated-as-tlp</id>
        <link href="https://kvrocks.apache.org/blog/kvrocks-graduated-as-tlp"/>
        <updated>2023-07-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Wilmington, DE – June 28, 2023 – The Apache Software Foundation(ASF) announced Kvrocks has graduated from the incubator as a Top-Level-Project. Means that the Kvrocks community has met the Apache Foundation's requirements for The Apache Way practices, diversity, and open communication. Graduation marks a new starting point, while much work is still necessary for the community's long-term health.]]></summary>
        <content type="html"><![CDATA[<p><a href="https://news.apache.org/foundation/entry/the-apache-software-foundation-announces-new-top-level-project-apache-kvrocks" target="_blank" rel="noopener noreferrer">Wilmington, DE – June 28, 2023 – The Apache Software Foundation(ASF) announced Kvrocks has graduated from the incubator as a Top-Level-Project.</a> Means that the Kvrocks community has met the Apache Foundation's requirements for The Apache Way practices, diversity, and open communication. Graduation marks a new starting point, while much work is still necessary for the community's long-term health.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="kvrocks-overview--advantages">Kvrocks Overview &amp; Advantages<a href="#kvrocks-overview--advantages" class="hash-link" aria-label="Direct link to Kvrocks Overview &amp; Advantages" title="Direct link to Kvrocks Overview &amp; Advantages">​</a></h3><p>Kvrocks is a distributed key-value NoSQL database that uses RocksDB as its storage engine and supports the Redis protocol. Compared to Redis, Kvrocks allows users to decrease memory costs and increase capacity.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="kvrocks-feature-highlights">Kvrocks Feature Highlights<a href="#kvrocks-feature-highlights" class="hash-link" aria-label="Direct link to Kvrocks Feature Highlights" title="Direct link to Kvrocks Feature Highlights">​</a></h3><ul><li>Redis Compatible: Support common Redis data types and commands;</li><li>Namespace: Similar to Redis DB but equipped with a token per namespace;</li><li>Replication: Async replication using WAL of RocksDB;</li><li>High Availability: Support Redis sentinel to failover when master failed; and</li><li>Cluster: Centralized management but accessible via any Redis cluster client.</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="additional-resources">ADDITIONAL RESOURCES<a href="#additional-resources" class="hash-link" aria-label="Direct link to ADDITIONAL RESOURCES" title="Direct link to ADDITIONAL RESOURCES">​</a></h3><ul><li>GitHub: <a href="https://github.com/apache/kvrocks" target="_blank" rel="noopener noreferrer">https://github.com/apache/kvrocks</a></li><li>Download: <a href="https://kvrocks.apache.org/download" target="_blank" rel="noopener noreferrer">https://kvrocks.apache.org/download</a></li><li>Docs: <a href="https://kvrocks.apache.org/" target="_blank" rel="noopener noreferrer">https://kvrocks.apache.org/</a></li><li>Contribute: <a href="https://kvrocks.apache.org/community/contributing" target="_blank" rel="noopener noreferrer">https://kvrocks.apache.org/community/contributing</a></li></ul><p>Since being open-sourced in 2019, Kvrocks has been serving as an alternative for Redis in massive data scenarios. Many companies are deploying and using Kvrocks in the production environment, such as Baidu, Circl.lu, Ctrip, Meitu, Opera, U-Next and Xueqiu, among others.</p><p>To serve users better, Kvrocks plans to add Kubernetes deployment support, the controller to make the cluster easier to maintain and operate, and add more data structures for fulfilling the user requirements.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="incubation-processes">Incubation processes<a href="#incubation-processes" class="hash-link" aria-label="Direct link to Incubation processes" title="Direct link to Incubation processes">​</a></h3><p>Kvrocks joined the Apache Incubator at the end of April 2022 and officially became an Apache top-level project in June 2023. There has been a significant increase in community activity</p><ol><li>The number of contributors has increased from <strong>27</strong> to <strong>82</strong></li><li>Released <strong>4</strong> minor versions during incubation: 2.1.0 / 2.2.0 / 2.3.0 / 2.4.0</li><li>Created <strong>900+</strong> Pull Requests</li><li>Created <strong>300+</strong> Issues</li><li>Nominated <strong>4</strong> Committers: PragmaTwice / Torwig / Ruixiang Tan / Xiaobiao Zhao, and PragmaTwice and Torwig are also PMC members now.</li></ol><p><img loading="lazy" alt="image" src="/assets/images/contributions-2d8cea294fa9f11ce498290c46715733.png" width="1780" height="494" class="img_ev3q"></p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="our-users">Our Users<a href="#our-users" class="hash-link" aria-label="Direct link to Our Users" title="Direct link to Our Users">​</a></h3><p>It's worth noting that the majority of Kvrocks community contributors and committers are from the community users. In addition to using, they also help make the community better in their own way. Huge thanks to every user, contributor and committer.</p><p><img loading="lazy" alt="image" src="/assets/images/users-a425130ebbe91fe07ee9b0dfbd6adae2.jpg" width="1834" height="1126" class="img_ev3q"></p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="mentor--pmc-words">Mentor &amp; PMC Words<a href="#mentor--pmc-words" class="hash-link" aria-label="Direct link to Mentor &amp; PMC Words" title="Direct link to Mentor &amp; PMC Words">​</a></h3><p><strong>@Liang Chen (Champion, Apache Member, Apache Incubator Mentor):</strong> Congratulations to Kvrocks for becoming an ASF Top Project. Kvrocks community has become an outstanding NoSQL DB open source project in the Big Data ecosystem after more than 1 year of hard work and incubation in accordance with The Apache Way.</p><p><strong>@Xiaoqiao He (Mentor, Apache Member, Apache Incubator Mentor):</strong> Congratulations to Kvrocks on its successful graduation from the Apache Incubator. It was a pleasure to participate and witness Kvrocks successfully apply The Apache Way to community operations and project evolution after more than a year of incubation run and graduation. Graduation means a new beginning, I wish Kvrocks continues to build and prosper the data ecosystem, and look forward to Kvrocks creating more value.</p><p><strong>@tison (Mentor, Apache Member, Apache Incubator Mentor):</strong> Congratulations to Kvrocks, my first mentor-incubated project, for graduating from the incubator. I'm excited to help the Kvrocks community grow under the guidance of The Apache Way, and to see how quickly the diversity of Kvrocks users and developers has grown over the past year. Graduating from the incubator is a small step for Kvrocks, but also a big step for all community members. Thank you and congratulations to everyone who has contributed to the development of Kvrocks!</p><p><strong>@Yaroslav Stepanchuk (Kvrocks PMC Member): </strong>It has been approximately a year since I first joined the Kvrocks community. From the very beginning, I was welcomed into a supportive environment, which made it incredibly easy for me, a complete newcomer to open-source, to make my initial contributions. Witnessing the project's journey and observing its current state fills me with pride. The fact that Kvrocks has successfully graduated from the Apache Incubator is a remarkable accomplishment that can be credited to all the contributors and users involved in the project. Reflecting on this milestone, I am truly amazed by the power of open-source collaboration. I firmly believe that the project's graduation will provide an additional boost, serving as a source of inspiration and leading to the expansion of our already exceptional community.</p><p><strong>@Hulk Lin (Kvrocks PMC Member):</strong> As one of the community maintainers, I am very happy that Kvrocks has become a top project in Apache after more than a year of incubation, and I am honored to work with many excellent contributors from home and abroad to maintain the project and the community. Graduation is just a new starting point, and I'm looking forward to having more great people join us.</p><p><strong>@Yuan Wang (Kvrocks PMC Member):</strong> We're excited to graduate from the incubator and are happy to be working on Kvrocks with great contributors in the community, and we'll continue to pay attention to the feedback from the community to polish and optimize the product to make Kvrocks more stable and easy to use.</p><p><strong>@PragmaTwice (Kvrocks PMC Member):</strong> The ASF community's volunteer model and the community over code credo have helped me understand more about open source and make me feel very comfortable in the community. Coincidentally, Kvrocks graduated almost at the same time as me, so congratulations to me and the community!</p><p><strong>@DongHui Liu (Kvrocks PMC Member):</strong> We are happy to witness the whole journey of Kvrocks from the initial open source to the incubation in Apache to the successful graduation, and we are honored to maintain the Kvrocks project with the excellent contributors in the community. Graduation is an important milestone and the beginning of a new journey, we will continue to focus on our product capabilities and make Kvrocks better for the community. Good luck with Kvrocks!</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="thanks">Thanks<a href="#thanks" class="hash-link" aria-label="Direct link to Thanks" title="Direct link to Thanks">​</a></h3><ul><li>Thanks to the great efforts of every contributor, to make it possible for Kvrocks becoming a top-level project;</li><li>Thank you to every Release Manager and to those who have helped Kvrocks release process. It is a great opportunity for community members to learn and practice the Apache Way from the release process;</li><li>Thanks to the incubator mentors for their guidance and help in helping community members understand Apache's philosophy and code of conduct. In addition to being heavily involved in code development and community building;</li><li>Finally, thank you to all users, your feedback and suggestions have had a significant impact on the development of Kvrocks, and this is what keeps the community going!</li></ul><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="the-end">The End<a href="#the-end" class="hash-link" aria-label="Direct link to The End" title="Direct link to The End">​</a></h3><p>Apache Kvrocks has significant potential for further development in terms of community influence and feature iteration. We work on maintaining an open and friendly atmosphere based on The Apache Way and attracting more excellent contributors. In the future, we plan to support more data types and optimize the experience of using Kvrocks in container environments. Additionally, we plan to simplify the operation and management of clusters.</p>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Getting started with Kvrocks and go-redis]]></title>
        <id>https://kvrocks.apache.org/blog/go-redis-kvrocks-opentelemetry</id>
        <link href="https://kvrocks.apache.org/blog/go-redis-kvrocks-opentelemetry"/>
        <updated>2022-11-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Learn how to use go-redis client to get started with Apache Kvrocks, a distributed key-value NoSQL database.]]></summary>
        <content type="html"><![CDATA[<p>Learn how to use go-redis client to get started with Apache Kvrocks, a distributed key-value NoSQL database.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-is-kvrocks">What is Kvrocks?<a href="#what-is-kvrocks" class="hash-link" aria-label="Direct link to What is Kvrocks?" title="Direct link to What is Kvrocks?">​</a></h2><p><a href="https://kvrocks.apache.org/" target="_blank" rel="noopener noreferrer">Apache Kvrocks</a> is a distributed key-value NoSQL database that uses RocksDB as a storage engine and is compatible with Redis protocol.</p><p>You can use Kvrocks as a drop-in replacement for Redis to store data on SSD, reducing storage costs and increasing capacity. For example, imagine taking one of the many existing Redis-based job queues and using them with Kvrocks and SSD storage.</p><p>Kvrocks supports most <a href="/docs/supported-commands">Redis commands</a> including the <code>watch</code> command starting from v2.4.0. <a href="/docs/cluster">Kvrocks Cluster</a> and <a href="/docs/replication">replication</a> are available as well.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="getting-started-with-kvrocks">Getting started with Kvrocks<a href="#getting-started-with-kvrocks" class="hash-link" aria-label="Direct link to Getting started with Kvrocks" title="Direct link to Getting started with Kvrocks">​</a></h2><p>You can launch Kvrocks using Docker:</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run -it -p </span><span class="token number" style="color:#36acaa">6666</span><span class="token plain">:6666 apache/kvrocks</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>And start using it right away:</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">redis-cli -p 6666</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">127.0.0.1:6666&gt; get foo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">(nil)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">127.0.0.1:6666&gt; set foo bar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">OK</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">127.0.0.1:6666&gt; get foo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"bar"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>You can also <a href="https://github.com/apache/kvrocks#build-and-run-kvrocks" target="_blank" rel="noopener noreferrer">build</a> Kvrocks with GCC yourself.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="connecting-to-kvrocks-from-go">Connecting to Kvrocks from Go<a href="#connecting-to-kvrocks-from-go" class="hash-link" aria-label="Direct link to Connecting to Kvrocks from Go" title="Direct link to Connecting to Kvrocks from Go">​</a></h2><p>Since Kvrocks uses Redis-compatible protocol, you can use your favorite Redis client to work with Kvrocks, for example, <a href="https://redis.uptrace.dev/guide/go-redis.html" target="_blank" rel="noopener noreferrer">Go Redis client</a>:</p><div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"context"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"github.com/redis/go-redis/v9"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ctx </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Background</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rdb </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">NewClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Options</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        Addr</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"localhost:6666"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> rdb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"value"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Err</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">panic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    val</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> rdb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Result</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">panic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    fmt</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"key"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> val</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Pipelines, pub/sub, and even Lua scripts are working as well:</p><div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> incrBy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">NewScript</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">`</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">local key = KEYS[1]</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">local change = ARGV[1]</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">local value = redis.call("GET", key)</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">if not value then</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">  value = 0</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">end</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">value = value + change</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">redis.call("SET", key, value)</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">return value</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>You can then run the script like this:</p><div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">keys </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"my_counter"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">values </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token keyword" style="color:#00009f">interface</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">{</span><span class="token operator" style="color:#393A34">+</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">num</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> incrBy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rdb</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> keys</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> values</span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="monitoring-kvrocks">Monitoring Kvrocks<a href="#monitoring-kvrocks" class="hash-link" aria-label="Direct link to Monitoring Kvrocks" title="Direct link to Monitoring Kvrocks">​</a></h2><p>Monitoring Kvrocks performance is crucial to ensure optimal operation and identify any potential bottlenecks or issues.</p><p>OpenTelemetry provides instrumentation libraries and integrations for monitoring Kvrocks using its unified observability framework.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-is-opentelemetry">What is OpenTelemetry?<a href="#what-is-opentelemetry" class="hash-link" aria-label="Direct link to What is OpenTelemetry?" title="Direct link to What is OpenTelemetry?">​</a></h3><p><a href="https://opentelemetry.io/" target="_blank" rel="noopener noreferrer">OpenTelemetry</a> is an open-source observability framework designed to standardize and simplify the collection, analysis, and export of telemetry data</p><p>OpenTelemetry allows developers to collect and export telemetry data in a vendor agnostic way. With OpenTelemetry, you can instrument your application once and then add or change vendors without changing the instrumentation, for example, here is a list <a href="https://uptrace.dev/blog/datadog-competitors.html" target="_blank" rel="noopener noreferrer">DataDog competitors</a> that support OpenTelemetry.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="what-is-uptrace">What is Uptrace?<a href="#what-is-uptrace" class="hash-link" aria-label="Direct link to What is Uptrace?" title="Direct link to What is Uptrace?">​</a></h3><p>Uptrace is an <a href="https://uptrace.dev/get/opentelemetry-apm.html" target="_blank" rel="noopener noreferrer">OpenTelemetry APM</a> that supports <a href="https://uptrace.dev/opentelemetry/distributed-tracing.html" target="_blank" rel="noopener noreferrer">distributed tracing</a>, metrics, and logs. You can use it to monitor applications and set up automatic alerts to receive notifications via email, Slack, Telegram, and more.</p><p>Uptrace stores telemetry data in ClickHouse database. ClickHouse is an open source column-oriented database management system that is designed to process large volumes of data in real-time and to provide fast analytics and reporting capabilities.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="monitoring-kvrocks-client">Monitoring Kvrocks client<a href="#monitoring-kvrocks-client" class="hash-link" aria-label="Direct link to Monitoring Kvrocks client" title="Direct link to Monitoring Kvrocks client">​</a></h3><p>You can use OpenTelemetry and Uptrace together to monitor Kvrocks performance using the go-redis instrumentation:</p><div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"github.com/redis/go-redis/extra/redisotel/v9"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rdb </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">NewClient</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Options</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Addr</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">":6666"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> redisotel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">InstrumentTracing</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rdb</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> redisotel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WithDBSystem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"kvrocks"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">panic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> redisotel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">InstrumentMetrics</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rdb</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> redisotel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WithDBSystem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"kvrocks"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">panic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Once the data reaches Uptrace, it will generate the following dashboard for you:</p><p><img loading="lazy" alt="Uptrace DB dashboard" src="/assets/images/db-dashboard-88f6c06ead1f69680176c55f13a980cc.png" width="996" height="479" class="img_ev3q"></p><p>Because OpenTelemetry provides a vendor-agnostic approach, it allows you to choose an <a href="https://uptrace.dev/blog/opentelemetry-backend.html" target="_blank" rel="noopener noreferrer">OpenTelemetry backend</a> that best suits your requirements, for example, Jaeger or Zipkin.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="monitoring-kvrocks-server">Monitoring Kvrocks server<a href="#monitoring-kvrocks-server" class="hash-link" aria-label="Direct link to Monitoring Kvrocks server" title="Direct link to Monitoring Kvrocks server">​</a></h3><p>You can also configure <a href="https://uptrace.dev/get/monitor/opentelemetry-redis.html" target="_blank" rel="noopener noreferrer">OpenTelemetry Redis receiver</a> to monitor Kvrocks:</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">receivers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">redis/kvrocks</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">endpoint</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"kvrocks:6666"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">collection_interval</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10s</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The receiver works by parsing the output of <code>INFO</code> command and produces a number of useful metrics:</p><p><img loading="lazy" alt="Redis Metrics" src="/assets/images/redis-metrics-415edd2a4992ccb450d10694aff469b7.png" width="981" height="752" class="img_ev3q"></p><p>See <a href="https://github.com/uptrace/uptrace/tree/master/example/kvrocks" target="_blank" rel="noopener noreferrer">GitHub example</a> for details.</p><p>You can also export the collected metrics to Prometheus using <a href="https://uptrace.dev/opentelemetry/prometheus-metrics.html" target="_blank" rel="noopener noreferrer">OpenTelemetry Prometheus bridge</a></p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="custom-metrics">Custom metrics<a href="#custom-metrics" class="hash-link" aria-label="Direct link to Custom metrics" title="Direct link to Custom metrics">​</a></h3><p>Using OpenTelemetry Metrics API, you can even create custom Kvrocks metrics. For example, the following function parses <code>used_disk_percent</code> to create <code>kvrocks.used_disk_percent</code> metric:</p><div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> re </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> regexp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">MustCompile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">`used_disk_percent:\s(\d+)%`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">monitorKvrocks</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Context</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rdb </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Client</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token builtin">error</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mp </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> global</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">MeterProvider</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    meter </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> mp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Meter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"github.com/uptrace/uptrace/example/kvrocks"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    usedDiskPct</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> meter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">AsyncFloat64</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Gauge</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"kvrocks.used_disk_percent"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        instrument</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">WithUnit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"%"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> err</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> meter</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">RegisterCallback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">instrument</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Asynchronous</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            usedDiskPct</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">func</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Context</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            pct</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUsedDiskPercent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rdb</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                otel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Handle</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            usedDiskPct</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Observe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> pct</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> semconv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DBSystemKey</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"kvrocks"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getUsedDiskPercent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Context</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rdb </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">redis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Client</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">float64</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    info</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> rdb</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"keyspace"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Result</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    m </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">FindStringSubmatch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> m </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> errors</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">New</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"can't find used_disk_percent metric"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    n</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> strconv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">ParseInt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">m</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">64</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> err </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> err</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">float64</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">nil</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>The metric looks like this in Uptrace:</p><p><img loading="lazy" alt="Redis Metrics" src="/assets/images/used-disk-percent-73d26545dfb630b49195f761a055b23b.png" width="963" height="707" class="img_ev3q"></p><p>See <a href="https://uptrace.dev/opentelemetry/go-metrics.html" target="_blank" rel="noopener noreferrer">OpenTelemetry Go Metrics API</a> for details.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="useful-links">Useful links<a href="#useful-links" class="hash-link" aria-label="Direct link to Useful links" title="Direct link to Useful links">​</a></h2><ul><li><a href="/docs/getting-started">Getting started with Kvrocks</a></li><li><a href="https://redis.uptrace.dev/guide/go-redis.html" target="_blank" rel="noopener noreferrer">Golang Redis</a></li><li><a href="https://uptrace.dev/get/get-started.html" target="_blank" rel="noopener noreferrer">Get started with Uptrace</a></li></ul>]]></content>
        <author>
            <name>Vladimir Mihailenco</name>
            <uri>https://github.com/vmihailenco</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How we use RocksDB in Kvrocks?]]></title>
        <id>https://kvrocks.apache.org/blog/how-we-use-rocksdb-in-kvrocks</id>
        <link href="https://kvrocks.apache.org/blog/how-we-use-rocksdb-in-kvrocks"/>
        <updated>2021-12-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Kvrocks is an open-source key-value database that is based on rocksdb and compatible with Redis protocol. Intention to decrease the cost of memory and increase the capability while compared to Redis. We would focus on how we use RocksDB features to improve the performance of the Redis on disk. Hopes this helps people who want to improve performance on RocksDB.]]></summary>
        <content type="html"><![CDATA[<p>Kvrocks is an open-source key-value database that is based on rocksdb and compatible with Redis protocol. Intention to decrease the cost of memory and increase the capability while compared to Redis. We would focus on how we use RocksDB features to improve the performance of the Redis on disk. Hopes this helps people who want to improve performance on RocksDB.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="background">Background<a href="#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background">​</a></h2><p>Let's have a look at how Kvrocks uses the RocksDB before introducing performance optimization. From the implementation side, Kvrocks would encode the Redis data structure into the key-values and write them into the different RocksDB's column families. There's five column family type in Kvrocks:</p><ul><li>Metadata Column Family: used to store the metadata(expired, size..) for complex data structures like Hash/Set/ZSet/List, also string key-value was stored in this column family</li><li>Subkey Column Family: used to store key-values for complex data structures were mentioned before</li><li>ZSetScore Column Family: only store the score of the sorted set</li><li>PubSub Column Family: used to store and propagate pubsub messages between the master and replicas</li><li>Propagated Column Family: used to propagate commands between the master and replicas</li></ul><p>Also, Kvrocks uses the RocksDB WAL to implement the replication, for more detail can see:</p><ul><li><a href="https://kvrocks.medium.com/distributed-disk-key-value-storage-kvrocks-7bc5101c8585" target="_blank" rel="noopener noreferrer">Kvrocks: An Open-Source Distributed Disk Key-Value Storage With Redis Protocol</a></li><li><a href="/community/data-structure-on-rocksdb">How to implement the Redis data structures on RocksDB</a></li></ul><p>We can have a glance at the Kvrocks architecture from 10,000 feet view：</p><p><img loading="lazy" alt="image" src="/assets/images/architecture-bc95982f349fde3491dd3e5baee0e863.jpeg" width="465" height="340" class="img_ev3q"></p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="how-to-profile-rocksdb">How to profile RocksDB<a href="#how-to-profile-rocksdb" class="hash-link" aria-label="Direct link to How to profile RocksDB" title="Direct link to How to profile RocksDB">​</a></h2><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="memtable-optimization">Memtable Optimization<a href="#memtable-optimization" class="hash-link" aria-label="Direct link to Memtable Optimization" title="Direct link to Memtable Optimization">​</a></h3><p>Currently, Kvrocks was using the SkipList Memtable. Compared with the HashSkipList Memtable, it has better performance when searching across multiple prefixes and uses less memory. Kvrocks also enabled the whole_key_filtering the option which would create a bloom filter for the key in the memtable, it can reduce the number of comparisons, thereby reducing the CPU usage during point query.
Related configuration:</p><div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">metadata_opts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memtable_whole_key_filtering </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata_opts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">memtable_prefix_bloom_size_ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="data-block-optimization">Data Block Optimization<a href="#data-block-optimization" class="hash-link" aria-label="Direct link to Data Block Optimization" title="Direct link to Data Block Optimization">​</a></h3><p>Previously, Kvrocks used binary search when searching the data block, which may cause CPU cache miss and increase the CPU usage. As the point query was the most used scenario in the key-value service, so Kvrocks switched to the hash index to reduce the binary search comparisons. <strong>Official test data shows that this feature can reduce CPU utilization by 21.8% and increase throughput by 10%, but it will increase space usage by 4.6%.</strong> Compared with disk resources, CPU resources are more expensive. Under the trade-off, Kvrocks chose to enable the Hash index to improve the efficiency of point queries.</p><p>Related configuration:</p><div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">BlockBasedTableOptions</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">data_block_index_type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DataBlockIndexType</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">kDataBlockBinaryAndHash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">BlockBasedTableOptions</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">data_block_hash_table_util_ratio </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.75</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="filterindex-block">Filter/Index Block<a href="#filterindex-block" class="hash-link" aria-label="Direct link to Filter/Index Block" title="Direct link to Filter/Index Block">​</a></h3><p>The old version of RocksDB used Bloom Filter of BlockBasedFilter type by default. The basic mechanism is to generate a Filter for every 2KB of Key-Value data, and finally form a Filter array. When searching, first check the Index Block, and for the Data Block that may have the Key, then use the corresponding Filter Block to determine whether the key exists or not.</p><p>The new version of RocksDB optimizes the original Filter mechanism by introducing Full Filter. Each SST has a Filter, which can check whether the Key exists or not in the SST and avoid reading the Index Block. However, in the scenario with a large key number in the SST, the Filter Block and Index Block will still be larger. For 256MB SST, the size of Index and Filter Block is about 0.5MB and 5MB, which is much larger than Data Block (usually 4–32KB). In the most ideal case, when the Index/Filter Block is completely stored in memory, it will only be read once per SST life cycle, but when it competes with the Data Block for the Block Cache, it is likely to be re-read from the disk due to being evicted. Do it many times, resulting in very serious read amplification.</p><p>Kvrocks' previous approach was to dynamically adjust the SST-related configuration so that the SST file will not be too large, thereby avoiding the Index/Filter Block from being too large. However, the problem with this mechanism is that when the amount of data is very large, too many SST files will take up more system resources and cause performance degradation. The new version of Kvrocks optimizes this and opens the related configuration of the Partitioned Block. The principle of the Partitioned Block is to add a secondary index to the Index/Filter Block. When reading the Index or Filter, the secondary index is first to read into the memory, and then Find the required partition Index Block according to the secondary index, and load it into the Block Cache.</p><p>The advantages of Partitioned Block are as follows:</p><ul><li>Increase the cache hit rate: Large Index/Filter Block will pollute the cache space. The large Block will be partitioned, allowing the Index/Filter Block to be loaded at a finer granularity, thereby effectively using the cache space</li><li>Improve cache efficiency: The partition Index/Filter Block will become smaller, the lock competition in the Cache will be further reduced, and the efficiency under high concurrency will be improved</li><li>Reduce IO utilization: When the cache Miss of the index/filter partition, only a small partition needs to be loaded from the disk. Compared with the Index/Filter Block that reads the entire SST file, this will make the load on the disk smaller</li></ul><p>Related configuration:</p><div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">format_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">index_type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> IndexType</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">kTwoLevelIndexSearch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">BlockBasedTableOptions</span><span class="token double-colon punctuation" style="color:#393A34">::</span><span class="token plain">partition_filters </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cache_index_and_filter_blocks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pin_top_level_index_and_filter </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">cache_index_and_filter_blocks_with_high_priority </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pin_l0_filter_and_index_blocks_in_cache </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">optimize_filters_for_memory </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="data-compression-optimization">Data compression optimization<a href="#data-compression-optimization" class="hash-link" aria-label="Direct link to Data compression optimization" title="Direct link to Data compression optimization">​</a></h3><p>RocksDB compresses the data when it's placed on the disk. We compared and tested different compression algorithms on Kvrocks and found that different compression algorithms have a great impact on performance, especially when CPU resources are tight, which will significantly increase latency.</p><p>The following figure shows the test data of compression speed and compression ratio of different compression algorithms:</p><p><img loading="lazy" alt="image" src="/assets/images/compression-e250cf7f3bdadb030040797e7d4139e9.jpeg" width="691" height="507" class="img_ev3q"></p><p>In Kvrocks, compression is not set for the SST of the L0 and L1 layers, because these two layers have a small amount of data. Compressing the data at these levels cannot reduce a lot of disk space, but not compressing the data at these levels can save CPU. Each Compaction from L0 to L1 needs to access all files in L1. In addition, the range scan cannot use Bloom Filter, and it needs to find all files in L0. If you do not need to decompress when reading data in L0 and L1, and writing data in L0 and L1 do not need to be compressed, then these two frequent CPU-intensive operations will take up less CPU, compared to the disk space saved by compression, it is more profitable.</p><p><strong>Considering the trade-off between compression speed and compression ratio, Kvrocks mainly chooses two algorithms, LZ4 and ZSTD.</strong> For other levels, LZ4 is used because the compression algorithm is faster and the compression ratio is higher. RocksDB officially recommends using LZ4. For scenes with large data volume and low QPS, the last layer will be set to ZSTD to further reduce storage space and reduce costs. The advantage of ZSTD is that the compression ratio is higher and the compression speed is faster.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="cache-optimization">Cache optimization<a href="#cache-optimization" class="hash-link" aria-label="Direct link to Cache optimization" title="Direct link to Cache optimization">​</a></h3><p>For the simple data type (String type), the data is directly stored in Metadata CF, while for complex data types, only the metadata is stored in Metadata CF, and the actual data is stored in Subkey CF. Kvrocks previously allocated the same size of Block Cache to these two CFs by default. However, the online scene is complicated and the user's data type cannot be predicted, so it is not possible to allocate an appropriate Block Cache size to each CF in advance. If users use simple types and use complex types in different proportions, the Block Cache hit rate will decrease. Kvrocks shared the same large Block Cache to achieve a 30% improvement in the command rate of the Cache.
In addition, Row Cache is also introduced to deal with the problem of hotkeys. RocksDB checks Row Cache first, then Block Cache. For scenes with hot spots, data will be stored in Row Cache first to further improve Cache utilization.</p><h3 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="key-value-separation">Key-Value Separation<a href="#key-value-separation" class="hash-link" aria-label="Direct link to Key-Value Separation" title="Direct link to Key-Value Separation">​</a></h3><p>The LSM storage engine will store the Key and Value together. During the compaction process, both Key and Value will be rewritten. When the Value is large, it will cause serious write amplification problems. In response to this problem, the  <a href="https://www.usenix.org/system/files/conference/fast16/fast16-papers-lu.pdf" target="_blank" rel="noopener noreferrer">WiscKey</a> paper proposed a Key-Value separation scheme. Based on this paper, the industry also realized the KV separation of LSM-type storage engines, such as RocksDB's BlobDB, PingCAP's Titan engine, Quantum engine used by Baidu's UNDB.</p><p>RocksDB 6.18 version re-implemented BlobDB (RocksDB's Key-Value separation scheme), integrated it into the main logic of RocksDB, and has been improving and optimizing BlobDB related features. <strong>Kvrocks introduced this feature in 2.0.5 to deal with scenarios with large values. Tests show that when Kvrocks turns on the KV separation switch, for the scenario where Value is 10KB, the write performance is increased by 2.5 times, and the read performance is not attenuated; the larger the value, the greater the write performance improvement, and the write performance is improved when the Value is 50KB. 5 times.</strong></p><p>Related configuration:</p><div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ColumnFamilyOptions</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">enable_blob_files </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config_</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">RocksDB</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">enable_blob_files</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">min_blob_size </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4096</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">blob_file_size </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">128</span><span class="token plain">M</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">blob_compression_type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> lz4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">enable_blob_garbage_collection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">blob_garbage_collection_age_cutoff </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.25</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">blob_garbage_collection_force_threshold </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.8</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="kvrocks-roadmap">Kvrocks Roadmap<a href="#kvrocks-roadmap" class="hash-link" aria-label="Direct link to Kvrocks Roadmap" title="Direct link to Kvrocks Roadmap">​</a></h2><p>2021 is coming to an end, the related work of <a href="https://github.com/apache/kvrocks/projects/1" target="_blank" rel="noopener noreferrer">Kvrocks 2.0</a> has been basically completed, and the plan of <a href="https://github.com/apache/kvrocks/projects/2" target="_blank" rel="noopener noreferrer">Kvrocks 3.0</a> has also been listed on GitHub. This article lists the following two important features.</p>]]></content>
        <author>
            <name>Hulk Lin</name>
            <uri>https://github.com/git-hulk</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[How to implement bitmap on RocksDB?]]></title>
        <id>https://kvrocks.apache.org/blog/how-to-implement-bitmap-on-rocksdb</id>
        <link href="https://kvrocks.apache.org/blog/how-to-implement-bitmap-on-rocksdb"/>
        <updated>2021-11-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Most developers should be familiar with bitmap, in addition to the storage implementation for the bloom filter, and many databases also provide bitmap type indexes. For memory storage, the bitmap can be regarded as the special type of sparse bit array, which would not cause the read-write amplification issue (means read/write bytes far more than the request). While Redis supports bit-related operations on string types, it is a big challenge for disk KV-based storage like Kvrocks. So this article mainly discusses "How to reduce disk read/write amplification on RocksDB".]]></summary>
        <content type="html"><![CDATA[<p>Most developers should be familiar with bitmap, in addition to the storage implementation for the bloom filter, and many databases also provide bitmap type indexes. For memory storage, the bitmap can be regarded as the special type of sparse bit array, which would not cause the read-write amplification issue (means read/write bytes far more than the request). While Redis supports bit-related operations on string types, it is a big challenge for disk KV-based storage like <a href="https://github.com/apache/kvrocks" target="_blank" rel="noopener noreferrer">Kvrocks</a>. So this article mainly discusses "<strong>How to reduce disk read/write amplification on RocksDB</strong>".</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="why-amplification-occurs">Why amplification occurs<a href="#why-amplification-occurs" class="hash-link" aria-label="Direct link to Why amplification occurs" title="Direct link to Why amplification occurs">​</a></h2><p>Amplification mainly comes from two aspects:</p><ul><li>The hardware-level requires the smallest reading and writing unit</li><li>How we organize the data distribution on the software-level</li></ul><p>Take SSD as an example, the smallest unit of reading/writing operation was the page (commonly 4KiB/8KiB/16KiB), and it would read or write one page even the request size was 1byte. Moreover, the way of SSD modification was Read-Modify-Write instead of in-place, which means SSD would read the page content and modify then write it out to another page, the old page would be reclaimed by GC. Similar to the following:</p><p><img loading="lazy" alt="value-update-on-page" src="/assets/images/value-update-on-page-58b38f7aacd4ab1e4663246f5e115f43.jpeg" width="489" height="238" class="img_ev3q"></p><p>As we can see, a large random io was very unfriendly to the SSD disk, except for the performance issue, frequent erasing and writing will also seriously lead to the life of the SSD (random reads and writes are also unfriendly to HDDs, requiring constant seek and addressing). LSM-Tree alleviates such problems by changing random writes into sequential batch writes.</p><p>The read-write amplification at the software level mainly comes from the data organization method, and the degree of read-write amplification brought about by different organization methods will also vary greatly. Take RocksDB as an example here, RocksDB is Facebook based on Google LevelDB which enriches the multi-threading, Backup and Compaction, and many other very useful functions. To solve the problem of the disk write amplification, it also brings some space enlargement problems. Let's take a brief look at how LSM-Tree organizes data:</p><p><img loading="lazy" alt="major-compaction" src="/assets/images/major-compaction-a24632e2a6a2da7c1ce1678a9f46230c.jpeg" width="880" height="362" class="img_ev3q"></p><p>LSM-Tree would create a new entry per write. For example in the above picture, the variable X is written 4 times successively, which are 0, 1, 2, 3. From the single variable X side, it was caused 4 times space amplification, those old spaces would be reclaimed on the background compaction. Similarly, deletion is achieved by inserting a record whose value is empty. The space size of each layer of LSM-Tree increases layer by layer. When the capacity reaches the limit, it will trigger compaction to merge to the next layer, and so on. Assuming that the maximum storage size of Level 0 is M Bytes, it increases layer by layer in 10 times and the maximum is 7 layers. Theoretically, the space enlargement is about 1.111111 times. Calculated as follows:</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">amplification ratio = (1 + 10 + 100 +1000 + 10000 + 100000 + 1000000) * M / (1000000 * M)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>However, the magnification space rate is much larger than this theoretical value since the last layer generally cannot reach the maximum value. It is also mentioned in the RocksDB documentation. For details see also RockDB's blog <a href="https://rocksdb.org/blog/2015/07/23/dynamic-level.html" target="_blank" rel="noopener noreferrer">"Dynamic Level Size for Level-Based Compaction"</a>.</p><p>In addition, since RocksDB reads and writes are all based on key-value, the larger the value, the greater the read-write amplification may be. For example, suppose there is a JSON with a Value of 10 MiB. If you want to modify a field in this key, you need to read the entire JSON, modify and write it back again, which will cause huge read-write amplification. The paper <a href="https://www.usenix.org/system/files/conference/fast16/fast16-papers-lu.pdf" target="_blank" rel="noopener noreferrer">"WiscKey: Separating Keys from Values in SSD-conscious Storage"</a> that optimizes the large Key-Value of LSM-Tree by separating key-value to reduce the write amplification problem caused by Compaction. The <a href="https://github.com/tikv/titan" target="_blank" rel="noopener noreferrer">titan</a> project of TiKV is based on WiscKey paper to optimize RocksDB's write amplification in large key-value scenarios. RocksDB also implements this function in the community version, but it is still in an experimental stage.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="implement-bitmap-on-rocksdb">Implement bitmap on RocksDB<a href="#implement-bitmap-on-rocksdb" class="hash-link" aria-label="Direct link to Implement bitmap on RocksDB" title="Direct link to Implement bitmap on RocksDB">​</a></h2><p>Kvrocks is disk storage compatible with the Redis protocol implemented on RocksDB. It needs to support the bitmap data structure, so needs to implement the bitmap on RocksDB. In most scenes, the bitmap is used as sparse arrays, which means the offset written should be random, for the first time maybe 1, and the next offset maybe 1000000000 or more. Therefore, the implementation will face the above-mentioned amplification issue.</p><p>A simple way is to regard the entire bitmap as a value, and read the value into the memory and then write it back when writing. Although this implementation is very simple, it would cause seriously amplification when the value was huge. In addition to the problem of effective space utilization, it may directly cause the entire service to be unavailable since we need to read and write back the entire value. Bitmap in Pika is such an implementation, but the maximum value is limit to 128 KiB. Limit the value size can avoid the above-mentioned extreme cases, but it will greatly affect the user scenes of bitmap.</p><p>Since we know that the core problem is caused by a single key-value that is too large, the most direct way is to split the bitmap into multiple key-values, and control the single key-value size within a reasonable range, so the amplification is relatively under control. In the current implementation of Kvrocks, each key-value is divided into 1 KiB(8192 bits). The algorithm diagram is as follows:</p><p><img loading="lazy" alt="bitmap-of-kvrocks" src="/assets/images/bitmap-of-kvrocks-f41f2bb531bc2551c745e4f5ba788c67.jpeg" width="709" height="536" class="img_ev3q"></p><p>Take <code>setbit foo 8192002 1</code> as an example, the implementation steps are:</p><ul><li>Calculate the key corresponding to the offset of <code>8192002</code>, because Kvrocks uses a value of 1 KiB, so the number of the key is <code>8192002/(1024*8)=1000</code>, so you can know the bit should be stored in the sub key <code>foo1000</code>.</li><li>Then get the value corresponding to this key from RocksDB and calculate the offset in the segment, <code>8192002%8192</code> is equal to <code>2</code>, and then set the bit with the offset of 2 to 1.</li><li>Finally, write the entire value back to RocksDB.</li></ul><p>A key point of this implementation is only read-write the limit part of the bitmap we need. Assuming that we have only executed <code>setbit</code> twice, <code>setbit foo 1 1</code> and <code>setbit foo 8192002 1</code>, then there will only read and write two keys <code>foo:0</code> and <code>foo:1000</code> in RocksDB, and the actual read-value size is only 2 KiB in total. It can be perfectly adapted to the sparse array scene like the bitmap, and will also not cause the problem of space enlargement due to sparse writing.</p><blockquote><p>This idea is also similar to Linux's virtual memory/physical memory mapping strategy. For example, we request to malloc for 1GiB, and the operating system only allocates a piece of virtual memory address space. The physical memory was allocated when it is actually written will it trigger a page fault interrupt. That is, if the memory page has not been written, read-only will not cause physical memory allocation.</p></blockquote><p>GetBit is similar. It first calculates the key where the offset is located, and then reads the key from RocksDB.</p><ul><li>If not exist, means that segment has not been written, returns 0 directly.</li><li>If exists, read the Value and return the value of the corresponding bit.</li></ul><p>In addition, the actual key-value size is also determined by the largest offset currently written. It would NOT always create a 1024 KiB key-value when there is a write. This can also help to optimize the read-write amplification problem within a single key-value in some degree. You can read <a href="https://github.com/apache/kvrocks/blob/unstable/src/types/redis_bitmap.cc" target="_blank" rel="noopener noreferrer">the source code of bitmap</a> for more details.</p><h2 class="anchor anchorWithHideOnScrollNavbar_WYt5" id="summary">Summary<a href="#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary">​</a></h2><p>It can be seen that to achieve the same thing in memory and disk was entirely different, the challenges are completely different. For disk-type services, it MUST continuously optimize the random read and write and space amplification issues. Familiar with the software was not enough, also requires to understand the hardware internal.</p>]]></content>
        <author>
            <name>Hulk Lin</name>
            <uri>https://github.com/git-hulk</uri>
        </author>
    </entry>
</feed>