<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: PSWU</title>
    <description>The latest articles on DEV Community by PSWU (@pswu11).</description>
    <link>https://dev.to/pswu11</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F478833%2F5a1a3d3e-00d9-454a-b116-1410efbef9c3.png</url>
      <title>DEV Community: PSWU</title>
      <link>https://dev.to/pswu11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pswu11"/>
    <language>en</language>
    <item>
      <title>Join Hacktoberfest 2022 and contribute to QuestDB!</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Tue, 04 Oct 2022 13:27:27 +0000</pubDate>
      <link>https://dev.to/questdb/join-hacktoberfest-2022-and-contribute-to-questdb-1f8l</link>
      <guid>https://dev.to/questdb/join-hacktoberfest-2022-and-contribute-to-questdb-1f8l</guid>
      <description>&lt;p&gt;Hacktoberfest 2022 is starting soon! We're super excited about joining&lt;br&gt;
Hackberfest again and meeting new or returning open-source contributors! 🤝&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacktoberfest
&lt;/h2&gt;

&lt;p&gt;For those who aren't familiar with Hacktoberfest, it's a month-long online celebration for open-source softwares and communities. The first 40,000 participants who &lt;a href="https://hacktoberfest.com/participation/#contributors"&gt;successfully completed the requirements&lt;/a&gt; will be rewarded with a special-edition Hacktoberfest T-shirt 👕 or a tree planted in your name. 🌴&lt;/p&gt;

&lt;p&gt;Participating in Hacktoberfest is one of our approaches to raise awareness and encourage more developers or technical writers to contribute to open source. We welcome both code and non-code contributions, such as docs improvement, tutorials, and blog posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⛳ About us
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/questdb/questdb"&gt;QuestDB&lt;/a&gt; is a high-performance open-source database for time series. The project is built from scratch in Java and C++ with no dependencies and zero garbage collection. It is optimized for high-throughput ingestion over InfluxDB line protocol and fast SQL queries. QuestDB is also one of the most popular time series databases according to the independent reviewer &lt;a href="https://db-engines.com/en/ranking/time+series+dbms"&gt;DBEngines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Developers can use QuestDB as a library for java applications. &lt;a href="https://dev.to/docs/reference/clients/overview/"&gt;Official clients&lt;/a&gt; for Python, Go, C, C++, Node.js, Rust, and .NET. are also available for the wider developer community.&lt;/p&gt;

&lt;p&gt;This year, there are three QuestDB open source projects opted in for Hacktoberfest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/questdb/questdb"&gt;&lt;strong&gt;QuestDB&lt;/strong&gt;&lt;/a&gt;: QuestDB core database, mainly written in Java and C++. Check &lt;a href="https://github.com/questdb/questdb/blob/master/CONTRIBUTING.md"&gt;CONTRIBUTING.md&lt;/a&gt; to get started.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/questdb/questdb.io"&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/a&gt;: QuestDB's website for documentation, suitable for technical writers who're experienced in Docusaurus and Markdown. Read &lt;a href="https://github.com/questdb/questdb.io#contributing"&gt;our guidelinesm for docs contributors&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/questdb/ui/tree/main/packages/web-console"&gt;&lt;strong&gt;Web console&lt;/strong&gt;&lt;/a&gt;: Monorepo that contains web console components.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🚀 Contribute to QuestDB
&lt;/h2&gt;

&lt;p&gt;If you're interested in contributing to QuestDB, make sure you read the official guidelines about &lt;a href="https://hacktoberfest.com/participation/#contributors"&gt;contributors&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://hacktoberfest.com/participation/#pr-mr-details"&gt;pull requests&lt;/a&gt;, and &lt;a href="https://hacktoberfest.com/participation/#spam"&gt;spam&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To maintain a friendly environment for both maintainers and contributors, we put together extra recommendations to increase the chance that your pull requests get accepted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay attention to &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; or any contribution guidelines available.&lt;/li&gt;
&lt;li&gt;Start with existing issues instead of inventing new ones. While new ideas are generally welcomed, they don't always fit the project roadmap.&lt;/li&gt;
&lt;li&gt;Filter issues with &lt;code&gt;good first issues&lt;/code&gt; or &lt;code&gt;help wanted&lt;/code&gt; tags if you're new to the projects.&lt;/li&gt;
&lt;li&gt;Avoid commenting on all the available issues but those you really plan to work on, so you can leave some opportunities to other contributors.&lt;/li&gt;
&lt;li&gt;Our maintainers will review your pull requests, please make sure you address all the comments before asking for another review.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎁 QuestDB swag
&lt;/h2&gt;

&lt;p&gt;In addition to the official reward, if you successfully contribute one valid pull-request to any of the QuestDB projects listed above, we offer an extra QuestDB T-shirt for you through our &lt;a href="https://dev.to/community/"&gt;swag program&lt;/a&gt;. 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--upwwPNH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://questdb.io/img/blog/2022-09-30/swag-hacktoberfest.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--upwwPNH6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://questdb.io/img/blog/2022-09-30/swag-hacktoberfest.png" alt="QuestDB Swag" width="500" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ℹ️ Get help
&lt;/h2&gt;

&lt;p&gt;If you have any questions when you're trying to contribute to QuestDB projects; here are the places to get help from our team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://slack.questdb.io"&gt;Community Slack&lt;/a&gt;; join #contributors channel.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/docs/"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/tagged/questdb"&gt;StackOverflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, don't forget to follow us on social media to receive the latest updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/questdb"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/company/questdb/"&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last but not least, star our &lt;a href="https://github.com/questdb/questdb"&gt;GitHub repo&lt;/a&gt; if you haven't!&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>java</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Importing 3m rows/sec with io_uring</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Thu, 22 Sep 2022 10:31:22 +0000</pubDate>
      <link>https://dev.to/questdb/importing-3m-rowssec-with-iouring-4h54</link>
      <guid>https://dev.to/questdb/importing-3m-rowssec-with-iouring-4h54</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is originally published on &lt;a href="https://questdb.io" rel="noopener noreferrer"&gt;questdb.io&lt;/a&gt; by &lt;a href="https://github.com/puzpuzpuz" rel="noopener noreferrer"&gt;Andrey Pechkurov&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, QuestDB’s very own &lt;a href="https://github.com/puzpuzpuz" rel="noopener noreferrer"&gt;Andrei Pechkurov&lt;/a&gt; presents how to ingest large CSV files a lot more efficiently using the SQL &lt;a href="https://questdb.io/docs/reference/sql/copy" rel="noopener noreferrer"&gt;&lt;code&gt;COPY&lt;/code&gt;&lt;/a&gt; statement, and takes us through the journey of benchmarking. Andrei also shares insights about how the new improvement is made possible by &lt;code&gt;io_uring&lt;/code&gt; and compares QuestDB's import versus several well-known OLAP and time-series databases in Clickhouse's ClickBench benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As an open source time series database company, we understand that getting your existing data into the database in a fast and convenient manner is as important as being able to &lt;a href="https://questdb.io/time-series-benchmark-suite" rel="noopener noreferrer"&gt;ingest&lt;/a&gt; and &lt;a href="https://questdb.io/blog/2022/05/26/query-benchmark-questdb-versus-clickhouse-timescale" rel="noopener noreferrer"&gt;query&lt;/a&gt; your data efficiently later on. That's why we decided to dedicate our new release, QuestDB 6.5, to the new parallel &lt;a href="https://questdb.io/docs/guides/importing-data" rel="noopener noreferrer"&gt;CSV file import&lt;/a&gt; feature. In this blog post, we discuss what parallel import means for our users and how it's implemented internally. As a bonus, we also share how recent ClickHouse team's benchmark helped us to improve both QuestDB and its demonstrated results.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ClickBench helped us to improve
&lt;/h2&gt;

&lt;p&gt;Recently ClickHouse conducted a &lt;a href="https://github.com/ClickHouse/ClickBench" rel="noopener noreferrer"&gt;benchmark&lt;/a&gt; for their own database and many others, including QuestDB. The benchmark included data import as the first step. Since we were in the process of building a faster import, this benchmark provided us with nice test data and baseline results. So, what have we achieved? Let's find out. The benchmark was using QuestDB's HTTP &lt;a href="https://questdb.io/docs/reference/api/rest#imp---import-data" rel="noopener noreferrer"&gt;import endpoint&lt;/a&gt; to ingest the data into an existing non-partitioned table. You may wonder why it doesn't use a &lt;a href="https://questdb.io/docs/concept/partitions" rel="noopener noreferrer"&gt;partitioned&lt;/a&gt; table, which stores the data sorted by the timestamp values and provides many benefits for time series analysis. Most likely, the reason is terrible import execution time. Both HTTP-based import and pre-6.5 COPY SQL command are simply not capable of importing a big CSV file with unsorted data. Thus, the benchmark opts for a non-partitioned table with no designated timestamp column. The test CSV file may be downloaded and uncompressed following the commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="s1"&gt;'https://datasets.clickhouse.com/hits_compatible/hits.csv.gz'&lt;/span&gt;
&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; hits.csv.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is on the bigger side, 76GB when decompressed, and contains rows that are heavily out-of-order in terms of time. This makes it a nice import performance challenge for any time series database. Getting the data into a locally running QuestDB instance via HTTP is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@hits.csv &lt;span class="s1"&gt;'http://localhost:9000/imp?name=hits'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such import took almost 28 minutes (1,668 seconds, to be precise) on a c6a.4xlarge EC2 instance with a 500GB gp2 volume in ClickBench. This yields around 47MB/s and leaves a lot to wish for. In contrast, it took ClickHouse database around 8 minutes (476 seconds) to import the file on the same hardware. But since we were already working on faster imports for partitioned tables, this benchmark provided us with nice test data and baseline results.&lt;/p&gt;

&lt;p&gt;In addition to import speed, ClickBench measures query performance. Although none of the queries it ran were related to time series analysis, the results helped us to improve QuestDB. We found and fixed a stability issue, as well as added support for some SQL functions. Other than that, our SQL engine had a bug around multi-threaded &lt;code&gt;min()&lt;/code&gt;/&lt;code&gt;max()&lt;/code&gt; SQL function optimization: it was case-sensitive and simply ignored &lt;code&gt;MIN()&lt;/code&gt;/&lt;code&gt;MAX()&lt;/code&gt; used in ClickBench. After a trivial fix, queries using these aggregate functions got their intended speed back. Finally, a few queries marked with N/A result were using unsupported SQL syntax and it was trivial to rewrite them to get proper results. With all of these improvements, we have run ClickBench on QuestDB 6.5.2 and created a &lt;a href="https://github.com/ClickHouse/ClickBench/pull/25" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; with the updated results.&lt;/p&gt;

&lt;p&gt;Long story short, although ClickBench has nothing to do with time series analysis, it provided us with a test CSV file and baseline import results, as well as helped us to improve query stability and performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The import speed-up
&lt;/h2&gt;

&lt;p&gt;Our new optimized import is based on the SQL &lt;code&gt;COPY&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'hits.csv'&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="s1"&gt;'EventTime'&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="s1"&gt;'yyyy-MM-dd HH:mm:ss'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command uses the new &lt;code&gt;COPY&lt;/code&gt; syntax to import the &lt;code&gt;hits.csv&lt;/code&gt; file from ClickBench to the &lt;code&gt;hits&lt;/code&gt; table. For the command to work, the file should be made available in the import root directory configured on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cairo.sql.copy.root&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\h&lt;/span&gt;ome&lt;span class="se"&gt;\m&lt;/span&gt;y-user&lt;span class="se"&gt;\m&lt;/span&gt;y-qdb-import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we care about time series data analysis, in our experiments, we partitioned it by day while the original benchmark used a non-partitioned table. Let's start with the most powerful AWS EC2 instance from the original benchmark:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-09-12%2Fcover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-09-12%2Fcover.png"&gt;&lt;/a&gt;&lt;br&gt;Ingesting a 76GB CSV file, from fast to slow: ClickHouse, QuestDB, Apache Pinot, TimescaleDB, DuckDB, and Apache Druid.
  &lt;/p&gt;

&lt;p&gt;The above benchmark compares the import speed of several well-known OLAP and time-series databases: Apache Pinot, Apache Druid, ClickHouse, DuckDB, TimescaleDB, and QuestDB. Here, our new optimized &lt;code&gt;COPY&lt;/code&gt; imports almost 1Bln rows from the &lt;code&gt;hits.csv&lt;/code&gt; file in 335 seconds, leaving a higher place in the competition only to ClickHouse.&lt;/p&gt;

&lt;p&gt;We also did a run on the c6a.4xlarge instance (16 vCPU and 32GB RAM) from the original benchmark which is noticeably less powerful than the c6a.metal instance (192 vCPU and 384GB RAM). Yet, both instances had a rather slow gp2 500GB EBS volume, the result was 17,401 seconds for the less powerful c6a.4xlarge instance. So, in spite of a very slow disk, c6a.metal is 52x faster than c6a.4xlarge. Why is that?&lt;/p&gt;

&lt;p&gt;The answer is simple. The metal instance has a huge amount of memory, so once the CSV file gets decompressed, it fits fully into the OS page cache. Hence, the import doesn't do any physical reads from the input file and instead reads the pages from the memory (note: the machine has a &lt;a href="https://en.wikipedia.org/wiki/Non-uniform_memory_access" rel="noopener noreferrer"&gt;NUMA&lt;/a&gt; architecture, but non-local memory access is still way faster than the disk reads). That's why we observe such huge difference here for QuestDB and, also, you may notice a 2.5x difference for ClickHouse in the original benchmark.&lt;/p&gt;

&lt;p&gt;You may wonder why, by removing the need to read the data from the slow disk, QuestDB makes a very noticeable improvement, while it's only 2.5x for ClickHouse and even less for other databases? We're going to explain it soon, but for now, let's continue the benchmarking fun.&lt;/p&gt;

&lt;p&gt;Honestly speaking, we find the choice of the metal instance in the ClickBench results rather synthetic, as it makes little sense to use a very powerful (and expensive) machine in combination with a very slow (and cheap) disk. So, we did a benchmark run on a different test stand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;c5d.4xlarge EC2 instance (16 vCPU and 32GB RAM), Amazon Linux 2 with 5.15.50
kernel&lt;/li&gt;
&lt;li&gt;400GB NVMe drive&lt;/li&gt;
&lt;li&gt;250GB gp3, 16K IOPS and 1GB/s throughput, or gp2 of the same size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What we got is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-09-12%2Fcomparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-09-12%2Fcomparison.png"&gt;&lt;/a&gt;&lt;br&gt;QuestDB ingestion time for ClickBench's 76GB CSV file by instance type and storage.&lt;br&gt;

  &lt;/p&gt;

&lt;p&gt;The very last result on the above chart stands for the scenario of c5d.4xlarge instance with a slow gp2 volume. We are including it to show the importance of the disk speed to the performance.&lt;/p&gt;

&lt;p&gt;In the middle of the chart, the-gp3-volume-only result doesn't use the local SSD, but manages to ingest the data into a partitioned table a lot faster than the gp2 run, thanks to the faster EBS volume. Finally, in the NVMe SSD run, the import takes less than 7 minutes - an impressive ingestion rate of 2.5Bln row/s (or 193MB/s) without having the whole input file in the OS page cache. Here, the SSD is used as a read-only storage for the CSV file, while the database files are placed on the EBS volume. This is a convenient approach for a single-time import of high volume of data. As soon as the import is done, the SSD is no longer needed, so the EBS volume may be attached to a more affordable instance where the database would run.&lt;/p&gt;

&lt;p&gt;As shown by the top result in the chart above, the optimized import makes a terrific difference for anyone who wants to import their time series data to QuestDB, but also takes us close to the ClickHouse's results from the practical perspective. Another nice property of QuestDB's import is that, as soon as the import ends, the data is laid out on disk optimally, i.e. the column files are organized in partitions and no background merging is required.&lt;/p&gt;

&lt;p&gt;Now, as we promised, we're going to explain why huge amount of RAM or a locally-attached SSD makes such a difference for QuestDB's import performance. To learn that, we're taking a leap into an engineering story full of trial and error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing the import
&lt;/h2&gt;

&lt;p&gt;Our HTTP endpoint, as well as the old &lt;code&gt;COPY&lt;/code&gt; implementation, is handling the incoming data serially (think, as a single-time stream) and uses a single thread for that. For out-of-order (O3) data, this means lots of O3 writes and, hence, partition re-writes. Both single-threaded handling and O3 writes become the limiting factor for these types of import.&lt;br&gt;
However, the &lt;code&gt;COPY&lt;/code&gt; statement operates on a file, so there is nothing preventing us from going over it as many times as needed.&lt;/p&gt;

&lt;p&gt;QuestDB's storage format doesn't involve complicated layout like the one in &lt;a href="https://en.wikipedia.org/wiki/Log-structured_merge-tree" rel="noopener noreferrer"&gt;LSM trees&lt;/a&gt; or in other similar persistent data structures. The column files are &lt;a href="https://questdb.io/docs/concept/partitions" rel="noopener noreferrer"&gt;partitioned&lt;/a&gt; by time and versioned to handle concurrent reads and writes. The advantages of this approach is that as soon as the rows are committed, the on-disk data is optimal from the read operation perspective - there is no need to go through multiple files with potentially overlapping data when reading from a single partition. The downside is that such storage format may be problematic to cope with, when it comes to data import.&lt;/p&gt;

&lt;p&gt;But no worries, that's something we have optimized.&lt;br&gt;
The big ideas we had when working on our shiny new &lt;code&gt;COPY&lt;/code&gt; are really simple. First, we should organize the import in multiple phases in order to enable in-order data ingestion. Second, we go parallel, i.e. multi-threaded, in each of those phases, where it is possible.&lt;/p&gt;

&lt;p&gt;Broadly speaking, the phases are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check input file boundaries. Here we try to split the file into N chunks, so that N worker threads may work on their own chunk in isolation.&lt;/li&gt;
&lt;li&gt;Index the input file. Each thread scans its chunk, reads designated timestamp column values, and creates temporary index files. The index files are organized in partitions and contain sorted timestamps, as well as offsets pointing to the source file.&lt;/li&gt;
&lt;li&gt;Scan the input file and import data into temporary tables. Here, the threads use the newly built indexes to go through the input file and write their own temporary tables. The scanning and subsequent writes are guaranteed to be in-order thanks to the index files containing timestamps and offsets tuples sorted by time. The parallelism in this phase comes from multiple partitions being available to the threads to work independently.&lt;/li&gt;
&lt;li&gt;Perform additional metadata manipulations (say, merge symbol tables) and, finally, move the partitions from temporary tables to the final one. This is completed in multiple smaller phases that we summarize as one, for the sake of simplicity.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The indexes we build at phase 2 may be illustrated in the following way:&lt;/p&gt;


  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-09-12%2Fdiagram.png"&gt;Temporary indexes built during parallel import.
  


&lt;p&gt;The above description is an overview of what we've done for the new &lt;code&gt;COPY&lt;/code&gt;. Yet, a careful reader might spot a potential bottleneck. Yes, the third phase involves lots of random disk reads in case of an unordered input file. That's exactly what we observed as a noticeable bottleneck when experimenting with the initial implementation. But does it mean that there is nothing we can do with this? Not really. Modern HW &amp;amp; SW to the rescue!&lt;/p&gt;
&lt;h2&gt;
  
  
  io_uring everything!
&lt;/h2&gt;

&lt;p&gt;Modern SSDs, especially NVMe ones, have evolved quite far from their spinning magnetic ancestors. They're able to cope with much higher concurrency levels for disk operations, including random read ones. But utilizing these hardware capabilities with traditional blocking interfaces, like &lt;a href="https://man7.org/linux/man-pages/man2/pwrite.2.html" rel="noopener noreferrer"&gt;&lt;code&gt;pread()&lt;/code&gt;&lt;/a&gt;, would involve many threads and, hence, some overhead here and there (like increased memory footprint or context switching). &lt;br&gt;
Moreover, QuestDB's threading model operates on a fixed-size thread pool and doesn't assume running more threads than the available CPU cores.&lt;/p&gt;

&lt;p&gt;Luckily, newer Linux kernel versions support &lt;a href="https://kernel.dk/io_uring.pdf" rel="noopener noreferrer"&gt;&lt;code&gt;io_uring&lt;/code&gt;&lt;/a&gt;, a new asynchronous I/O interface. But would it help in our case? Learning the answer is simple and, in fact, doesn't even require a single line of code, thanks to &lt;a href="https://github.com/axboe/fio" rel="noopener noreferrer"&gt;fio&lt;/a&gt;, a very flexible I/O tester utility.&lt;/p&gt;

&lt;p&gt;Let's check how blocking random reads of 4KB chunks would perform on a laptop with a decent NVMe SSD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fio &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;read_sync_4k &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hits.csv &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--rw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;randread &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4K &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--numjobs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--ioengine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--group_reporting&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60 &lt;span class="se"&gt;\&lt;/span&gt;
/
...
Run status group 0 &lt;span class="o"&gt;(&lt;/span&gt;all &lt;span class="nb"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
   READ: &lt;span class="nv"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;223MiB/s &lt;span class="o"&gt;(&lt;/span&gt;234MB/s&lt;span class="o"&gt;)&lt;/span&gt;, 223MiB/s-223MiB/s &lt;span class="o"&gt;(&lt;/span&gt;234MB/s-234MB/s&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;io&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;13.1GiB &lt;span class="o"&gt;(&lt;/span&gt;14.0GB&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60001-60001msec
Disk stats &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;/write&lt;span class="o"&gt;)&lt;/span&gt;:
  nvme0n1: &lt;span class="nv"&gt;ios&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3166224/361, &lt;span class="nv"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0/318, &lt;span class="nv"&gt;ticks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;217837/455, &lt;span class="nv"&gt;in_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;218357, &lt;span class="nv"&gt;util&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50.72%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we're using 8 threads to make blocking read calls to the same CSV file and observe 223MB/s read rate which is not bad at all.&lt;/p&gt;

&lt;p&gt;Now, we use &lt;code&gt;io_uring&lt;/code&gt; to do the same job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fio &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;read_io_uring_4k &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./hits.csv &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--rw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;randread &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4K &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--numjobs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--ioengine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;io_uring &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--iodepth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--group_reporting&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60 &lt;span class="se"&gt;\&lt;/span&gt;
/
...
Run status group 0 &lt;span class="o"&gt;(&lt;/span&gt;all &lt;span class="nb"&gt;jobs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
   READ: &lt;span class="nv"&gt;bw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2232MiB/s &lt;span class="o"&gt;(&lt;/span&gt;2340MB/s&lt;span class="o"&gt;)&lt;/span&gt;, 2232MiB/s-2232MiB/s &lt;span class="o"&gt;(&lt;/span&gt;2340MB/s-2340MB/s&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;io&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;131GiB &lt;span class="o"&gt;(&lt;/span&gt;140GB&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60003-60003msec
Disk stats &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;/write&lt;span class="o"&gt;)&lt;/span&gt;:
  nvme0n1: &lt;span class="nv"&gt;ios&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;25482866/16240, &lt;span class="nv"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6262/571137, &lt;span class="nv"&gt;ticks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;27625314/25206, &lt;span class="nv"&gt;in_queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;27650786, &lt;span class="nv"&gt;util&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;98.86%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get an impressive 2,232MB/s this time. Also, it is worth noting that disk utilization has increased to 98.86% against 50.72% in the previous fio run, all of that with the same number of threads.&lt;/p&gt;

&lt;p&gt;This simple experiment proved to us that &lt;code&gt;io_uring&lt;/code&gt; may be a great fit in our parallel &lt;code&gt;COPY&lt;/code&gt; implementation, so we added an experimental API and continued our experiments. As a result, QuestDB checks the kernel version and, if it's new enough, uses &lt;code&gt;io_uring&lt;/code&gt; to speed up the import. Our code is also smart enough to detect in-order adjacent lines and read these lines in one I/O operation. Thanks to such behavior, parallel COPY is faster than the serial counterpart even on ordered files.&lt;/p&gt;

&lt;p&gt;We have explained why presence of a NVMe SSD made such a change in our introductory benchmarks. EBS volumes are very convenient, but they show an order of magnitude less IOPS and throughput rates than a physically attached drive. Thus, using such drive for the purposes of initial data import makes a lot of sense, especially when we consider a few terabytes to be imported.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Prior to QuestDB 6.5, importing large amounts of unsorted data into a partitioned table was practically impossible. We hope that our users will appreciate this feature, as well as other improvements we've made recently. As a logical next step, we want to take our data import one step further by making it available and convenient to use in QuestDB Cloud. Finally, needless to say, we'll be thinking of more use cases for &lt;code&gt;io_uring&lt;/code&gt; in our database.&lt;/p&gt;

&lt;p&gt;As usual, we encourage you to try out the latest QuestDB 6.5.2 release and share your feedback with our &lt;a href="https://slack.questdb.io" rel="noopener noreferrer"&gt;Slack Community&lt;/a&gt;. You can also play with our &lt;a href="https://demo.questdb.io" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; to see how fast it executes your queries. And, of course, contributions to our open source &lt;a href="https://github.com/questdb/questdb" rel="noopener noreferrer"&gt;project on GitHub&lt;/a&gt; are more than welcome.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>sql</category>
      <category>timeseries</category>
    </item>
    <item>
      <title>4Bn rows/sec query benchmark: Clickhouse vs QuestDB vs Timescale</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Thu, 23 Jun 2022 13:30:34 +0000</pubDate>
      <link>https://dev.to/questdb/4bn-rowssec-query-benchmark-clickhouse-vs-questdb-vs-timescale-i9g</link>
      <guid>https://dev.to/questdb/4bn-rowssec-query-benchmark-clickhouse-vs-questdb-vs-timescale-i9g</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is originally published on &lt;a href="https://questdb.io" rel="noopener noreferrer"&gt;questdb.io&lt;/a&gt; by &lt;a href="https://github.com/puzpuzpuz" rel="noopener noreferrer"&gt;Andrey Pechkurov&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We &lt;a href="https://questdb.io/blog/2022/01/12/jit-sql-compiler" rel="noopener noreferrer"&gt;introduced&lt;/a&gt; JIT (Just-in-Time) compiler for SQL filters in our previous version, QuestDB 6.2. As we mentioned last time, the next step would be to parallelize the query execution when suitable to improve the execution time even further and that's what we're going to discuss and benchmark today. QuestDB 6.3 enables JIT compiled filters by default and, what's even more noticeable, includes parallel SQL filter execution optimization allowing us to reduce both cold and hot query execution times quite dramatically.&lt;/p&gt;

&lt;p&gt;Prior to diving into the implementation details and running some before/after benchmarks for QuestDB, we'll be having a friendly competition with two popular time series and analytical databases, TimescaleDB and ClickHouse. The purpose of the competition is nothing more but an attempt to understand whether our parallel filter execution is worth the hassle or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing with other databases
&lt;/h2&gt;

&lt;p&gt;Our test box is a c5a.12xlarge AWS VM running Ubuntu Server 20.04 64-bit. In practice, this means 48 vCPU and 96 GB RAM. The attached storage is a 1 TB gp3 volume configured for 1,000 MB/s throughput and 16,000 IOPS. Apart from that, we'll be using QuestDB 6.3.1 with the default settings which means both parallel filter execution and JIT compilation being enabled.&lt;/p&gt;

&lt;p&gt;In order to make the benchmark easily reproducible, we're going to use &lt;a href="https://github.com/timescale/tsbs" rel="noopener noreferrer"&gt;TSBS&lt;/a&gt; benchmark utilities to generate the data. We'll be using so-called IoT use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tsbs_generate_data &lt;span class="nt"&gt;--use-case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"iot"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123 &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000 &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--timestamp-start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2020-01-01T00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--timestamp-end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2020-07-01T00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--log-interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"60s"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"influx"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/data &lt;span class="se"&gt;\&lt;/span&gt;
                     /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command generates six months of per-minute measurements for 5,000 truck IoT devices. This yields almost 1.2 billion records stored in a table named &lt;code&gt;readings&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Loading the data is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tsbs_load_questdb &lt;span class="nt"&gt;--file&lt;/span&gt; /tmp/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we have the data in the database, we're going to execute the following query on the &lt;code&gt;readings&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
 &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;
 &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This (kinda synthetic) query aims to find all measurements sent from fast-moving trucks in the given location. Apart from that, it has a filter on three DOUBLE columns and doesn't include analytical clauses, like &lt;code&gt;GROUP BY&lt;/code&gt; or &lt;code&gt;SAMPLE BY&lt;/code&gt;, which is exactly what we need.&lt;/p&gt;

&lt;p&gt;Our first competitor is TimescaleDB 2.6.0 running on top of PostgreSQL 14.2. As the official installation guide suggests, we made sure to run &lt;code&gt;timescaledb-tune&lt;/code&gt; to fine-tune TimescaleDB for better performance.&lt;/p&gt;

&lt;p&gt;We generate the test data with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tsbs_generate_data &lt;span class="nt"&gt;--use-case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"iot"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123 &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5000 &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--timestamp-start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2020-01-01T00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--timestamp-end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2020-07-01T00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--log-interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"60s"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                     &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"timescaledb"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/data &lt;span class="se"&gt;\&lt;/span&gt;
                     /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the same command as before, but with the &lt;code&gt;format&lt;/code&gt; argument set to &lt;code&gt;timescaledb&lt;/code&gt;. Next, we load the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tsbs_load_timescaledb &lt;span class="nt"&gt;--pass&lt;/span&gt; your_pwd &lt;span class="nt"&gt;--file&lt;/span&gt; /tmp/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be prepared to wait for quite a while for the data to get in this time. We observed 5-8x ingestion rate difference between QuestDB and two other databases in this particular environment. Yet, that's nothing more but a note for anyone who wants to repeat the benchmark. If you'd like learn more on the ingestion performance topic, check out this &lt;a href="https://questdb.io/time-series-benchmark-suite/" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we're able to run the first query and measure the hot execution time. Yet, if we do it, it would take more than 15 minutes for TimescaleDB to execute this query. At this point, experienced TimescaleDB &amp;amp; PostgreSQL users may suggest us to add an index to speed up this concrete query. &lt;/p&gt;

&lt;p&gt;So, let's do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With an index in place, TimescaleDB can execute the query much much faster, in around 4.4 seconds. To get the full picture, let's include one more contestant.&lt;/p&gt;

&lt;p&gt;The third member of our competition is ClickHouse 22.4.1.752. Just like with TimescaleDB, the command to generate the data stays the same with only the &lt;code&gt;format&lt;/code&gt; argument being set to &lt;code&gt;clickhouse&lt;/code&gt;. Once the data is generated, it can be loaded into the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./tsbs_load_clickhouse &lt;span class="nt"&gt;--file&lt;/span&gt; /tmp/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're ready to do the benchmark run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Ffilter-benchmark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Ffilter-benchmark.png" alt="Hot query execution times of QuestDB, ClickHouse and TimescaleDB - Query 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above chart shows that QuestDB is an order of magnitude faster than both TimescaleDB and ClickHouse in this specific query.&lt;/p&gt;

&lt;p&gt;Interestingly, an index-based scan doesn't help TimescaleDB to win the competition. This is a nice illustration of the fact that a specialized parallelism-friendly storage model may save you from having to deal with indexes and paying the additional overhead during data ingestion.&lt;/p&gt;

&lt;p&gt;As the next step, let's give another popular type of query a go. In the world of time series data, it's common to query only the latest rows based on a certain filter. QuestDB supports that elegantly through negative &lt;code&gt;LIMIT&lt;/code&gt; clause values. If we were to query ten latest measurements sent from fast-moving, yet fuel-efficient trucks it would look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fuel_consumption&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;LIMIT -10&lt;/code&gt; clause in our query, it basically asks the database to return the last 10 rows that correspond to the filter. Thanks to the implicit ascending order based on the &lt;a href="https://questdb.io/docs/concept/designated-timestamp/" rel="noopener noreferrer"&gt;designated timestamp&lt;/a&gt; column, we also didn't have to specify the &lt;code&gt;ORDER BY&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;In TimescaleDB, this query would look more verbose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fuel_consumption&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we had to specify descending &lt;code&gt;ORDER BY&lt;/code&gt; and &lt;code&gt;LIMIT&lt;/code&gt; clauses. When it comes to ClickHouse, the query would look just like for TimescaleDB with the exception of another column being used to store timestamps (&lt;code&gt;created_at&lt;/code&gt; instead of &lt;code&gt;time&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;How do databases from our list deal with such query? Let's measure and find out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Ffilter-with-limit-benchmark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Ffilter-with-limit-benchmark.png" alt="A chart comparing hot LIMIT query execution times of QuestDB, ClickHouse and TimescaleDB - Query 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, surprisingly or not, TimescaleDB does a better job than ClickHouse. That's because, just like QuestDB, TimescaleDB filters the data starting with the latest time-based partitions and stops the filtering once enough rows are found. We could also add an index on the &lt;code&gt;velocity&lt;/code&gt; and &lt;code&gt;fuel_consumption&lt;/code&gt; columns, but it won't change the result. That's because TimescaleDB doesn't use the index and does a full scan instead for this query. Thanks to such behavior, both QuestDB and TimescaleDB are significantly faster than ClickHouse in the exercise.&lt;/p&gt;

&lt;p&gt;Needless to say, that both TimescaleDB and ClickHouse are great pieces of engineering. Your mileage may vary and the performance of your particular application depends on a large number of factors. So, as with any benchmark, take our results with a grain of salt and make sure to measure things on your own.&lt;/p&gt;

&lt;p&gt;That should be it for our comparison and now it's time to discuss the design decisions behind our parallel SQL filter execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;First, let's quickly recap on QuestDB's &lt;a href="https://questdb.io/docs/concept/storage-model/" rel="noopener noreferrer"&gt;storage model&lt;/a&gt; to understand why it supports efficient multi-core execution. The database has a column-based append-only storage model. Data is stored in tables, with each column stored in its own file or multiple files in case when the table is &lt;a href="https://questdb.io/docs/concept/partitions" rel="noopener noreferrer"&gt;partitioned&lt;/a&gt; by the designated timestamp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fstorage-format.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fstorage-format.png" alt="A diagram showing column file partitioning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a SQL filter (i.e. the WHERE clause) is executed, the database needs to scan the files for the corresponding filtered columns. As you may have already guessed, when the column files are large enough, or the query touches multiple partitions, filtering the records on a single thread is inefficient. Instead, the file(s) could be split into contiguous chunks (we call them "page frames"). Then, multiple threads could execute the filter on each page frame utilizing both CPU and disk resources in a much more optimal way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fhow-filtering-works.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fhow-filtering-works.png" alt="A diagram showing how parallel page frame scanning works"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We already had this optimization in place for some of the analytical types of queries, but not for full or partial table scan with a filter. So, that's basically what we've added in version 6.3.&lt;/p&gt;

&lt;p&gt;As usual, there are edge cases and hidden reefs, so the implementation is not as simple as it may sound. Say, what if your query has a filter and a LIMIT -10 clause, just like in our recent benchmark? Then the database should execute the query in parallel, fetch the last 10 records and cancel the remaining page frame filtering tasks, so that there is no useless filtering done by other worker threads. A similar cancellation should take place in the face of a closed PGWire or HTTP connection or a query execution timeout. So, as you already saw in the above comparison, we made sure to handle all of these edge cases. If you're interested in the implementation details, go check this lengthy &lt;a href="https://github.com/questdb/questdb/pull/1732" rel="noopener noreferrer"&gt;pull request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the end user perspective, this optimization is always enabled and applies to non-JIT and JIT-compiled filters. But how does it improve QuestDB's performance? Let's find out!&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed up measurements
&lt;/h2&gt;

&lt;p&gt;We'll be using the same benchmark environment as above while using a slightly different query to keep things simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;readings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fuel_consumption&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query counts the total number of measurements sent from fast-moving, yet fuel-efficient trucks.&lt;/p&gt;

&lt;p&gt;First, we focus on cold execution time, i.e. situation when the column files data is not in the OS page cache. Multi-threaded runs use QuestDB 6.3.1 while single-threaded ones use 6.2.0 version of the database. That's because JIT compilation is only available in when parallel filter execution is on starting from 6.3. The database configuration is kept default, except for the JIT being disabled or enabled in the corresponding measurements. Also notice while this given query supports JIT compilation, there is a number of &lt;a href="https://questdb.io/docs/concept/jit-compiler/#known-limitations" rel="noopener noreferrer"&gt;limitations&lt;/a&gt; for the types of the queries supported by the JIT compiler.&lt;/p&gt;

&lt;p&gt;The below chart shows the cold execution times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fbefore-and-after-cold-runs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fbefore-and-after-cold-runs.png" alt="A chart comparing cold query execution time improvements in QuestDB 6.3 - Query 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's that? Parallel filter execution is only two times faster. More than that, enabled JIT-compiled filters have almost no effect on the end result. The thing is that the disk is the bottleneck here.&lt;/p&gt;

&lt;p&gt;Let's try to make some sense out of these results. It takes around 30.7 seconds for QuestDB 6.3 to execute the query when the data is only on disk. The query engine has to scan two groups of column files, 182 partitions each having two 50 MB files. This gives us around 18.2 GB of on-disk data and around 592 MB/s disk read rate. That's lower than the configured maximum in our EBS volume, but we should keep in mind allowed 10% fluctuations from the maximum throughput and, what's even more important, &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-optimized.html" rel="noopener noreferrer"&gt;individual limits&lt;/a&gt; for EBS-optimized instances. Our instance type is c5a.12xlarge and, according to the AWS documentation, it's limited with 594 MB/s on 128 KiB I/O which is very close to our back of the envelope calculation.&lt;/p&gt;

&lt;p&gt;Long story short, we're maxing out the disk with multi-threaded query execution while single-threaded execution time in version 6.2 stays the same. With this in mind, further instance type and volume improvements would lead to better performance.&lt;/p&gt;

&lt;p&gt;Things should get even more exciting when it comes to hot execution scenario, so there we go. In the next and all of the subsequent benchmark runs, we measure the average hot execution time for the same query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fbefore-and-after-hot-runs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fquestdb.io%2Fimg%2Fblog%2F2022-05-26%2Fbefore-and-after-hot-runs.png" alt="A chart comparing hot query execution time improvements in QuestDB 6.3 - Query 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this particular box, default QuestDB configuration leads to 16 threads being used for the shared worker pool. So, both 6.3 runs execute the filter on multiple threads speeding up the query when compared with the 6.2 runs. Another observation is almost 1x difference between JIT-compiled and non-JIT filters on 6.3. So, even with many cores available to parallel query execution, it's a good idea to keep JIT compilation enabled.&lt;/p&gt;

&lt;p&gt;You might have noticed a weird proportion in the above chart. Namely, the difference between the execution times when JIT compilation is disabled. QuestDB 6.2 takes 30 seconds to finish the query with a single thread, while it takes only roughly 1.3 seconds on 6.3. That's 23x improvement and it's impossible to explain it only with parallel processing (remember, we run the filter on 16 threads). So, what may be the reason?&lt;/p&gt;

&lt;p&gt;The thing is that parallel filter execution uses the same batch-based model as JIT-compiled filter functions. This means that the filter is executed in a tight, CPU-friendly loop while the resulting identifiers for the matching rows&lt;br&gt;
are stored in an intermediate array. For instance, if we restrict parallel filter engine to run on a single thread which is as simple as adding &lt;code&gt;shared.worker.count=1&lt;/code&gt; database setting, the query under test would execute in around 13.5 seconds. Thus, in this very scenario batch-based filter processing done on a single thread allows us to cut down 55% of the query execution time. Obviously, multiple threads available to the engine let it run even faster. Refer to this &lt;a href="https://questdb.io/blog/2022/01/12/jit-sql-compiler/#jit-based-filtering" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; for more information on how we do batch-based filter processing in our SQL JIT compiler.&lt;/p&gt;

&lt;p&gt;There is one more optimization opportunity around the query we used here. Namely, in case of queries that select only simple aggregate functions, like &lt;code&gt;count(*)&lt;/code&gt; or &lt;code&gt;max(*)&lt;/code&gt;, and no column values we could push down the functions into the filter loop. As an example, the filter loop will be incrementing the &lt;code&gt;count(*)&lt;/code&gt; function's counter in-place rather than doing a more generic accumulation of the filtered row identifiers. You could say that such queries are rather niche, but they could be met in various dashboard applications. Thus, it's something that we definitely consider adding in future.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Certainly, parallel SQL filter execution introduced in 6.3 is not the final point in our quest. As we've mentioned already, we have multi-threading in place for aggregate queries, like &lt;code&gt;SAMPLE BY&lt;/code&gt; or &lt;code&gt;GROUP BY&lt;/code&gt;, but only for certain shapes of them. Aggregate functions push-down is another potential optimization. So stay tuned for further improvements!&lt;/p&gt;

&lt;p&gt;As always, we encourage our users to try out 6.3.1 release on your QuestDB instances and provide feedback in our &lt;a href="https://slack.questdb.io/" rel="noopener noreferrer"&gt;Slack Community&lt;/a&gt;. You can also play with our &lt;a href="https://demo.questdb.io/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; to see how fast it executes your queries. And, of course, open-source contributions to our &lt;a href="https://github.com/questdb/questdb" rel="noopener noreferrer"&gt;project on GitHub&lt;/a&gt; are more than welcome.&lt;/p&gt;

</description>
      <category>timeseries</category>
      <category>sql</category>
      <category>database</category>
      <category>java</category>
    </item>
    <item>
      <title>Join Hacktoberfest 2021 and contribute to QuestDB!</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Tue, 05 Oct 2021 15:16:50 +0000</pubDate>
      <link>https://dev.to/questdb/join-hacktoberfest-2021-and-contribute-to-questdb-3o9p</link>
      <guid>https://dev.to/questdb/join-hacktoberfest-2021-and-contribute-to-questdb-3o9p</guid>
      <description>&lt;p&gt;Hacktoberfest 2021 is starting today! For the first time, &lt;a href="https://questdb.io"&gt;QuestDB&lt;/a&gt; is participating as an open source project. We're super excited to meet with other open source contributors and maintainers.&lt;/p&gt;

&lt;p&gt;For those who're not familiar with Hacktoberfest, it's a month-long online celebration for open source software and communities. By &lt;a href="https://hacktoberfest.digitalocean.com/resources/participation"&gt;contributing to open source projects&lt;/a&gt;, you can get a special edition Hacktoberfest T-shirt 👕 or choose to plant a tree for our planet. 🌴&lt;/p&gt;

&lt;p&gt;Many widely used open-source projects are maintained by a small number of developers or even a single person without any financial incentives. And we rely so much on their perseverance and commitment! Participating in Hacktoberfest is one of our approaches to raise awareness and encourage more people to contribute to open source. To celebrate Hacktoberfest, we put together some hints for you to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⛳ Get started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you have a GitHub (or GitLab) account&lt;/li&gt;
&lt;li&gt;Sign up for the event at
&lt;a href="https://hacktoberfest.digitalocean.com/"&gt;Hacktoberfest's official website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Go to open source repositories that opt in for Hacktoberfest:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;QuestDB Core Project&lt;/strong&gt;:
&lt;a href="https://github.com/questdb/questdb"&gt;https://github.com/questdb/questdb&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QuestDB Documentation&lt;/strong&gt;:
&lt;a href="https://github.com/questdb/questdb.io"&gt;https://github.com/questdb/questdb.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Or, look for other open source projects labeled with &lt;code&gt;hacktoberfest&lt;/code&gt; in their topics&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you're new to the project, look for open issues labeled with &lt;code&gt;good first issues&lt;/code&gt; or &lt;code&gt;help wanted&lt;/code&gt; to get started&lt;/li&gt;
&lt;li&gt;Before you commit, don't forget to read &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; and follow the contribution guideline 👍&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🎁 Tees, trees and QuestDB swag
&lt;/h2&gt;

&lt;p&gt;Once you reach the &lt;a href="https://hacktoberfest.digitalocean.com/resources/participation"&gt;contribution target&lt;/a&gt; of &lt;strong&gt;4 valid pull requests&lt;/strong&gt;, you can claim the reward from the official organizer! In addition, if you successfully contribute to QuestDB projects, we offer extra SWAG for you through our &lt;a href="https://questdb.io/community/"&gt;SWAG program&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;To make sure that your pull request is valid, please follow Hacktoberfest's &lt;a href="https://hacktoberfest.digitalocean.com/resources/qualitystandards"&gt;quality standard&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ℹ️ Get support and updates
&lt;/h2&gt;

&lt;p&gt;Some questions might appear when you're trying to contribute to QuestDB projects; here are the places to get support and hints from our team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QuestDB Community Slack: &lt;a href="https://slack.questdb.io"&gt;https://slack.questdb.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Discussions:
&lt;a href="https://github.com/questdb/questdb/discussions"&gt;https://github.com/questdb/questdb/discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;QuestDB Documentation:
&lt;a href="https://questdb.io/docs/introduction/"&gt;https://questdb.io/docs/introduction/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, don't forget to follow us on social media to receive the latest updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QuestDB Twitter: &lt;a href="https://twitter.com/questdb"&gt;https://twitter.com/questdb&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;QuestDB Linkedin:
&lt;a href="https://www.linkedin.com/company/questdb/"&gt;https://www.linkedin.com/company/questdb/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Thank you Hacktoberfest!</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Wed, 04 Nov 2020 08:05:45 +0000</pubDate>
      <link>https://dev.to/pswu11/thank-you-hacktoberfest-429d</link>
      <guid>https://dev.to/pswu11/thank-you-hacktoberfest-429d</guid>
      <description>&lt;p&gt;Hacktoberfest 2020 finally wrapped up. It’s been the first one we’ve done here at Jina &lt;em&gt;(c’mon, we were only born this year)&lt;/em&gt;, but it certainly won’t be our last!&lt;/p&gt;

&lt;p&gt;Hacktoberfest isn’t just about the glamor, prestige and adoration of the masses that comes with contributing to open source. You can also get a T-shirt for your contributions, or have a tree planted in your name. We’re hoping quite a few of our readers managed to hit their Hacktoberfest target and can show off their T-shirt (or sapling) with pride. 👕 🌳&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/JwTqLNfrx4OPe/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/JwTqLNfrx4OPe/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;
Contribute to Hacktoberfest and get a tree (note: image is for illustration purposes only and may not reflect final product. Pot and ability to dance not included)




&lt;p&gt;We’re super grateful for all the pull requests from new open-source contributors. You’ve helped us improve our &lt;a href="//get.jina.ai"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt; in Portuguese, Russian, and Ukrainian. On top of that, many folks also pulled out all the stops in resolving issues related to &lt;code&gt;RouteDriver&lt;/code&gt;, &lt;code&gt;mypy&lt;/code&gt;, or &lt;code&gt;numpy indexer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iEeiiSlm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/650/1%2As-jQZQEWMKji56Qeu-4q1w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iEeiiSlm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://miro.medium.com/max/650/1%2As-jQZQEWMKji56Qeu-4q1w.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are just a few of the highlights of our community pull requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1097"&gt;https://github.com/jina-ai/jina/pull/1097&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1057"&gt;https://github.com/jina-ai/jina/pull/1057&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/examples/pull/258"&gt;https://github.com/jina-ai/examples/pull/258&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1093"&gt;https://github.com/jina-ai/jina/pull/1093&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1124"&gt;https://github.com/jina-ai/jina/pull/1124&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1155"&gt;https://github.com/jina-ai/jina/pull/1155&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jina-ai/jina/pull/1140"&gt;https://github.com/jina-ai/jina/pull/1140&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Huge thanks again 🙏 to &lt;a href="https://github.com/fernandakawasaki"&gt;&lt;strong&gt;fernandakawasaki&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/averkij"&gt;&lt;strong&gt;averkij&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/jyothishkjames"&gt;&lt;strong&gt;jyothishkjames&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/clennan"&gt;&lt;strong&gt;clennan&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/Syarol"&gt;&lt;strong&gt;Syarol&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://github.com/minfun"&gt;&lt;strong&gt;minfun&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://github.com/yartem"&gt;&lt;strong&gt;yartem&lt;/strong&gt;&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Hacktoberfest is over for now. But at Jina AI, our mission still continues— &lt;strong&gt;building a world-class neural search framework for any kind of data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’re all about open source and building a sustainable open source community that reflects that. Join, contribute, help build the future of AI, and get some stickers along the way! 💪&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is co-created by Pei-Shan Wu and Alex-CG.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Jina GitHub Repo:&lt;/strong&gt; &lt;a href="//get.jina.ai"&gt;get.jina.ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="//jina.ai"&gt;jina.ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/JinaAI_"&gt;@JinaAI_&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linkedin:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/company/jinaai/"&gt;https://www.linkedin.com/company/jinaai/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slack Community:&lt;/strong&gt; &lt;a href="//slack.jina.ai"&gt;slack.jina.ai&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Jina AI x NLP Zurich: Designing the data model for an open source AI search framework</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Thu, 15 Oct 2020 13:16:00 +0000</pubDate>
      <link>https://dev.to/pswu11/designing-the-data-model-for-an-open-source-ai-search-framework-9p3</link>
      <guid>https://dev.to/pswu11/designing-the-data-model-for-an-open-source-ai-search-framework-9p3</guid>
      <description>&lt;p&gt;If you are interested in deep learning, machine learning, NLP, data science, open source, and search framework, you might also be interested participating in &lt;strong&gt;our virtual talk at NLP Zurich next Tuesday on Oct 20, 2020&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;My colleague, &lt;a href="https://twitter.com/alexcg" rel="noopener noreferrer"&gt;Alex&lt;/a&gt;, and I got in touch with &lt;strong&gt;NLP Zurich&lt;/strong&gt; some time ago. Immediately, we feel so connected and want to host a meetup together. &lt;br&gt;
&lt;a href="https://www.meetup.com/NLP-Zurich/events/273854724/" rel="noopener noreferrer"&gt;So here we come!&lt;/a&gt; 😁&lt;/p&gt;

&lt;p&gt;Before COVID-19, NLP Zurich meetups were usually offline. Since it's only possible to have virtual meetups right now, wouldn't it be nice to meet more people from around the world?&lt;/p&gt;

&lt;h2&gt;
  
  
  About NLP Zurich
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9dwfsvbzbkxcv6pewlry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9dwfsvbzbkxcv6pewlry.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.linkedin.com/company/nlp-zurich/" rel="noopener noreferrer"&gt;NLP Zurich&lt;/a&gt; is the first Natural Language Processing (NLP) open platform in Switzerland.&lt;/strong&gt; It aims to stimulate information and opinion exchange on topics around Artificial Intelligence, Machine Learning and Language Technologies. &lt;/p&gt;

&lt;p&gt;Through events and meetups, it's now becoming the key platform to meet the stakeholders from academia and industry that are passionate about shaping the ecosystem. &lt;/p&gt;

&lt;h2&gt;
  
  
  About Jina
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgk3935fdv3n1gkj74bqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgk3935fdv3n1gkj74bqp.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="//opensource.jina.ai"&gt;Jina&lt;/a&gt; is an open-source search framework that provides an easier way to build neural search on the cloud.&lt;/strong&gt; Whether you’re searching with images, video clips, audio snippets, or texts in various lengths, Jina provides high-level support to many existing neural search modals. &lt;/p&gt;

&lt;p&gt;&lt;a href="//jina.ai"&gt;Jina AI&lt;/a&gt; is the company behind the Jina project. Our mission is to provide &lt;strong&gt;the universal solution for these neural search problems&lt;/strong&gt;. As an open source company, we are working our best to live by open source principles, to continuously improve the developer experience, and to make it as accessible as possible.&lt;/p&gt;

&lt;p&gt;Feel free to join &lt;a href="http://jina-ai.slack.com/" rel="noopener noreferrer"&gt;our community on Slack&lt;/a&gt;! &lt;/p&gt;

&lt;h2&gt;
  
  
  Agenda
&lt;/h2&gt;

&lt;p&gt;18:50 Participants join the webinar&lt;br&gt;
19:00 &lt;strong&gt;Talk: Designing data model for open source AI search framework&lt;/strong&gt; (Maximilian Werk, Senior AI Engineer at Jina AI)&lt;br&gt;
19:35 Q&amp;amp;A&lt;br&gt;
19:50 Virtual Hugs and Kisses ⊂(◉‿◉)つ&lt;/p&gt;

&lt;p&gt;Sounds interesting? &lt;br&gt;
&lt;strong&gt;Then register here:&lt;/strong&gt; &lt;a href="https://www.meetup.com/NLP-Zurich/" rel="noopener noreferrer"&gt;https://www.meetup.com/NLP-Zurich/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Speaker
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Max is one of the core contributors of Jina.&lt;/strong&gt; He works on making search truly intelligent that can handle not only text but also graphics, audio or video data. He is passionate about clean, maintainable code and architecture in the AI environment. &lt;/p&gt;

&lt;p&gt;Max has a master’s in mathematics at TU Berlin and worked as senior research engineer at Zalando SE before joining Jina AI.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Source: NLP Zurich, Jina AI GmbH&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Jina AI at Hacktoberfest 2020</title>
      <dc:creator>PSWU</dc:creator>
      <pubDate>Wed, 14 Oct 2020 15:07:40 +0000</pubDate>
      <link>https://dev.to/pswu11/jina-ai-at-hacktoberfest-2020-6nn</link>
      <guid>https://dev.to/pswu11/jina-ai-at-hacktoberfest-2020-6nn</guid>
      <description>&lt;p&gt;Two weeks have passed for &lt;strong&gt;2020 Hacktoberfest&lt;/strong&gt;, are you looking for fun project to contribute to? &lt;/p&gt;

&lt;p&gt;You can find &lt;strong&gt;real challenges and true open source spirit here at Jina AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dY8xnCQD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/195xnlc1m755wcl1gyox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dY8xnCQD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/195xnlc1m755wcl1gyox.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Hacktoberfest
&lt;/h2&gt;

&lt;p&gt;Hacktoberfest is a month-long (Oct 1 to Oct 31) event, celebrating open source software and its community. &lt;/p&gt;

&lt;p&gt;Participants can get a &lt;strong&gt;Hacktoberfest T-shirt&lt;/strong&gt; or &lt;strong&gt;choose to plant a tree&lt;/strong&gt; if they successfully make 4 valid pull requests &lt;strong&gt;(Sidenote: PRs have to be merged, approved by a maintainer OR labelled as hacktoberfest-accepted)&lt;/strong&gt; to any GitHub hosted open source projects &lt;em&gt;that have opted in with putting #hacktoberfest in their topics&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Most importantly, it’s free and open for everyone. All you need to attend is your GitHub account and then register at the &lt;a href="//hacktoberfest.digitalocean.com"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Jina
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="//opensource.jina.ai"&gt;Jina&lt;/a&gt; is an open-source search framework powered by deep-learning technology&lt;/strong&gt;, empowering developers to build cross-modal or multi-modal search systems for text, images, video, and audio.&lt;/p&gt;

&lt;p&gt;As a fairly young open source project, Jina was first released on GitHub in April 2020. The project is currently under heavy development and is maintained by a full-time, venture-backed team.&lt;/p&gt;

&lt;p&gt;Thus, this is a great opportunity for contributors to &lt;strong&gt;make real impact&lt;/strong&gt;. 💻 🙋&lt;/p&gt;

&lt;p&gt;To give you some idea about how Jina can be used in the real world, here are some use cases created by our community members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transformer for lawyers&lt;/strong&gt; &lt;a href="https://github.com/ArturTan/transformers-for-lawyers"&gt;(Read More)&lt;/a&gt; by &lt;a href="https://github.com/ArturTan"&gt;ArturTan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chatbot integration: Jina AI with Rasa&lt;/strong&gt; &lt;a href="https://chatbotslife.com/jina-ai-with-rasa-1e81a8b869cc"&gt;(Read More)&lt;/a&gt; by &lt;a href="https://github.com/sibbsnb"&gt;sibbsnb&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But of course, there are more possibilities to be explored!&lt;/p&gt;

&lt;p&gt;Jina’s core components are written mostly in Python. We also make use of other open source software stacks such as Tensorflow, Docker, PyTorch, Hugging Face, etc.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="//opensource.jina.ai"&gt;Jina’s repo on Github&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing to Jina — where to start?
&lt;/h2&gt;

&lt;p&gt;Whether you’re a beginner or a veteran, we welcome all kinds of contributors from the open-source community. We would love to have more and more active contributors or even users working together with our team.&lt;/p&gt;

&lt;p&gt;Aligning with what is communicated by Hackoberfest, Jina’s team also &lt;strong&gt;values quality over quantity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before you submit your pull request, make sure you you have read through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hacktoberfest’s participation rules and quality standards (&lt;a href="https://hacktoberfest.digitalocean.com/details"&gt;Here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Jina’s contribution guideline (&lt;a href="https://github.com/jina-ai/jina/blob/master/CONTRIBUTING.md"&gt;Here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Jina’s code of conduct (&lt;a href="https://github.com/jina-ai/jina/blob/master/CODE_OF_CONDUCT.md"&gt;Here&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, please do not hesitate to join our &lt;a href="https://join.slack.com/t/jina-ai/shared_invite/zt-dkl7x8p0-rVCv~3Fdc3~Dpwx7T7XG8w"&gt;&lt;strong&gt;Slack Community&lt;/strong&gt;&lt;/a&gt; if you need more guidance when contributing to Jina. &lt;/p&gt;

&lt;p&gt;We look forward to seeing your pull requests!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
