<?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: ScyllaDB</title>
    <description>The latest articles on DEV Community by ScyllaDB (@scylladb).</description>
    <link>https://dev.to/scylladb</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%2Forganization%2Fprofile_image%2F9699%2F39631723-ec7f-4687-8b0d-56ce667e5742.png</url>
      <title>DEV Community: ScyllaDB</title>
      <link>https://dev.to/scylladb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scylladb"/>
    <language>en</language>
    <item>
      <title>Database 101: SSL/TLS for beginners</title>
      <dc:creator>Daniel Reis</dc:creator>
      <pubDate>Fri, 04 Oct 2024 15:33:01 +0000</pubDate>
      <link>https://dev.to/scylladb/database-101-ssltls-for-beginners-4lmn</link>
      <guid>https://dev.to/scylladb/database-101-ssltls-for-beginners-4lmn</guid>
      <description>&lt;p&gt;Once again, I'm bringing some knowledge from my day-to-day work as a Developer Advocate at &lt;a href="https://scylladb.com" rel="noopener noreferrer"&gt;ScyllaDB&lt;/a&gt;, and this time I'm going to teach you about secure and encrypted connections ! It's a topic that can cause a lot of anxiety for newbies, but I'm going to make it easier for you.&lt;/p&gt;

&lt;p&gt;If you're just getting started with databases in general or databases in particular, you might want to start by reading my first article, &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database 101: Data Consistency for Beginners&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This article captures my own exploration of how many database paradigms exist as I look far beyond my previous experience with just SQL and MySQL. I'm keeping track of my studies in this &lt;strong&gt;Database 101&lt;/strong&gt; series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Prologue&lt;/li&gt;
&lt;li&gt;2. What is SSL and TLS?&lt;/li&gt;
&lt;li&gt;3. OpenSSL for Noobs&lt;/li&gt;
&lt;li&gt;
4. Database Authentication

&lt;ul&gt;
&lt;li&gt;4.1 No Authentication&lt;/li&gt;
&lt;li&gt;4.2 Username/Password Authentication&lt;/li&gt;
&lt;li&gt;4.3 Creating Users and Roles&lt;/li&gt;
&lt;li&gt;
4.4 Certificated / Role Certificated Authentication

&lt;ul&gt;
&lt;li&gt;4.4.1 Change the Authentication Type&lt;/li&gt;
&lt;li&gt;4.4.2 Enable the Encryption Port&lt;/li&gt;
&lt;li&gt;4.4.3 Enable the Client Encryption Port&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;5. Testing Encrypted Connections&lt;/li&gt;

&lt;li&gt;6. Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Prologue
&lt;/h2&gt;

&lt;p&gt;Working on a NoSQL database is challenging in the sense that I have to learn stuff that I'd NEVER touch or study as a regular web developer. I mean, mostly you build a CRUD application, try your best not to screw up the database indexes (right guys ??????) and paint a few buttons.&lt;/p&gt;

&lt;p&gt;But if you've been following my Database 101 journey, you probably know that I came to Scylla knowing NOTHING about databases except what MySQL was and how to build things with Laravel, and now I'm into it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building Highly Scalable Applications with ScyllaDB&lt;/li&gt;
&lt;li&gt;Still learning Rust - 1 year and 6 months &lt;strong&gt;(and I feel like I know shit about it)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Started learning ShellScript, Go, Python, JavaScript, and anything that has a ScyllaDB driver&lt;/li&gt;
&lt;li&gt;Learned the basics of observability using Grafana/Prometheus&lt;/li&gt;
&lt;li&gt;and my new friend &lt;strong&gt;Transport Layer Security&lt;/strong&gt; (a.k.a. &lt;strong&gt;TLS&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're setting up your own servers, you've probably already used &lt;a href="https://certbot.com" rel="noopener noreferrer"&gt;certbot&lt;/a&gt; to install certificates to run HTTPS smoothly, and that's what I've been doing for the last 6~7 years. To be really honest, knowing how to use a tool is what we &lt;em&gt;developers&lt;/em&gt; do. &lt;/p&gt;

&lt;p&gt;It even seems like an omen, but I'm working on a browser extension whose backend is written in Rust, and I didn't manage to use &lt;code&gt;certbot&lt;/code&gt; there, so I had to create my certificate using weird commands and add a weird &lt;code&gt;openssl&lt;/code&gt; crate to &lt;a href="https://github.com/basementdevs/tbp-consumer-api/blob/e130e279b6b53d34e3ba9270f3d4c211a815582b/src/main.rs#L31" rel="noopener noreferrer"&gt;my project&lt;/a&gt; and trust that it would work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;certs_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;key_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// load TLS certs and key to create a self-signed temporary cert for testing:&lt;/span&gt;
&lt;span class="c1"&gt;// `openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'`&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tls_certs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;rustls_pemfile&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;certs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;certs_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tls_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;rustls_pemfile&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;pkcs8_private_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;key_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few hours of trying/catching, I finally managed to deploy the API with the certificate, but I didn't learn anything about it. THEN, a few days later, my boss asked me to extend the &lt;a href="https://opensource.docs.scylladb.com/branch-6.0/operating-scylla/security/index.html" rel="noopener noreferrer"&gt;ScyllaDB Security Pages&lt;/a&gt; and learn how &lt;strong&gt;Certificated Based Connection&lt;/strong&gt; works, since I'd never used it. &lt;/p&gt;

&lt;p&gt;I spent 10 hours on it and managed to get it to work, but mostly it wasn't about the documentation, it was about &lt;strong&gt;my lack of knowledge&lt;/strong&gt; in this area, which is no longer a problem and I will teach you everything I've learned so far!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What is SSL and TLS?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;TLDR: this is that boring section which you can skip if you want. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SSL (Secure Sockets Layer) was introduced in 1995 by Netscape to provide privacy, authentication, and data integrity for internet communications, primarily in browsers. TLS (Transport Layer Security), which succeeded SSL, is now the standard security protocol used across the internet for applications like email, messaging, and VoIP.&lt;/p&gt;

&lt;p&gt;Without SSL/TLS, data transmitted between a client and a server is unencrypted and vulnerable to interception and misuse, making it an easy target for phishing and other attacks. SSL/TLS ensures that a secure "handshake" occurs before data transmission, meaning that all data is encrypted for that specific session.&lt;/p&gt;

&lt;p&gt;However, even though SSL/TLS provides encryption, it doesn’t guarantee that the server itself is fully trustworthy or secure. SSL, being deprecated since 1996 due to its vulnerabilities, is still often referenced alongside TLS by developers as "SSL/TLS" because of its historical significance.&lt;/p&gt;

&lt;p&gt;With this understanding of SSL/TLS, let’s now dive into the tools that help manage secure connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. OpenSSL for Noobs
&lt;/h2&gt;

&lt;p&gt;There are many data encryption toolkits available today, but we're going to talk about a very specific one: the &lt;a href="https://openssl-library.org/" rel="noopener noreferrer"&gt;openssl-library.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This library, which had its first release in 1998, is attached to &lt;em&gt;every single tutorial&lt;/em&gt; on data encryption, and is used by many other tools around the open/closed source environment - such as Let's Encrypt and Certbot - and the one we're going to use here. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb017wsv0r8ld8l10rjb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb017wsv0r8ld8l10rjb1.png" alt="OpenSSL Manual" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purpose of this tool is to create keychains and credentials to authorize someone to do whatever you want. &lt;/p&gt;

&lt;p&gt;Imagine you work in a commercial building where you have to show your badge to security to get in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your company creates a &lt;strong&gt;"master key"&lt;/strong&gt; and generates a certificate for it. &lt;/li&gt;
&lt;li&gt;You also generate a &lt;strong&gt;key and certificate&lt;/strong&gt; for the security guard and give them to him along with a list of all the employee's certificates.&lt;/li&gt;
&lt;li&gt;The guard has a &lt;strong&gt;truststore keychain&lt;/strong&gt; with all sorts of tags on it.&lt;/li&gt;
&lt;li&gt;When you arrive at the entrance, he will check that your tag has the same root certificate as his to decide whether you can pass or not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvdj4670oqksjerytc0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvdj4670oqksjerytc0m.png" alt="Encryption Purpose Diagram" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In matter of commands, we did something like this:&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="c"&gt;# Creating the root/master key and certificate&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;
openssl genpkey &lt;span class="nt"&gt;-algorithm&lt;/span&gt; RSA &lt;span class="nt"&gt;-out&lt;/span&gt; root_key.pem &lt;span class="nt"&gt;-pkeyopt&lt;/span&gt; rsa_keygen_bits:2048 &lt;span class="c"&gt;# Generate the root key&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; root_key.pem &lt;span class="nt"&gt;-days&lt;/span&gt; 3650 &lt;span class="nt"&gt;-out&lt;/span&gt; root_cert.pem &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=administrator"&lt;/span&gt; &lt;span class="c"&gt;# Generate the root certificate&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;

&lt;span class="c"&gt;# Creating the Employee Key and Certificate&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;
openssl genpkey &lt;span class="nt"&gt;-algorithm&lt;/span&gt; RSA &lt;span class="nt"&gt;-out&lt;/span&gt; employee_key.pem &lt;span class="nt"&gt;-pkeyopt&lt;/span&gt; rsa_keygen_bits:2048 &lt;span class="c"&gt;# Generate the employee base key&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; employee_key.pem &lt;span class="nt"&gt;-out&lt;/span&gt; employee.csr &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=employee"&lt;/span&gt; &lt;span class="c"&gt;# Generate the employee CSR&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; employee.csr &lt;span class="nt"&gt;-CA&lt;/span&gt; root_cert.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; root_key.pem &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; employee_cert.pem &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="c"&gt;# Sign and generate the employee certificate with the root/master key and certificate&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;root_cert.pem employee_cert.pem &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; employee_truststore.pem &lt;span class="c"&gt;# Create a truststore (tag) to be used together with the key.&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;

&lt;span class="c"&gt;# Creating the Security (server) Key and Certificate&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;
openssl genpkey &lt;span class="nt"&gt;-algorithm&lt;/span&gt; RSA &lt;span class="nt"&gt;-out&lt;/span&gt; security_key.pem &lt;span class="nt"&gt;-pkeyopt&lt;/span&gt; rsa_keygen_bits:2048 &lt;span class="c"&gt;# Generate the security key&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; security_key.pem &lt;span class="nt"&gt;-out&lt;/span&gt; security.csr &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=server"&lt;/span&gt; &lt;span class="c"&gt;# Generate the security CSR&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; security.csr &lt;span class="nt"&gt;-CA&lt;/span&gt; root_cert.pem &lt;span class="nt"&gt;-CAkey&lt;/span&gt; root_key.pem &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; security_cert.pem &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="c"&gt;# Sign and generate the security certificate with the root/master key and certificate&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;root_cert.pem security_cert.pem &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; security_truststore.pem &lt;span class="c"&gt;# Create a truststore (tag) to be used together with the key.&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;


&lt;span class="c"&gt;# Creating the Malicious Root and Client Key and Certificate&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;
openssl genpkey &lt;span class="nt"&gt;-algorithm&lt;/span&gt; RSA &lt;span class="nt"&gt;-out&lt;/span&gt; malicious_key.pem &lt;span class="nt"&gt;-pkeyopt&lt;/span&gt; rsa_keygen_bits:2048 &lt;span class="c"&gt;# Generate the malicious root key&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; malicious_key.pem &lt;span class="nt"&gt;-days&lt;/span&gt; 3650 &lt;span class="nt"&gt;-out&lt;/span&gt; malicious_cert.pem &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=fake-admin"&lt;/span&gt; &lt;span class="c"&gt;# Generate the malicious root certificate&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;root_cert.pem malicious_cert.pem &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; malicious_truststore.pem &lt;span class="c"&gt;# Combine root and malicious root certificates into a truststore&lt;/span&gt;
&lt;span class="c"&gt;# -----&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are too many commands, but basically this is what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;root_key.pem&lt;/code&gt; and &lt;code&gt;root_cert.pem&lt;/code&gt; were generated.&lt;/li&gt;
&lt;li&gt;These keys and certificates were used to sign other keys and certificates for employees:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;security_key.pem&lt;/code&gt; was signed by &lt;code&gt;root_key.pem&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;employee_key.pem&lt;/code&gt; was signed by &lt;code&gt;root_key.pem&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;server_key.pem&lt;/code&gt; has been signed by &lt;code&gt;root_key.pem&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;All these keys are in a file called &lt;code&gt;security_truststore.pem&lt;/code&gt;. &lt;/li&gt;

&lt;li&gt;Someone tried to create a &lt;strong&gt;fake key&lt;/strong&gt; called &lt;code&gt;malicious_key.pem&lt;/code&gt; using the &lt;code&gt;root_cert.pem&lt;/code&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now let's do some validation. Theoretically, only the keys in &lt;code&gt;security_truststore.pem&lt;/code&gt; should be allowed to join:&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="c"&gt;# Validating the certificates with the truststores&lt;/span&gt;
&lt;span class="c"&gt;# ----- &lt;/span&gt;
openssl verify &lt;span class="nt"&gt;-CAfile&lt;/span&gt; security_truststore.pem employee_cert.pem &lt;span class="c"&gt;# Check if the certificate is valid&lt;/span&gt;
&lt;span class="c"&gt;# employee_cert.pem: OK&lt;/span&gt;

openssl verify &lt;span class="nt"&gt;-CAfile&lt;/span&gt; security_truststore.pem malicious_cert.pem &lt;span class="c"&gt;# Check if the certificate is valid&lt;/span&gt;
&lt;span class="c"&gt;# CN = fake-admin&lt;/span&gt;
&lt;span class="c"&gt;#  error 18 at 0 depth lookup: self-signed certificate&lt;/span&gt;
&lt;span class="c"&gt;#  error malicious_cert.pem: verification failed&lt;/span&gt;
&lt;span class="c"&gt;# ----- &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And as expected, since the malicious key is not signed by the root user, authentication fails and that person is kicked out. &lt;/p&gt;

&lt;p&gt;So now we know the basics of encryption. But where do you use it in the real world? Let me tell you more about my current task and then you will understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Database Authentication
&lt;/h2&gt;

&lt;p&gt;Say you're running any database (e.g. MySQL, Postgres, ScyllaDB, Redis, etc.) in Docker. When you spin up these instances in a containerized environment, you usually don't need a password unless you set one. In any case, we have a number of options when we talk about authentication in any part of the modern web, and the same is true when we're talking about databases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Were going to use &lt;a href="https://scylladb.com" rel="noopener noreferrer"&gt;ScyllaDB&lt;/a&gt; in this example however this flow is pretty common in &lt;strong&gt;many databases&lt;/strong&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this step, we're going to learn about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No Authentication:&lt;/strong&gt; Literally no credentials required, just spin your instance and be happy;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username/Password Authentication:&lt;/strong&gt; Some basic credentials are fine to keep people away from your data;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Certified / Role Certified Authentication:&lt;/strong&gt; This is where the big companies like to play and you should learn that.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After this brief introduction of encryption 101, we can get started with the content itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 No Authentication
&lt;/h3&gt;

&lt;p&gt;In Scylla, if you're running a node or cluster in the development environment (locally or using Docker), there is no password by default. Also, &lt;strong&gt;ScyllaDB uses the CQL protocol on port 9042&lt;/strong&gt; by default, which can be easily changed in the config files.&lt;/p&gt;

&lt;p&gt;Why am I telling you this port information? Because this information will be important later, trust me. &lt;/p&gt;

&lt;p&gt;You can spin up a Docker instance by running it:&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="c"&gt;# Running a ScyllaDB Node with Docker&lt;/span&gt;
docker  run &lt;span class="nt"&gt;--name&lt;/span&gt; some_scylla &lt;span class="nt"&gt;-p&lt;/span&gt; 9042:9042 &lt;span class="nt"&gt;-p&lt;/span&gt; 9142:9142 &lt;span class="nt"&gt;-d&lt;/span&gt; scylladb/scylla:6.1.2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--overprovisioned&lt;/span&gt; 1 &lt;span class="nt"&gt;--smp&lt;/span&gt; 1 

&lt;span class="c"&gt;# Checking the node status -&amp;gt; Expect for UN (Up And Running)Cassandra&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla nodetool status
&lt;span class="c"&gt;# Datacenter: datacenter1&lt;/span&gt;
&lt;span class="c"&gt;# =======================&lt;/span&gt;
&lt;span class="c"&gt;# Status=Up/Down&lt;/span&gt;
&lt;span class="c"&gt;# |/ State=Normal/Leaving/Joining/Moving&lt;/span&gt;
&lt;span class="c"&gt;# -- Address   Load      Tokens Owns Host ID                              Rack &lt;/span&gt;
&lt;span class="c"&gt;# UN 10.10.5.2 509.46 KB 256    ?    9f597eb5-a77f-493f-9835-85dd1e571fcc rack1&lt;/span&gt;
&lt;span class="c"&gt;# --&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you can login at &lt;strong&gt;cqlsh (CQL Shell)&lt;/strong&gt; by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla cqlsh
&lt;span class="c"&gt;# Connected to  at 10.10.5.5:9042&lt;/span&gt;
&lt;span class="c"&gt;# [cqlsh 6.0.18 | Scylla 6.0.1-0.20240612.bc89aac9d017 | CQL spec 3.3.1 | Native protocol v4]&lt;/span&gt;
&lt;span class="c"&gt;# Use HELP for help.&lt;/span&gt;
&lt;span class="c"&gt;# cqlsh&amp;gt; select address, port, client_type, username from system.clients;&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  address   | port  | client_type | username&lt;/span&gt;
&lt;span class="c"&gt;# -----------+-------+-------------+-----------&lt;/span&gt;
&lt;span class="c"&gt;#  10.10.5.5 | 50854 |         cql | anonymous&lt;/span&gt;
&lt;span class="c"&gt;#  10.10.5.5 | 50868 |         cql | anonymous&lt;/span&gt;
&lt;span class="c"&gt;# -----------+-------+-------------+-----------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, we're connected as &lt;strong&gt;anonymous users&lt;/strong&gt;! Perfect for developing new stuff and &lt;strong&gt;testing locally&lt;/strong&gt;. But then you decide to deploy the same configuration into production... &lt;/p&gt;

&lt;p&gt;And OF COURSE that, in less than an hour you'll be owned by some random database crawler LOL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm58lj3s8lsd3285nu9jc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm58lj3s8lsd3285nu9jc.png" alt="Owned PostgreSQL" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can read more about &lt;a href="https://www.helpnetsecurity.com/2024/01/18/postgresql-mysql-ransomware-bot/" rel="noopener noreferrer"&gt;this ransomware&lt;/a&gt; if you want. Anyway, let's add some base credentials and configure it properly in the next step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Seriously, don't open your database to the Internet with the default configurations or without binding a unique IP address to access it. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.2 Username/Password Authentication
&lt;/h3&gt;

&lt;p&gt;While running any of these other databases, you have the ability to switch authentication types by changing a bunch of configuration files. At &lt;strong&gt;ScyllaDB&lt;/strong&gt;, we're really proud of how &lt;strong&gt;modular and easy&lt;/strong&gt; the configuration is.&lt;/p&gt;

&lt;p&gt;If you look at &lt;code&gt;/etc/scylla/scylla.yaml&lt;/code&gt; in your docker (you can get a better look at the repository by &lt;a href="https://github.com/scylladb/scylladb/blob/master/conf/scylla.yaml#L247" rel="noopener noreferrer"&gt;clicking here (file: scylla.yaml#247)&lt;/a&gt;, you will find these configuration flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file: /etc/scylla/scylla.yaml&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="c1"&gt;# Authentication backend, identifying users&lt;/span&gt;
&lt;span class="c1"&gt;# Out of the box, Scylla provides org.apache.cassandra.auth.{AllowAllAuthenticator,&lt;/span&gt;
&lt;span class="c1"&gt;# PasswordAuthenticator}.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# - AllowAllAuthenticator performs no checks - set it to disable authentication.&lt;/span&gt;
&lt;span class="c1"&gt;# - PasswordAuthenticator relies on username/password pairs to authenticate&lt;/span&gt;
&lt;span class="c1"&gt;#   users. It keeps usernames and hashed passwords in system_auth.credentials table.&lt;/span&gt;
&lt;span class="c1"&gt;#   Please increase system_auth keyspace replication factor if you use this authenticator.&lt;/span&gt;
&lt;span class="c1"&gt;# - com.scylladb.auth.TransitionalAuthenticator requires username/password pair&lt;/span&gt;
&lt;span class="c1"&gt;#   to authenticate in the same manner as PasswordAuthenticator, but improper credentials&lt;/span&gt;
&lt;span class="c1"&gt;#   result in being logged in as an anonymous user. Use for upgrading clusters' auth.&lt;/span&gt;
&lt;span class="c1"&gt;# authenticator: AllowAllAuthenticator&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default is the &lt;code&gt;AllowAllAuthenticator&lt;/code&gt; which allows anonymous connection with superpowers. Now, we'll uncomment the &lt;code&gt;# authenticator:&lt;/code&gt; line and replace with &lt;code&gt;PasswordAuthenticator&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# file: /etc/scylla/scylla.yaml
# ...
&lt;span class="gd"&gt;-#authenticator: AllowAllAuthenticator  # line ~247
&lt;/span&gt;&lt;span class="gi"&gt;+authenticator: PasswordAuthenticator
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay! Now we just need to tell our Scylla node that we have made this change. Since this is a "critical" change to our system environment, we'll need to &lt;code&gt;drain&lt;/code&gt; and then restart the node. Here's a step-by-step guide:&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="c"&gt;# Before Editing&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla nodetool drain &lt;span class="c"&gt;# Stop gossiping and preparing to shut down&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla supervisorctl stop scylla &lt;span class="c"&gt;# Stop ScyllaDB&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/scylla/scylla.yaml | &lt;span class="nb"&gt;grep &lt;/span&gt;authenticator: &lt;span class="c"&gt;# Check the current flag&lt;/span&gt;

&lt;span class="c"&gt;# Edit /etc/scylla/scylla.yaml ...&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/# authenticator:.*/authenticator: PasswordAuthenticator/'&lt;/span&gt; /etc/scylla/scylla.yaml

&lt;span class="c"&gt;# After Edit&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/scylla/scylla.yaml | &lt;span class="nb"&gt;grep &lt;/span&gt;authenticator: &lt;span class="c"&gt;# Check if the update is there&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla supervisorctl start scylla &lt;span class="c"&gt;# Run ScyllaDB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that we're finally able to run &lt;code&gt;cqlsh&lt;/code&gt; and receives a huge "YOU NEED CREDENTIALS, BITCH":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla cqlsh
Connection error: &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unable to connect to any servers'&lt;/span&gt;, &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'10.10.5.5:9042'&lt;/span&gt;: AuthenticationFailed&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Remote end requires authentication'&lt;/span&gt;&lt;span class="o"&gt;)})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and by default, at ScyllaDB, we can login using the username/password "cassandra" (superuser):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla cqlsh &lt;span class="nt"&gt;-u&lt;/span&gt; cassandra &lt;span class="nt"&gt;-p&lt;/span&gt; cassandra
&lt;span class="c"&gt;# Connected to  at 10.10.5.5:9042&lt;/span&gt;
&lt;span class="c"&gt;# [cqlsh 6.0.18 | Scylla 6.0.1-0.20240612.bc89aac9d017 | CQL spec 3.3.1 | Native protocol v4]&lt;/span&gt;
&lt;span class="c"&gt;# Use HELP for help.&lt;/span&gt;
&lt;span class="c"&gt;# cassandra@cqlsh&amp;gt; select address, port, client_type, username from system.clients; &lt;/span&gt;

&lt;span class="c"&gt;#  address   | port  | client_type | username&lt;/span&gt;
&lt;span class="c"&gt;# -----------+-------+-------------+-----------&lt;/span&gt;
&lt;span class="c"&gt;#  10.10.5.5 | 56998 |         cql | cassandra&lt;/span&gt;
&lt;span class="c"&gt;#  10.10.5.5 | 57014 |         cql | cassandra&lt;/span&gt;
&lt;span class="c"&gt;# -----------+-------+-------------+-----------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... with that, we know the basics of how to configure authentication in ScyllaDB, and if you want to know why the user/password is "cassandra", you can read my &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;first article&lt;/a&gt; from this series. Also, be sure to change your default credentials before deploying it. &lt;/p&gt;

&lt;p&gt;The CQL protocol runs on port 9042 by default and is well known. In the same way that you can get owned by just letting your database run without authentication, you can &lt;strong&gt;get owned&lt;/strong&gt; by letting the default credentials there.&lt;/p&gt;

&lt;p&gt;At the moment, all connections still &lt;code&gt;unencrypted&lt;/code&gt; but at least we have a minimum security, but don't worry! We're about fix the encryption soon!  Before that, let's create some users/roles inside our database.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Creating Users and Roles
&lt;/h3&gt;

&lt;p&gt;Before creating new users for this database, we need to understand how it works. At Scylla we use &lt;code&gt;Role Based Authentication&lt;/code&gt; every day. So every user is placed in &lt;code&gt;system.roles&lt;/code&gt; as a role. &lt;/p&gt;

&lt;p&gt;This is not a problem because you can set a &lt;code&gt;PASSWORD&lt;/code&gt; to a role. It looks weird, but I'll show you how it works. Check it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scylladb@cqlsh&amp;gt; desc system.roles;

CREATE TABLE system.roles (
    role text,
    can_login boolean,
    is_superuser boolean,
    member_of set&amp;lt;text&amp;gt;,
    salted_hash text,
    PRIMARY KEY (role)
);

scylladb@cqlsh&amp;gt; select * from system.roles;

 role        | can_login | is_superuser |     member_of | salted_hash
-------------+-----------+--------------+---------------+--------------------
 scylladb    |      True |         True |          null | $6$rAJ6FflUo8Chf...
 employee    |     False |        False |          null | null
 danielhe4rt |      True |        False |  {'employee'} | $6$AF4F6CflAA8Cw...
-------------+-----------+--------------+---------------+--------------------


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This output gives us some information about the authentication itself, such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A role becomes an authenticatable when the &lt;code&gt;can_login&lt;/code&gt; flag is set to &lt;code&gt;true&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;A role must/can have a password after it becomes an authenticatable;&lt;/li&gt;
&lt;li&gt;A role can belong to &lt;code&gt;another role&lt;/code&gt;, which will inherit all privileges from it.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is just an environment created to give you an example of how the modeling and features work. Be sure to read the documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a matter of &lt;strong&gt;DCL (Data Control Language)&lt;/strong&gt; and &lt;strong&gt;DML (Data Manipulation Language)&lt;/strong&gt;, we'll do basic commands like:&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="c1"&gt;-- Create our user/roles&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;danielhe4rt&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'some_cool_password'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;LOGIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Grant roles to our users&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="n"&gt;developer&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;danielhe4rt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... which we will be running at our ScyllaDB Docker Instance following the keys created in the beginning of the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some-scylla cqlsh &lt;span class="nt"&gt;-u&lt;/span&gt; cassandra &lt;span class="nt"&gt;-p&lt;/span&gt; cassandra &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"CREATE ROLE IF NOT EXISTS 'employee' WITH LOGIN = true;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some-scylla cqlsh &lt;span class="nt"&gt;-u&lt;/span&gt; cassandra &lt;span class="nt"&gt;-p&lt;/span&gt; cassandra &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"CREATE ROLE IF NOT EXISTS 'server' WITH LOGIN = true;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: You can only create new roles/users using an authenticated account&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.4 Certificated / Role Certificated Authentication
&lt;/h3&gt;

&lt;p&gt;At the beginning of this tutorial we introduced the topic by talking about a story of someone with valid keys to enter a building and now the goal is to understand what is different from a regular authentication. As I said, without TLS/SSL, your data is in transit without any encryption, and that's BAD depending on the content you're sending/receiving.&lt;/p&gt;

&lt;p&gt;Using a certificate means that before you send your data out, we have guarantees that if someone intercepts the data, it won't be a problem if they don't have the key to decrypt it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, ScyllaDB listens to the CQL protocol on port 9042, which can be configured using the &lt;code&gt;native_transport_port&lt;/code&gt; configuration option.&lt;/li&gt;
&lt;li&gt;Scylla also supports the CQL protocol via TLS/SSL encryption, which is disabled by default and can be enabled using the &lt;code&gt;native_transport_port_ssl&lt;/code&gt; configuration option.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The traditional choice of port for secure connections is 9142, but if &lt;code&gt;client_encryption_options&lt;/code&gt; is specified and &lt;code&gt;native_transport_port_ssl&lt;/code&gt; is not, then &lt;code&gt;native_transport_port&lt;/code&gt; will only handle encrypted connections. The same thing happens if &lt;code&gt;native_transport_port&lt;/code&gt; and &lt;code&gt;native_transport_port_ssl&lt;/code&gt; are set to the same value.&lt;/p&gt;

&lt;p&gt;I know, it seems a bit crazy with all these flags and options, but I'll try to make it simpler. Check out the rules that govern port assignment/encryption, which are summarized in the table below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;np  := native_transport_port is set
nps := native_transport_port_ssl is set
ceo := client_encryption_options are enabled
eq  := native_transport_port_ssl == native_transport_port

+-----+-----+-----+-----+
|  np | nps | ceo |  eq |
+-----+-----+-----+-----+
|  0  |  0  |  0  |  *  |   =&amp;gt;   listen on native_transport_port, unencrypted
|  0  |  0  |  1  |  *  |   =&amp;gt;   listen on native_transport_port, encrypted
|  0  |  1  |  0  |  *  |   =&amp;gt;   don't listen
|  0  |  1  |  1  |  *  |   =&amp;gt;   listen on native_transport_port_ssl, encrypted
|  1  |  0  |  0  |  *  |   =&amp;gt;   listen on native_transport_port, unencrypted
|  1  |  0  |  1  |  *  |   =&amp;gt;   listen on native_transport_port, encrypted
|  1  |  1  |  0  |  *  |   =&amp;gt;   listen on native_transport_port, unencrypted
|  1  |  1  |  1  |  0  |   =&amp;gt;   listen on native_transport_port, unencrypted + native_transport_port_ssl, encrypted
|  1  |  1  |  1  |  1  |   =&amp;gt;   listen on native_transport_port(_ssl - same thing), encrypted
+-----+-----+-----+-----+

// More at: https://github.com/scylladb/scylladb/blob/master/docs/dev/protocols.md#cql-client-protocol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells us that if we want to enable encryption, we need to update a few more things in &lt;code&gt;scylla.yaml&lt;/code&gt;. Instead of using the &lt;code&gt;PasswordAuthenticator&lt;/code&gt;, we'll switch to the &lt;code&gt;com.scylladb.auth.CertificateAuthenticator&lt;/code&gt; feature.&lt;/p&gt;

&lt;p&gt;Before we change anything, let's drain and stop our cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla nodetool drain &lt;span class="c"&gt;# Stop gossiping and preparing to shut down&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla supervisorctl stop scylla &lt;span class="c"&gt;# Stop ScyllaDB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous step we created some roles with LOGIN enabled. And we added these "roles" to our OpenSSL commands at the top with a &lt;strong&gt;CN=server/blabla&lt;/strong&gt;, right? Now it's time to make it useful and not use credentials anymore.&lt;/p&gt;

&lt;p&gt;Private Keys + Certs can hold more information, like &lt;strong&gt;raw strings&lt;/strong&gt; that can be used after the handshake is done. In this case, we have stored the &lt;strong&gt;user/role&lt;/strong&gt; that will be used to log in inside our keys. With this, we can do a pattern match with the certificate content and check if there's any &lt;strong&gt;CN=something&lt;/strong&gt; that matches our &lt;code&gt;select * from system.roles where role = 'something'&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's start the modifications by entering our ScyllaDB instance:&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="c"&gt;# Enter the ScyllaDB Instance&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla shell

&lt;span class="c"&gt;# root@c531i213hu:/#&lt;/span&gt;
&lt;span class="c"&gt;# Install any editor of your choice&lt;/span&gt;
apt &lt;span class="nb"&gt;install &lt;/span&gt;nano

&lt;span class="c"&gt;# Enter the ScyllaDB Config File&lt;/span&gt;
nano &lt;span class="nt"&gt;-l&lt;/span&gt; /etc/scylla/scylla.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With your config file opened, let's work on the modifications needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.4.1 Change the Authentication Type
&lt;/h4&gt;

&lt;p&gt;This is the last time we'll open this file, I promise! The idea here is to change the authentication type from &lt;code&gt;PasswordAuthenticator&lt;/code&gt; to &lt;code&gt;com.scylladb.auth.CertificateAuthenticator&lt;/code&gt;. We're also going to set a &lt;strong&gt;rule&lt;/strong&gt; that will extract the &lt;code&gt;CN=&lt;/code&gt; flag from each certificate and use the content for authentication purposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# file: /etc/scylla/scylla.yaml
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-authenticator: PasswordAuthenticator # line ~247
&lt;/span&gt;&lt;span class="gi"&gt;+authenticator: com.scylladb.auth.CertificateAuthenticator
+auth_certificate_role_queries:
+  - source: SUBJECT
+    query: CN=([^,\s]+)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.4.2 Enable the Encryption Port
&lt;/h4&gt;

&lt;p&gt;We still haven't enabled &lt;strong&gt;TLS/SSL&lt;/strong&gt; even after setting the authenticator. We still need to uncomment the &lt;code&gt;native_transport_port_ssl&lt;/code&gt; to get port 9142 (which is used to transport our encrypted data) into the game. So, back to the &lt;code&gt;scylla.yaml&lt;/code&gt;, lets change it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# file: /etc/scylla/scylla.yaml
# ...
&lt;span class="gd"&gt;-# native_transport_port_ssl: 9142 # &amp;lt;- line ~131
&lt;/span&gt;&lt;span class="gi"&gt;+native_transport_port_ssl: 9142
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.4.3 Enable the Client Encryption Port
&lt;/h4&gt;

&lt;p&gt;Step by step, we'll make it! Our final change is to the &lt;code&gt;client_encryption_options&lt;/code&gt; as defined in the table above. We need to uncomment everything and make sure everything matches in these configurations. Here's a brief explanation of each configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;enabled:&lt;/strong&gt; enables TLS/SSL encryption -&amp;gt; change to &lt;strong&gt;true&lt;/strong&gt; (default: false)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;certificate:&lt;/strong&gt; &lt;code&gt;absolute path&lt;/code&gt; to your server certificate (security_cert.pem / server_cert.pem)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;keyfile:&lt;/strong&gt; &lt;code&gt;absolute path&lt;/code&gt; your server key signed by the root_key (security_key.pem / server_key.pem)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;truststore:&lt;/strong&gt; &lt;code&gt;absolute path&lt;/code&gt; your server truststore containing server + root certificates (security_truststore.pem / server_truststore.pem)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;require_client_auth:&lt;/strong&gt; your server must receive a certificate to authenticate -&amp;gt; change to true&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let's work on these changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# file: /etc/scylla/scylla.yaml
# ...
# enable or disable client/server encryption.
&lt;span class="gd"&gt;-# client_encryption_options:
-#   enabled: true
-#   certificate: /etc/scylla/certs/cert.pem
-#   keyfile: /etc/scylla/certs/key.pem
-#   truststore: /etc/scylla/certs/truststore.pem
-#   require_client_auth: true
&lt;/span&gt;&lt;span class="gi"&gt;+client_encryption_options:
+  enabled: true
+  certificate: /etc/scylla/certs/server_cert.pem
+  keyfile: /etc/scylla/certs/server_key.pem
+  truststore: /etc/scylla/certs/server_truststore.pem
+  require_client_auth: true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok! Now we're good to go. Let's turn on our ScyllaDB cluster by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; some_scylla supervisorctl start scylla &lt;span class="c"&gt;# Start ScyllaDB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see if our port 9142 is running and listening for TLS/SSL connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; localhost:9042 
&lt;span class="c"&gt;# CONNECTED(00000003)&lt;/span&gt;
&lt;span class="c"&gt;# 40873B60D2750000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:354:&lt;/span&gt;
&lt;span class="c"&gt;# no peer certificate available&lt;/span&gt;
&lt;span class="c"&gt;# No client certificate CA names sent&lt;/span&gt;

openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; localhost:9142
&lt;span class="c"&gt;# CONNECTED(00000003)&lt;/span&gt;
&lt;span class="c"&gt;# Can't use SSL_get_servername&lt;/span&gt;
&lt;span class="c"&gt;# depth=0 CN = server&lt;/span&gt;
&lt;span class="c"&gt;# verify error:num=20:unable to get local issuer certificate&lt;/span&gt;
&lt;span class="c"&gt;# verify return:1&lt;/span&gt;
&lt;span class="c"&gt;# depth=0 CN = server&lt;/span&gt;
&lt;span class="c"&gt;# verify error:num=21:unable to verify the first certificate&lt;/span&gt;
&lt;span class="c"&gt;# verify return:1&lt;/span&gt;
&lt;span class="c"&gt;# depth=0 CN = server&lt;/span&gt;
&lt;span class="c"&gt;# verify return:1&lt;/span&gt;
&lt;span class="c"&gt;# ---&lt;/span&gt;
&lt;span class="c"&gt;# Certificate chain&lt;/span&gt;
&lt;span class="c"&gt;# 0 s:CN = server&lt;/span&gt;
&lt;span class="c"&gt;#   i:CN = administrator&lt;/span&gt;
&lt;span class="c"&gt;#   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256&lt;/span&gt;
&lt;span class="c"&gt;#   v:NotBefore: Aug  1 20:09:38 2024 GMT; NotAfter: Aug  1 20:09:38 2025 GMT&lt;/span&gt;
&lt;span class="c"&gt;# ---&lt;/span&gt;
&lt;span class="c"&gt;# YAY IT'S ASKING FOR CERTIFICATES!!!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we are 100% sure that TLS is enabled and requesting the certificates that match our previously generated keys. &lt;/p&gt;

&lt;h2&gt;
  
  
  5. Testing Encrypted Connections
&lt;/h2&gt;

&lt;p&gt;Too much stuff to configure, but how do we connect to it? Instead of using CQLSH (because it's a pain in the ass to set it up and &lt;a href="https://github.com/Daniel-Boll/scylla-javascript-driver/pull/34" rel="noopener noreferrer"&gt;I LITERALLY PREFERED TO MAKE IT WORK&lt;/a&gt; on Node than explain how to use it), we're going to use NodeJS for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;First, let's quick setup our driver by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @lambda-group/scylladb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you can already create a demo script pointing your nodes and don't forget to switch the ports at the connection string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cluster&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@lambda-group/scylladb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
    &lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;127.0.0.1:9142&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;truststoreFilepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/your/path/to/certificates/developer_cert.pem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;privateKeyFilepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/your/path/to/certificates/developer_key.pem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;caFilepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/your/path/to/certificates/developer_truststore.pem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;verifyMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VerifyMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;}});&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT address, port, username, driver_name, driver_version FROM system.clients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//  {&lt;/span&gt;
&lt;span class="c1"&gt;//     address: '127.0.0.1',&lt;/span&gt;
&lt;span class="c1"&gt;//     driver_name: 'scylla-js-driver',&lt;/span&gt;
&lt;span class="c1"&gt;//     driver_version: '0.0.1',&lt;/span&gt;
&lt;span class="c1"&gt;//     port: 58846,&lt;/span&gt;
&lt;span class="c1"&gt;//     username: 'developer' // We're logged in as the role 'developer'&lt;/span&gt;
&lt;span class="c1"&gt;//  }&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the output tells us, we managed to connect by only sending our key and certificate to the server! &lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;This "article/tutorial" took me a long time to write for several reasons, but one of them was to make sure of the content by going through it a few times.&lt;/p&gt;

&lt;p&gt;I also made a &lt;a href="https://github.com/DanielHe4rt/scylladb-role-tls-auth" rel="noopener noreferrer"&gt;demo (gh: danielhe4rt/scylladb-role-tls-auth)&lt;/a&gt; where you can run a &lt;code&gt;makefile&lt;/code&gt; command and set up all these steps. So if you got here somehow, please don't forget to drop a star :D&lt;/p&gt;

&lt;p&gt;Security is one of the topics that people are starting to pay more attention to nowadays and learning more about it was very exciting. Anyway, let me know what topics you would like to see in this series!&lt;/p&gt;

&lt;p&gt;Stay safe and don't forget to drink water!&lt;/p&gt;

</description>
      <category>scylladb</category>
      <category>database</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Build a Low-Latency Video Streaming App with ScyllaDB &amp; NextJS</title>
      <dc:creator>Attila Toth</dc:creator>
      <pubDate>Tue, 16 Jan 2024 13:47:14 +0000</pubDate>
      <link>https://dev.to/scylladb/how-to-build-a-low-latency-video-streaming-app-with-scylladb-nextjs-5bgi</link>
      <guid>https://dev.to/scylladb/how-to-build-a-low-latency-video-streaming-app-with-scylladb-nextjs-5bgi</guid>
      <description>&lt;p&gt;&lt;strong&gt;We created a new video streaming app to help you learn about ScyllaDB. This blog covers its features, tech stack, and data modeling.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We  just published a new ScyllaDB sample application, a video streaming app. The project is available on GitHub. This blog covers the video streaming application’s features, tech stack, and breaks down the data modeling process.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Video streaming app features
&lt;/h2&gt;

&lt;p&gt;The app has a minimal design with the most essential video streaming application features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    List all videos, sorted by creation date (home page)&lt;/li&gt;
&lt;li&gt;    List videos that you started watching&lt;/li&gt;
&lt;li&gt;    Watch video&lt;/li&gt;
&lt;li&gt;    Continue watching a video where you left off&lt;/li&gt;
&lt;li&gt;    Display a progress bar under each video thumbnail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technology stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;    Programming language: TypeScript&lt;/li&gt;
&lt;li&gt;    Database: ScyllaDB&lt;/li&gt;
&lt;li&gt;    Framework: NextJS (pages router)&lt;/li&gt;
&lt;li&gt;    Component library: Material_UI&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using ScyllaDB for low-latency video streaming applications
&lt;/h2&gt;

&lt;p&gt;ScyllaDB is a low-latency and high-performance NoSQL database compatible with Apache Cassandra and DynamoDB. It is well-suited to handle the large-scale data storage and retrieval requirements of video streaming applications. ScyllaDB has drivers in all the popular programming languages and, as this sample application demonstrates, it integrates well with modern web development frameworks like NextJS.&lt;/p&gt;

&lt;p&gt;Low latency in the context of video streaming services is crucial for delivering a seamless user experience. To lay the groundwork for high performance, you need to design a data model that fits your needs. Let’s continue with an example data modeling process to see what that looks like.&lt;br&gt;
Video streaming app data modeling&lt;/p&gt;

&lt;p&gt;In the ScyllaDB University Data Modeling course, we teach that NoSQL data modeling should always start with your application and queries first. Then you work backwards and create the schema based on the queries you want to run in your app. This process ensures that you create a data model that fits your queries and meets your requirements.&lt;/p&gt;

&lt;p&gt;With that in mind, let’s go over the queries that our video streaming app needs to run on each page load!&lt;/p&gt;
&lt;h3&gt;
  
  
  Page: Continue Watching
&lt;/h3&gt;

&lt;p&gt;On this page, you can list all the videos that they’ve started to watch. This view includes the video thumbnails and the progress bar under the thumbnail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc62dtz5q0bxwbcs2cd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc62dtz5q0bxwbcs2cd3.png" alt=" " width="793" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Query – get watch progress:&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="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;watch_history&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema – watch history 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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;watch_history&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;watched_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this query, it makes sense to define &lt;code&gt;user_id&lt;/code&gt; as the partition key because that is the filter we use to query the watch history table. Keep in mind that this schema might need to be updated later if there is a query that requires filtering on other columns beyond the &lt;code&gt;user_id&lt;/code&gt;. For now, though, this schema is correct for the defined query.&lt;/p&gt;

&lt;p&gt;Besides the progress value, the app also needs to fetch the actual metadata of each video (for example, the title and the thumbnail image). For this, the &lt;code&gt;video&lt;/code&gt; table has to be queried.&lt;/p&gt;

&lt;p&gt;Query – get video metadata:&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;video&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we use the “IN” operator and not “=” because we need to fetch a list of videos not just a single video.&lt;/p&gt;

&lt;p&gt;Schema – video 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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the video table, let’s define the &lt;code&gt;id&lt;/code&gt; as the partition key because that’s the only filter we use in the query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page: Watch Video
&lt;/h3&gt;

&lt;p&gt;If you click on any of the “Watch” buttons, they will be redirected to a page with a video player where they can start and pause the video.&lt;/p&gt;

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

&lt;p&gt;Query – get video content:&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;video&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very similar query to the one that runs on the Continue Watching page. Thus, the same schema will work just fine for this query as well.&lt;/p&gt;

&lt;p&gt;Schema – video 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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Page: Most Recent Videos
&lt;/h3&gt;

&lt;p&gt;Finally, let’s break down the Most Recent Videos page, which is the home page of the application. We analyze this page last because it is the most complex one from a data modeling perspective. This page lists ten of the most recently uploaded videos that are available in the database ordered by the video creation date.&lt;/p&gt;

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

&lt;p&gt;We will have to fetch these videos in two steps: first get the timestamps, then get the actual video content.&lt;/p&gt;

&lt;p&gt;Query – get the most recent ten videos’ timestamp:&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top10&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;recent_videos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might notice that we use a custom function called &lt;code&gt;top10()&lt;/code&gt;. This is not a standard function in ScyllaDB. It’s a UDF (user-defined function) that we created to solve this data modeling problem. This function returns an array of the most recent &lt;code&gt;created_at&lt;/code&gt; timestamps in the table. Creating a new UDF in ScyllaDB can be a great way to solve your unique data modeling challenges.&lt;/p&gt;

&lt;p&gt;These timestamp values can then be used to query the actual video content that we want to show on the page.&lt;/p&gt;

&lt;p&gt;Query – get metadata for those videos:&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;recent_videos&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="o"&gt;?&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;Schema – recent videos:&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="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;recent_videos_view&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&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;streaming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt;
   &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the recent videos materialized view, the &lt;code&gt;created_at&lt;/code&gt; column is the primary key because we filter by that column in our first query to get the most recent timestamp values. Be aware that in some cases, this can cause a hot partition.&lt;/p&gt;

&lt;p&gt;Furthermore, the UI also shows a small progress bar under each video’s thumbnail which indicates the progress you made watching that video. To fetch this value for each video, the app has to query the &lt;code&gt;watch history&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;Query – get watch progress for each video:&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="n"&gt;progress&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;watch_history&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema – watch history:&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;TABLE&lt;/span&gt; &lt;span class="n"&gt;watch_history&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;watched_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have noticed that the watch history table was already used in a previous query to fetch data. Now this time, the schema has to be modified slightly to fit this query. Let’s add &lt;code&gt;video_id&lt;/code&gt; as a clustering key. This way, the query to fetch watch progress will work correctly.&lt;/p&gt;

&lt;p&gt;That’s it. Now let’s see the final database schema!&lt;br&gt;
Final database schema&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="n"&gt;KEYSPACE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;streaming&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;replication&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'class'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'NetworkTopologyStrategy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'replication_factor'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'3'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;video&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;watch_history&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;watched_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;streaming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recent_videos&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;view&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cql&lt;/span&gt; &lt;span class="n"&gt;hosted&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="err"&gt;❤&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;GitHub&lt;/span&gt;

&lt;span class="k"&gt;User&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;defined&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;Most&lt;/span&gt; &lt;span class="n"&gt;Recent&lt;/span&gt; &lt;span class="n"&gt;Videos&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;

&lt;span class="c1"&gt;-- Create a UDF for recent videos&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;state_f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;CALLED&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;INPUT&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;lua&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
   &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;nil&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
       &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;


   &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
       &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;




&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;reduce_f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acc1&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc2&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;CALLED&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;INPUT&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;lua&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
   &lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
   &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
   &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

   &lt;span class="n"&gt;while&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="k"&gt;result&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="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;acc1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;acc2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
           &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
           &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
       &lt;span class="k"&gt;else&lt;/span&gt;
           &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
           &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;result&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;



&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;AGGREGATE&lt;/span&gt; &lt;span class="n"&gt;top10&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SFUNC&lt;/span&gt; &lt;span class="n"&gt;state_f&lt;/span&gt;
&lt;span class="k"&gt;STYPE&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;REDUCEFUNC&lt;/span&gt; &lt;span class="n"&gt;reduce_f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This UDF uses Lua, but you could also use Wasm to create UDFs in ScyllaDB. Creating the function make sure to enable UDFs in the scylla.yaml configuration file (location: /etc/scylla/scylla.yaml).&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone the repo and get started!
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/scylladb/video-streaming
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_BASE_URL="http://localhost:8000"
SCYLLA_HOSTS="172.17.0.2"
SCYLLA_USER="scylla"
SCYLLA_PASSWD="xxxxx"
SCYLLA_KEYSPACE="streaming"
SCYLLA_DATACENTER="datacenter1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Migrate the database and insert sample data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We hope you enjoy our video streaming app and it helps you build low-latency and high-performance applications with ScyllaDB. If you want to keep on learning, check out ScyllaDB University where we have free courses on data modeling, ScyllaDB drivers, and much more! If you have questions about the video streaming sample app or ScyllaDB, go to our forum and let’s discuss!&lt;/p&gt;

&lt;p&gt;More ScyllaDB sample applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;    &lt;a href="https://iot.scylladb.com/" rel="noopener noreferrer"&gt;CarePet – IoT&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://cloud-getting-started.scylladb.com/" rel="noopener noreferrer"&gt;Cloud getting started guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;    &lt;a href="https://feature-store.scylladb.com/" rel="noopener noreferrer"&gt;Feature Store&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>scylladb</category>
      <category>nosql</category>
      <category>nextjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Performance e elegância! Escrevendo uma CLI CRUD utilizando ScyllaDB e Ruby</title>
      <dc:creator>Cherry Ramatis</dc:creator>
      <pubDate>Mon, 28 Aug 2023 11:24:53 +0000</pubDate>
      <link>https://dev.to/scylladb/performance-e-elegancia-escrevendo-uma-cli-crud-utilizando-scylladb-e-ruby-1452</link>
      <guid>https://dev.to/scylladb/performance-e-elegancia-escrevendo-uma-cli-crud-utilizando-scylladb-e-ruby-1452</guid>
      <description>&lt;p&gt;Boas pessoas desenvolvedoras precisam saber fazer CRUD não é mesmo? Então já pensou em ser capaz de produzir um CRUD com um banco de dados NoSQL montado para &lt;strong&gt;alta&lt;/strong&gt; escalabilidade e ainda mais utilizando uma linguagem elegante e simples? Não? Pois muito que bem, nesse artigo você vai aprender como construir uma CLI utilizando a gem &lt;code&gt;dry-cli&lt;/code&gt; consumindo o banco de dados ScyllaDB com a gem &lt;code&gt;cassandra-driver&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Para saber um pouco mais sobre &lt;strong&gt;o que é ScyllaDB&lt;/strong&gt; e em quais contextos essa ferramenta é útil, recomendo ler a &lt;a href="https://cloud.docs.scylladb.com/stable/scylladb-basics/" rel="noopener noreferrer"&gt;documentação oficial&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Esse artigo assume conhecimento básico com bancos de dados visto que o objetivo vai ser focar na utilização em conjunto com ruby, para mais informações especificas sobre Scylla DB recomendo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Os artigos criados pelo Developer Advocate da ScyllaDB DanielHe4rt no &lt;a href="https://dev.to/danielhe4rt"&gt;Dev.To&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Os cursos gratuitos promovidos pela própria ScyllaDB no &lt;a href="https://university.scylladb.com/" rel="noopener noreferrer"&gt;ScyllaDB University&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Iniciando o projeto&lt;/li&gt;
&lt;li&gt;Definindo a camada de injeção de dependências&lt;/li&gt;
&lt;li&gt;Definindo o boilerplate para nossa CLI&lt;/li&gt;
&lt;li&gt;Implementando nossos comandos&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Iniciando o projeto
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Resolvendo bibliotecas de sistema para instalar o driver
&lt;/h3&gt;

&lt;p&gt;A gem que vamos utilizar para comunicar com o ScyllaDB se chama &lt;a href="https://github.com/datastax/ruby-driver/" rel="noopener noreferrer"&gt;cassandra-driver&lt;/a&gt;, infelizmente ela exige bibliotecas de sistema relacionadas ao banco de dados cassandra para serem instaladas, a forma mais simples de ter essas bibliotecas instaladas é instalar o próprio cassandra na maquina:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Não vamos utilizar o banco cassandra para nada na prática desse artigo, apenas precisamos do pacote instalado para que a gem consiga buscar as bibliotecas necessárias ao seu funcionamento. Visto que o ScyllaDB é baseado originalmente no Cassandra podemos utilizar tranquilamente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para instalar o cassandra no MacOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;cassandra
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para instalar o cassandra no Linux(Debian):&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="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; cassandra
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso sua Distro/Sistema operacional não tenha sido listada, recomendo sempre recorrer a &lt;a href="https://cassandra.apache.org/doc/latest/cassandra/getting_started/installing.html" rel="noopener noreferrer"&gt;Documentação Oficial&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Iniciando o projeto
&lt;/h3&gt;

&lt;p&gt;Com as bibliotecas de sistema corretamente instaladas, podemos iniciar nosso projeto utilizando &lt;a href="https://bundler.io/" rel="noopener noreferrer"&gt;bundler&lt;/a&gt;:&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="nb"&gt;mkdir &lt;/span&gt;project_scylla &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;project_scylla &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bundle init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 Instalando dependências de projeto
&lt;/h3&gt;

&lt;p&gt;Agora podemos finalmente adicionar as gems necessárias para o projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bundle add cassandra-driver
bundle add dry-auto_inject
bundle add dry-system
bundle add zeitwerk
bundle add dry-cli
bundle add dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/datastax/ruby-driver/" rel="noopener noreferrer"&gt;cassandra-driver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dry-rb/dry-auto_inject" rel="noopener noreferrer"&gt;dry-auto_inject&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dry-rb/dry-system" rel="noopener noreferrer"&gt;dry-system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fxn/zeitwerk" rel="noopener noreferrer"&gt;zeitwerk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bkeepers/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dry-rb/dry-cli" rel="noopener noreferrer"&gt;dry-cli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.4 Definindo um REPL
&lt;/h3&gt;

&lt;p&gt;Como somos bons rubistas devemos fazer o setup do nosso IRB como a primeira ação no projeto correto?&lt;/p&gt;

&lt;p&gt;Para isso crie um script executável em &lt;code&gt;bin/console&lt;/code&gt; com:&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="nb"&gt;touch &lt;/span&gt;bin/console &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x bin/console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse arquivo vamos incluir um setup básico com IRB + Dotenv:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dotenv/load'&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'irb'&lt;/span&gt;
&lt;span class="no"&gt;IRB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excelente! Agora temos o básico do nosso setup concluído! Vamos prosseguir definindo a camada de injeção de dependência e um provedor para incorporar a funcionalidade do ScyllaDB.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Definindo a camada de injeção de dependências
&lt;/h2&gt;

&lt;p&gt;Nesse artigo vamos seguir um modelo igual mostrado no meu &lt;a href="https://dev.to/cherryramatis/creating-a-sinatra-api-with-system-wide-dependency-injection-using-dry-system-10mp"&gt;artigo sobre injeção de dependências com sinatra&lt;/a&gt;, para um detalhamento maior de como esse setup e feito e o porquê é feito, por favor referencie a esse link.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Criando o container principal
&lt;/h3&gt;

&lt;p&gt;Como descrito no artigo referenciada acima, para nossa camada de injeção de dependências funcionar precisamos definir um container principal que vai servir de referencia para buscar dependências e registrar novos providers.&lt;/p&gt;

&lt;p&gt;Agora vamos criar um arquivo em &lt;code&gt;config/application.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/system'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Container&lt;/span&gt;
  &lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Pathname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component_dirs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Loader&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Autoloading&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component_dirs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="s1"&gt;'lib'&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component_dirs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="s1"&gt;'config'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Zeitwerk&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lib'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neste arquivo recém criado estamos definindo a localização dos componentes ao longo da nossa aplicação, a principio isso vai definir auto loading desses arquivos para que não precisemos usar &lt;code&gt;require&lt;/code&gt; sempre que usamos alguma classe de algum arquivo.&lt;/p&gt;

&lt;p&gt;Com o container definido já podemos modificar nosso script de REPL para que contenha o método &lt;code&gt;finalize!&lt;/code&gt; da nossa aplicação recém definida.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dotenv/load'&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../config/application'&lt;/span&gt;

&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalize!&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'irb'&lt;/span&gt;
&lt;span class="no"&gt;IRB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Também vamos criar um arquivo &lt;code&gt;main.rb&lt;/code&gt; na raiz do nosso projeto apenas com o &lt;code&gt;finalize!&lt;/code&gt; para servir como um ponto de entrada na nossa CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dotenv/load'&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../config/application'&lt;/span&gt;

&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalize!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Criando o provider de banco de dados
&lt;/h3&gt;

&lt;p&gt;Agora que temos um container principal para nossa aplicação, podemos definir o único provider desse projeto que vai ser a conexão com o ScyllaDB.&lt;/p&gt;

&lt;p&gt;Para isso crie um arquivo em &lt;code&gt;config/provider/database.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'cassandra'&lt;/span&gt;
    &lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../../lib/migration_utils'&lt;/span&gt;
    &lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../constants'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Cassandra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_USER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;password: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_PASSWORD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;hosts: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DB_HOSTS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;

    &lt;span class="no"&gt;MigrationUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_keyspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;session: &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;MigrationUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keyspace_exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;session: &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;MigrationUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;session: &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;MigrationUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;session: &lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'database.connection'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para as credenciais recomendo utilizar o serviço &lt;a href="https://cloud.scylladb.com/" rel="noopener noreferrer"&gt;cloud ScyllaDB&lt;/a&gt;, nele você consegue criar um cluster super rápido e ganhar acesso a todas as credenciais de maneira super simples.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Um exemplo .env pode ser visto abaixo:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB_USER=scylla
DB_PASSWORD=password
DB_HOSTS=node-0.amazonaws,node-1.amazonaws,node-2.amazonaws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso você tenha lido o artigo sobre injeção de dependências mencionado acima esse provider parece bem simples, mas também temos o uso de algumas classes novas que ainda não criamos e portanto vamos verificá-las passo a passo:&lt;/p&gt;

&lt;h4&gt;
  
  
  Definindo as constantes para nosso projeto
&lt;/h4&gt;

&lt;p&gt;Nessa aplicação vamos manter nomes de keyspace e tabela definidos em constantes, você pode alterar para receber por parâmetro ou variável de ambiente, mas para simplicidade vamos deixar apenas com uma constante mesmo.&lt;/p&gt;

&lt;p&gt;Crie um arquivo em &lt;code&gt;config/constants.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'media_player'&lt;/span&gt;
&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'playlist'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Criando uma classe utilitária para criar nosso banco
&lt;/h4&gt;

&lt;p&gt;Como podemos ver no exemplo do provider anterior, estamos usando a classe &lt;code&gt;MigrationUtils&lt;/code&gt; para produzir atividades comuns para a inicialização do nosso keyspace e da nossa tabela.&lt;/p&gt;

&lt;p&gt;Agora vamos seguir passo a passo pelos métodos necessários para a criação do nosso keyspace e tabelas.&lt;/p&gt;

&lt;h5&gt;
  
  
  Checando se um keyspace ou tabela existe
&lt;/h5&gt;

&lt;p&gt;Antes de prosseguirmos com a criação dos nossos keyspaces e tabelas, é crucial verificar se eles já existem a fim de evitar a execução desnecessária da função. Para isso, vamos implementar métodos booleanos da seguinte maneira:&lt;/p&gt;

&lt;p&gt;Primeiramente, criaremos um arquivo chamado &lt;code&gt;migration_utils.rb&lt;/code&gt;, localizado em&lt;br&gt;
&lt;code&gt;lib/migration_utils.rb&lt;/code&gt;, e o preencheremos com o código descrito abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MigrationUtils&lt;/span&gt;
  &lt;span class="c1"&gt;# @param session [Cassandra#Cluster]&lt;/span&gt;
  &lt;span class="c1"&gt;# @return [Boolean]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keyspace_exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      select keyspace_name from system_schema.keyspaces WHERE keyspace_name=?
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;arguments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# @param session [Cassandra#Cluster]&lt;/span&gt;
  &lt;span class="c1"&gt;# @return [Boolean]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;table_exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      select keyspace_name, table_name from system_schema.tables where keyspace_name = ? AND table_name = ?
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;arguments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui, estamos implementando métodos estáticos que serão executados antes de configurarmos a camada de injeção de dependência. Por essa razão, aceitamos a sessão do banco de dados como parâmetro para essas funções. Com essa sessão, podemos usar o método &lt;code&gt;execute_async&lt;/code&gt; para enviar uma consulta CQL. Esse método também nos permite utilizar placeholders &lt;code&gt;?&lt;/code&gt; para os parâmetros e especificar os valores em um objeto &lt;code&gt;arguments: []&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Como esse método funciona de forma assíncrona, precisamos usar o método &lt;code&gt;join&lt;/code&gt; para esperar essa &lt;a href="https://docs.datastax.com/en/developer/ruby-driver/3.2/api/cassandra/future/" rel="noopener noreferrer"&gt;Future&lt;/a&gt; finalizar e nos retornar um objeto. Com o objeto em mãos, podemos acessar a propriedade rows sendo esta uma lista contendo todas as linhas referentes a query mostrada acima.&lt;/p&gt;

&lt;p&gt;Para finalizar a implementação do método retornando um booleano, vamos checar se a lista esta vazia ou não checando se o tamanho da lista é zero com &lt;code&gt;.size.zero?&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Precisamos usar o &lt;code&gt;.size.zero?&lt;/code&gt; pois o retorno é um &lt;a href="https://ruby-doc.org/core-2.6/Enumerator.html" rel="noopener noreferrer"&gt;Enumerator&lt;/a&gt;, que não possui o método &lt;code&gt;.empty?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  Criando os keyspaces e tabelas
&lt;/h5&gt;

&lt;p&gt;Agora que criamos métodos responsáveis por checar a existência de um keyspace e uma tabela, precisamos criar os métodos que vão criar eles caso já não existam correto?&lt;/p&gt;

&lt;p&gt;Para isso, vamos continuar trabalhando na classe localizada em &lt;code&gt;lib/migration_utils.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MigrationUtils&lt;/span&gt;
  &lt;span class="c1"&gt;# @param session [Cassandra#Cluster]&lt;/span&gt;
  &lt;span class="c1"&gt;# @return [void]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_keyspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      CREATE KEYSPACE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
      WITH replication = {
        'class': 'NetworkTopologyStrategy',
        'replication_factor': '3'
      }
      AND durable_writes = true
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# @param session [Cassandra#Cluster]&lt;/span&gt;
  &lt;span class="c1"&gt;# @return [void]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
      CREATE TABLE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; (
        id uuid,
        title text,
        album text,
        artist text,
        created_at timestamp,
        PRIMARY KEY (id, created_at)
      );
&lt;/span&gt;&lt;span class="no"&gt;    SQL&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui vamos seguir o mesmo padrão onde recebemos o &lt;code&gt;session&lt;/code&gt; como parâmetro e o usamos para executar uma query assíncrona com &lt;code&gt;execute_async&lt;/code&gt;, como não precisamos lidar com o retorno dessas queries podemos apenas usar o &lt;code&gt;join&lt;/code&gt; para esperar ela finalizar.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Para qualquer duvida referente as queries em si recomendo fortemente os links mencionados da &lt;a href="https://university.scylladb.com/" rel="noopener noreferrer"&gt;ScyllaDB University&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.3 Carregando o novo provider na nossa aplicação
&lt;/h3&gt;

&lt;p&gt;Agora que definimos e entendemos o nosso provider de banco de dados, precisamos carregá-lo para que seja injetado nos nossos dois pontos principais da aplicação:&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;main.rb&lt;/code&gt; vamos adicionar o require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'config/provider/database'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E no &lt;code&gt;bin/console&lt;/code&gt; a mesma coisa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../config/provider/database'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Definindo o boilerplate para nossa CLI
&lt;/h2&gt;

&lt;p&gt;Agora que temos uma camada de banco de dados e auto requiring pronta pra ser usada, vamos utilizar a outra grande gem desse projeto para definir os comandos da nossa CLI. Vem ai &lt;code&gt;dry-cli&lt;/code&gt; senhoras e senhores!&lt;/p&gt;

&lt;p&gt;Nesse primeiro momento vamos nos preocupar em apenas definir o boilerplate para nossa CLI, sem nos preocupar com a implementação real certo?&lt;/p&gt;

&lt;p&gt;Para isso, vamos definir o modulo que vai registrar todos os comandos, localizado em &lt;code&gt;lib/cli.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;

  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'add'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Add&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse modulo inicial podemos usar uma &lt;a href="https://dev.to/vinistock/write-a-simple-dsl-in-ruby-1jgi"&gt;DSL&lt;/a&gt; para registrar novos comandos com o &lt;code&gt;register&lt;/code&gt;, essa DSL é fornecida ao extender o modulo &lt;code&gt;Dry::CLI::Registry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Agora que registramos um comando &lt;code&gt;Add&lt;/code&gt;, vamos criar a classe referente a ele localizada em &lt;code&gt;lib/cli/commands/add.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Add&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'This command add a new song to the playlist'&lt;/span&gt;

      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The title of the song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the album of that song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the artist of band'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Add command -&amp;gt; Title: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Album: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Artist: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nessa classe podemos ver outra DSL fornecida por herdar a classe &lt;code&gt;Dry::CLI::Command&lt;/code&gt;, com ela podemos prover uma descrição para o comando usando &lt;code&gt;desc&lt;/code&gt;, declarar quais argumentos vamos receber para esse comando junto com seu tipo, validação e descrição com &lt;code&gt;argument&lt;/code&gt; e muito mais!&lt;/p&gt;

&lt;p&gt;Logo após definir os metadados do nosso comando, definimos um método &lt;code&gt;call&lt;/code&gt; que vai receber os argumentos definidos como parâmetros nomeados.&lt;/p&gt;

&lt;p&gt;Em nosso arquivo &lt;code&gt;main.rb&lt;/code&gt; podemos inicializar a nossa CLI com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Cli&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, executando nossa aplicação com &lt;code&gt;ruby main.rb&lt;/code&gt; devemos ter o seguinte output:&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;ruby main.rb
Commands:
  main.rb add TITLE ALBUM ARTIST                 &lt;span class="c"&gt;# This command add a new song to the playlist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Implementando nossos comandos
&lt;/h2&gt;

&lt;p&gt;Agora que temos um boilerplate e um entendimento básico quanto ao funcionamento da gem &lt;code&gt;dry-cli&lt;/code&gt; podemos nos preocupar em implementar alguns comandos simples para o nosso CRUD.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Implementando o primeiro comando Add
&lt;/h3&gt;

&lt;p&gt;Como já temos a classe para esse comando criada, vamos apenas começar a trabalhar na implementação do método &lt;code&gt;call&lt;/code&gt; da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Add&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'This command add a new song to the playlist'&lt;/span&gt;

      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The title of the song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the album of that song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the artist of band'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="vi"&gt;@repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'database.connection'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
          INSERT INTO &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; (id,title,artist,album,created_at) VALUES (now(),?,?,?,?);
&lt;/span&gt;&lt;span class="no"&gt;        SQL&lt;/span&gt;

        &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;arguments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;

        &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Song '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' from artist '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' Added!"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesta implementação, primeiro vamos injetar nossa conexão com o banco de dados por meio da camada de injeção de dependência no construtor da classe e então iremos utilizar o método já conhecido &lt;code&gt;execute_async&lt;/code&gt; para inserir um novo dado na tabela correta.&lt;/p&gt;

&lt;p&gt;Um ponto importante a ressaltar é o uso da função &lt;code&gt;now()&lt;/code&gt; na query, essa função é nativa do banco de dados e serve para inserir um novo UUID no padrão esperado pelo ScyllaDB, dessa forma não precisamos lidar com geração de UUID pelo lado da linguagem.&lt;/p&gt;

&lt;p&gt;Bem simples certo? vamos continuar com os próximos comandos do nosso CRUD seguindo a mesma arquitetura já proposta.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Implementando o comando List
&lt;/h3&gt;

&lt;p&gt;Após ter elaborado um comando para adicionar novas músicas, avançaremos para a criação de um comando destinado a listar todas as músicas já criadas. Para isso vamos criar um arquivo em &lt;code&gt;lib/cli/commands/list.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'This command shows all the created songs'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="vi"&gt;@repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'database.connection'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
          SELECT * FROM &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;;
&lt;/span&gt;&lt;span class="no"&gt;        SQL&lt;/span&gt;

        &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;MSG&lt;/span&gt;&lt;span class="sh"&gt;
            ID: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Song Name: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Album: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'album'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Created At: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;          MSG&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Novamente, estamos aqui fazendo uma query &lt;code&gt;SELECT&lt;/code&gt; simples e percorrendo pelos resultados no array &lt;code&gt;rows&lt;/code&gt; para mostrar ao usuário final, é importante ressaltar que os fields acessados com &lt;code&gt;row['title']&lt;/code&gt; correspondem aos fields que criamos quando fizemos o &lt;code&gt;CREATE TABLE&lt;/code&gt; no provider.&lt;/p&gt;

&lt;p&gt;Não podemos esquecer de registrar esse comando então vamos modificar o arquivo &lt;code&gt;lib/cli.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;

  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'add'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Add&lt;/span&gt;
  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;List&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;= Novo comando&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfeito! Agora podemos tanto adicionar quanto listar musicas :D&lt;/p&gt;

&lt;p&gt;Uma simples demo do que temos agora tanto com add quanto list:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/604950" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2F604950.svg" width="1187" height="970"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Implementando o comando Delete
&lt;/h3&gt;

&lt;p&gt;Vamos explorar agora um comando bastante interessante. Neste caso, iremos exibir uma lista de músicas acompanhadas por seus indices, permitindo ao usuário selecionar uma música específica através da posição correspondente.&lt;/p&gt;

&lt;p&gt;Para realizar isso, vamos desenvolver o comando e implementar um método que seja responsável por listar as músicas com suas respectivas posições numéricas. Além disso, iremos também aguardar o input do usuário.&lt;/p&gt;

&lt;p&gt;A seguir, vamos criar um novo arquivo localizado em &lt;code&gt;lib/cli/commands/delete.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Delete&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'This command will prompt for a song to be deleted and then delete it'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="vi"&gt;@repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'database.connection'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;
        &lt;span class="n"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT * FROM &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rows&lt;/span&gt;

        &lt;span class="n"&gt;song_to_delete_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select_song_to_delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;

        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
          DELETE FROM &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; WHERE id = ?
&lt;/span&gt;&lt;span class="no"&gt;        SQL&lt;/span&gt;

        &lt;span class="n"&gt;song_to_delete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;song_to_delete_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;arguments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;song_to_delete&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="kp"&gt;private&lt;/span&gt;

      &lt;span class="c1"&gt;# @param songs [Array&amp;lt;Hash&amp;gt;]&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_song_to_delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;DESC&lt;/span&gt;&lt;span class="sh"&gt;
            &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)  Song: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Album: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'album'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Artist: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'artist'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Created At: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;          DESC&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;'Select a song to be deleted: '&lt;/span&gt;
        &lt;span class="vg"&gt;$stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No método &lt;code&gt;select_song_to_delete&lt;/code&gt; nós recebemos uma lista de musicas e percorremos por ela com um índice usando o método &lt;code&gt;each_with_index&lt;/code&gt;, dessa forma conseguimos mostrar uma mensagem no modelo &lt;code&gt;1) Song: | Album: | Artist: | Created At:&lt;/code&gt;. Ainda nesse método esperamos o input do usuário com o &lt;code&gt;$stdin.gets.chomp&lt;/code&gt; e repassamos no retorno como um integer convertendo com &lt;code&gt;.to_i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Já no método &lt;code&gt;call&lt;/code&gt;, começamos fazendo uma consulta para obter todas as músicas registradas na tabela. Em seguida, passamos esse conjunto de dados para o método que permite ao usuário selecionar uma em especifico e retornar o índice da mesma. Usamos esse índice para escolher uma música específica no array e, em seguida, executamos uma consulta &lt;code&gt;DELETE&lt;/code&gt; para removê-la.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Antes de selecionar um item especifico, precisamos transformar em um array com &lt;code&gt;.to_a&lt;/code&gt; visto que essas linhas são um &lt;a href="https://ruby-doc.org/core-2.6/Enumerator.html" rel="noopener noreferrer"&gt;Enumerator&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uma demo mostrando o uso prático do comando:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/604952" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2F604952.svg" width="1187" height="970"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Não podemos esquecer de registrar esse comando então vamos modificar o arquivo &lt;code&gt;lib/cli.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Registry&lt;/span&gt;

  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'add'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Add&lt;/span&gt;
  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;List&lt;/span&gt;
  &lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="s1"&gt;'delete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Commands&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Delete&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;= Novo comando&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Implementando o comando Update
&lt;/h3&gt;

&lt;p&gt;Agora vamos criar um comando que vai juntar todos os conceitos mostrados&lt;br&gt;
anteriormente, este será um &lt;code&gt;update&lt;/code&gt; e vai performar da seguinte forma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vamos aceitar parâmetros como &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;album&lt;/code&gt;, &lt;code&gt;artist&lt;/code&gt; para usar como parte do update (semelhante ao que fizemos no comando &lt;code&gt;add&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Vamos mostrar para o usuário uma lista das musicas cadastradas e esperar o input do usuário com um índice (semelhante ao que fizemos no comando &lt;code&gt;delete&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfeito! Vamos criar um comando localizado em &lt;code&gt;lib/cli/commands/update.rb&lt;/code&gt; com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dry/cli'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Cli&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Commands&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Update&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLI&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Command&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'This command will prompt for a song to be updated and use the argument information to updated it'&lt;/span&gt;

      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The title of the song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the album of that song'&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: :string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The name of the artist of band'&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;
        &lt;span class="vi"&gt;@repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'database.connection'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="n"&gt;songs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"SELECT * FROM &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rows&lt;/span&gt;

        &lt;span class="n"&gt;song_to_update_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;select_song_to_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;

        &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt;&lt;span class="sh"&gt;
          UPDATE &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;KEYSPACE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; SET title = ?, artist = ?, album = ? WHERE id = ? AND created_at = ?
&lt;/span&gt;&lt;span class="no"&gt;        SQL&lt;/span&gt;

        &lt;span class="n"&gt;song_to_update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;song_to_update_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="vi"&gt;@repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;arguments: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;song_to_update&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;song_to_update&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;]]).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="kp"&gt;private&lt;/span&gt;

      &lt;span class="c1"&gt;# @param songs [Array&amp;lt;Hash&amp;gt;]&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_song_to_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
        &lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_with_index&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
          &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;~&lt;/span&gt;&lt;span class="no"&gt;DESC&lt;/span&gt;&lt;span class="sh"&gt;
            &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;)  Song: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Album: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'album'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Artist: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'artist'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt; | Created At: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;          DESC&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;'Select a song to be updated: '&lt;/span&gt;
        &lt;span class="vg"&gt;$stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como pode ver, esse comando é realmente uma junção entre os conceitos do comando &lt;code&gt;add&lt;/code&gt; (arguments) e conceitos do comando &lt;code&gt;delete&lt;/code&gt; (método select pelo índice).&lt;/p&gt;

&lt;p&gt;Sobre a query &lt;code&gt;update&lt;/code&gt; é importante ressaltar que no ScyllaDB temos duas primary keys(nesse caso &lt;code&gt;id&lt;/code&gt; e &lt;code&gt;created_at&lt;/code&gt;), portanto precisamos utilizar ambas as informações para que o banco de dados ache corretamente nossa linha.&lt;/p&gt;

&lt;p&gt;Uma demo mostrando o funcionamento do comando:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://asciinema.org/a/604955" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fasciinema.org%2Fa%2F604955.svg" width="1187" height="970"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Espero que esse artigo tenha sido útil! Tentei ao máximo focar na integração entre Ruby e ScyllaDB pois não encontrei nada com uma linguagem simples para iniciantes. Para duvidas direcionadas especificamente ao ScyllaDB recomendo fortemente os artigos do &lt;a href="https://dev.to/danielhe4rt"&gt;DanielHe4rt&lt;/a&gt; e o &lt;a href="https://university.scylladb.com/" rel="noopener noreferrer"&gt;ScyllaDB University&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;E não se esqueça de dar continuidade a este projeto que você acaba de criar!  Deixo como um desafio a busca por aprimoramentos na arquitetura que construímos. Abaixo, elenco alguns pontos de melhoria evidentes, porém, encorajo você a identificar outros que possam ter passado despercebidos por mim. A prática é o segredo para se tornar uma pessoa desenvolvedora habilidosa!&lt;br&gt;
😄&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As funções &lt;code&gt;select_song_to_delete&lt;/code&gt; e &lt;code&gt;select_song_to_update&lt;/code&gt; são iguais, talvez mover ela para algum local comum?&lt;/li&gt;
&lt;li&gt;No comando &lt;code&gt;add&lt;/code&gt; nós printamos uma mensagem de sucesso para o usuário, mas não seguimos isso nos outros comandos, podemos melhorar essa experiencia de usuário?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;May the force be with you! 🍒&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>beginners</category>
      <category>ruby</category>
      <category>scylladb</category>
    </item>
    <item>
      <title>Database 101: How does migration between databases work in the real world?</title>
      <dc:creator>Daniel Reis</dc:creator>
      <pubDate>Mon, 21 Aug 2023 13:05:09 +0000</pubDate>
      <link>https://dev.to/scylladb/database-101-how-does-migration-between-databases-work-in-the-real-world-24dn</link>
      <guid>https://dev.to/scylladb/database-101-how-does-migration-between-databases-work-in-the-real-world-24dn</guid>
      <description>&lt;p&gt;Recently my new task at ScyllaDB is to study how to Migrate data between Databases and as I promised before, I'll keep you folks about my latest studies.&lt;/p&gt;

&lt;p&gt;If you’re just getting started with databases in general or databases, you might want to start off by reading my initial post, &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database 101: Data Consistency for Beginners&lt;/a&gt; for Beginners. That article captures my own exploration of how many database paradigms exist as I look far beyond my previous experience with just SQL and MySQL. I’m keeping track of my studies in this &lt;strong&gt;Database 101&lt;/strong&gt; series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Prologue&lt;/li&gt;
&lt;li&gt;2. Database Migrations: How to Start&lt;/li&gt;
&lt;li&gt;3. Why are we doing this migration?&lt;/li&gt;
&lt;li&gt;
4. Proof of Concept: where to start?

&lt;ul&gt;
&lt;li&gt;4.1 PoC: Same Paradigm&lt;/li&gt;
&lt;li&gt;4.2 PoC: Different Paradigm&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;5. Testing it before it's too late! (seriously)&lt;/li&gt;

&lt;li&gt;

6. Migration Strategies

&lt;ul&gt;
&lt;li&gt;6.1 Strategy: Cold Migration&lt;/li&gt;
&lt;li&gt;6.2 Strategy: Hot Migration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;7. Final Considerations&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Prologue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhedkuwqz93czrpsowb9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhedkuwqz93czrpsowb9u.png" alt="Pull Request with 532 files changed which broke half of the system" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I started my journey in this "database environment", I have asked myself so many questions that probably will take a few years to be answered. But one of those questions is how to properly perform a database migration, and I still don't have the answer.&lt;/p&gt;

&lt;p&gt;Actually, when I migrated the He4rt Developers (community I lead) bot, I did my best to make everything on the backend and the database model as perfect as possible. But when I had to migrate everything, it was a real mess. I decided to use the &lt;a href="https://github.com/he4rt/he4rt-bot-api/pull/53" rel="noopener noreferrer"&gt;"Big Bang" (pull request with 532 files changed)&lt;/a&gt; migration approach, and it was one of my bad decisions as an open source maintainer. &lt;/p&gt;

&lt;p&gt;At that time, I didn't have any knowledge about this topic (database migrations) at all. But I learned a lesson that I'm going to teach you in this article. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Database Migrations: How to Start
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nk5v6wbtbr7uyu5z8jg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nk5v6wbtbr7uyu5z8jg.jpg" alt="Drop Database Meme" width="600" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you ever considered receive such a task, of investigate and research how properly migrate data from a database to another, you should know a few things before do anything related to code. What do I mean by that? Just throw queries between databases is not where you're going to start the investigation. &lt;/p&gt;

&lt;p&gt;Let's just ask ourselves a few questions before everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the motivation for database migration?&lt;/li&gt;
&lt;li&gt;What migration approach should we use on the project?&lt;/li&gt;
&lt;li&gt;Are we going to jump from one paradigm to another?&lt;/li&gt;
&lt;li&gt;Does the new database have all the support we need for the things we're currently using?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these four questions in mind, we can start researching until answer all of that and then put hands on queries/code.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Why are we doing this migration?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvejgn8wj84z927gm9u0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvejgn8wj84z927gm9u0.png" alt="Image description" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we talk about replacing any part of the stack of a running project, the reason needs to be clear to the entire team that is planning this huge movement inside the product. &lt;/p&gt;

&lt;p&gt;So what are you looking for in the replacement? A few possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower latency for I/O operations;&lt;/li&gt;
&lt;li&gt;Cheaper database to maintain;&lt;/li&gt;
&lt;li&gt;Scalable database to keep up with the product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just a reminder that these are just a few items on the giant list of motivations for why to replace a database, ok? Ask your team/leaders all the details you need to understand the motivation and make sure this is what you're looking for!&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Proof of Concept: where to start?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F017tpffwvpntonwd8fa7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F017tpffwvpntonwd8fa7.png" alt="Persons discussing the better approach to solve a problem" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good! We have a clear idea of why we are doing this. Now let's figure out the things that will be important to make this work. Do you know the difference between each &lt;strong&gt;database paradigm&lt;/strong&gt;? Well, if you're migrating data between two different databases, it should be a constant concern. Let's understand which type of PoC you will be running.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.1 PoC: Same Paradigm
&lt;/h2&gt;

&lt;p&gt;Imagine a few scenarios where you want to migrate from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CassandraDB to ScyllaDB (Wide Column);&lt;/li&gt;
&lt;li&gt;MySQL to PostgreSQL (Relational);&lt;/li&gt;
&lt;li&gt;Memcache to Redis (Key-value);&lt;/li&gt;
&lt;li&gt;MongoDB to DynamoDB or Firebase (document).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these scenarios are kinda easier to migrate, because they share the same &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database Paradigm&lt;/a&gt;, which means that they follows the same architecture. &lt;/p&gt;

&lt;p&gt;So, they have mostly the same features, indexing and data types. Maybe will have slight differences but are things that you can handle by doing tests and migrating from a feature/data type to another. &lt;/p&gt;

&lt;p&gt;Even when Discord was &lt;a href="https://discord.com/blog/how-discord-stores-trillions-of-messages" rel="noopener noreferrer"&gt;migrating from CassandraDB to ScyllaDB&lt;/a&gt; had a few things to fix, but at the end of the day it's simpler than between different paradigms. To be super honest, if you have a problem during the PoC, remember that it's part of the job. ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2 PoC: Different Paradigm
&lt;/h2&gt;

&lt;p&gt;Ok, and if our problem is to go from some document based database to a wide column? Like going from a MongoDB to a ScyllaDB?&lt;/p&gt;

&lt;p&gt;Sounds like a tough task, and probably will be, and this part especially needs to be carefully designed. &lt;/p&gt;

&lt;p&gt;When Discord started &lt;a href="https://discord.com/blog/how-discord-stores-billions-of-messages" rel="noopener noreferrer"&gt;migrating from Document Based Database to the Wide Column Database in 2017&lt;/a&gt;, they had the same issue. &lt;/p&gt;

&lt;p&gt;How do you split an infinite JSON object? Can you imagine? Check the JSON example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"218378123781"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"218378123781"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"server_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"He4rt Developers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"78931278921723"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test Channel 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"312783712867"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"message_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"312783712867"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"chatter_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oh hi lol"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"312783712867"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"message_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"312783712867"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"chatter_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oh hi lol"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"chatter_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"joined_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dont_forget_to_follow_me_on_socials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"chatter_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dont_forget_to_follow_me_on_socials"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"joined_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1691972222"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how a briefly imagined a social modeling like Discord. Yeah, I know. That's far from the reality but let's stick into the problem here, ok? And in fact, if you have a good DBA and architects on your side, it will not be a problem. &lt;/p&gt;

&lt;p&gt;First, you need the whole team to know more about the paradigm and how to use it. With document-oriented, you can push whatever you want because there's no strong data consistency at all. But if you move to ScyllaDB, you'll need to model tables, focus on what query you want to run, and also understand how things like "consistency level" and "replication factor" work. &lt;/p&gt;

&lt;p&gt;At the end we should have something like:&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;TABLE&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;server_id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;
    &lt;span class="n"&gt;owner_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;server_name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;channels&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;server_id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;
    &lt;span class="n"&gt;channel_id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;
    &lt;span class="n"&gt;channel_name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;server_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;CLUSTERING&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;channel_messages&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;message_id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;
    &lt;span class="n"&gt;channel_id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chatter_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;CLUSTERING&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, CQL (Cassandra Query Language) is very similar to SQL (Structured Query Language). I invite you to have a try doing this &lt;a href="https://university.scylladb.com/courses/scylla-essentials-overview/" rel="noopener noreferrer"&gt;ScyllaDB: NoSQL 101 Essentials&lt;/a&gt; course. You will learn a bunch of cool things about the Wide Column paradigm :D&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Testing it before it's too late! (seriously)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k9lpwacz48myu44fbpn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k9lpwacz48myu44fbpn.png" alt="Meme: guy seeing that he ran an wrong query in production" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, we ran our PoC and we know which database and paradigm will be the best for our new environment. Now your team is focused to finish the data modeling ASAP, right? Once that is done, the focus will be on getting all the scripts ready to make this a successful migration. &lt;/p&gt;

&lt;p&gt;The best thing that you can do is to split your tests with 1% and 5% of your database in a staging environment. Why? If your "new system" or "new version" of the system crashes with 1% of the database, it will reflect that there's something missing in the data modeling or even at the backend. So, stop the migration scripts and write some tests for your application.&lt;/p&gt;

&lt;p&gt;Now that you're done with the 1% migration bugs, let's do the same process with 5% of your database. In this step you should test everything carefully, because it will be the last testing step you will do before choosing the migration strategy and executing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Migration Strategies
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0y6sog8q7shx1mnkr57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0y6sog8q7shx1mnkr57.png" alt="Meme: pick a button that will select between Hot and Cold Migration" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, migrating between databases isn't a "new" thing, so there are a few things planned and ready to make your life easier. Also, a few rules that together will make your database migration look like it was done by a pro. &lt;/p&gt;

&lt;p&gt;In this regard, there are two very popular strategies called "&lt;strong&gt;Hot Migration&lt;/strong&gt;" and "&lt;strong&gt;Cold Migration&lt;/strong&gt;". The names of the strategies themselves say something that we've probably been asking ourselves since the beginning of this research: Should I shut everything down or migrate while the old database is still running? Well, that depends on your needs. The process itself is called &lt;strong&gt;ETL (Extract, Transform and Load)&lt;/strong&gt; in any case btw. &lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Strategy: Cold Migration
&lt;/h3&gt;

&lt;p&gt;Let's start with the "easiest" and safer migration strategy: Cold Migration. The reason to use this type of migration is understand if you &lt;strong&gt;can stop&lt;/strong&gt;  your system for the time needed without any problem. Basically, know if there's a maintenance period and let users know that. It also may known as "Big Bang Migration", since it will be migrated entirely to a new environment that had nothing before.&lt;/p&gt;

&lt;p&gt;This approach is used on small/medium systems and is usually done at dawn, since it's probably the time of day when fewer users are requesting the system. &lt;/p&gt;

&lt;p&gt;There's a checklist of things you need to be aware of when doing a cold migration. Let's check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Planning:&lt;/strong&gt; what time of day and who will be responsible for this task?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Extraction:&lt;/strong&gt; how are we going to dump all this data? Maybe there's specific &lt;strong&gt;tooling&lt;/strong&gt; already done on the target database to be used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Transformation:&lt;/strong&gt; is the data in the right form for the new database? Do both databases have the same structure? Is there any &lt;strong&gt;tooling&lt;/strong&gt; to transform this data for us?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. Load:&lt;/strong&gt; how do we make sure that our new database gets all of our data? Any broken query? All good?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5. Validation:&lt;/strong&gt; ok, we loaded everything. Now we need to validate if the data matches with the legacy system. Good Luck!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6. Testing:&lt;/strong&gt; let's connect it in the migrated database into the system and run all the test suites. Almost there!!!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;7. Activation:&lt;/strong&gt; all good. Now it's time to shut down the legacy database. Congratz, buddy!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, this is the migration flow when you have the possibility to shut everything down and now you know how to properly do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.2 Strategy: Hot Migration
&lt;/h3&gt;

&lt;p&gt;Ok... If you want to migrate things while your system is still running... Now you're in trouble! Wait, I'm just kidding :p&lt;/p&gt;

&lt;p&gt;This is where things get a bit complicated because you have to write to two databases at the same time. So you can imagine that instead of having 1 instance of the database, you will need two, and depending on that &lt;/p&gt;

&lt;p&gt;We also have a checklist to help us with this. So let's check the flow: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Planning:&lt;/strong&gt; time of the day is not the main issue here, but a proper infrastructure to support the transition between both databases; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Replication:&lt;/strong&gt; both systems need to be feed at the same time while the migration is happening&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Tests and Validation:&lt;/strong&gt; during the replication, you should validate all the data that you're inserting along with the migrated one. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. Minimal Interruption:&lt;/strong&gt; the system should be online all the time, and if any problem occurs, it should not be perceptible by the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5. Switch:&lt;/strong&gt; When all the important data is finally migrated, is time to point the app to the new database;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6. Activation:&lt;/strong&gt; all good. Now it's time to shut down the legacy database. Congratz, buddy!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This migration strategy is indicated for companies that require high availability and have critical services that cannot be stopped. &lt;/p&gt;

&lt;h2&gt;
  
  
  7. Final Considerations
&lt;/h2&gt;

&lt;p&gt;Well, my task was to understand more about DynamoDB and create some content about the &lt;a href="https://opensource.docs.scylladb.com/stable/alternator/alternator.html" rel="noopener noreferrer"&gt;ScyllaDB Alternator&lt;/a&gt;, a migration tool that converts any DynamoDB I/O into a ScyllaDB. I'm still working in a PoC to understand more about it, but I certainly learned some really cool things along the way to know more tips, tricks, and last names of things related to migrations.&lt;/p&gt;

&lt;p&gt;Also there will be an &lt;a href="https://lp.scylladb.com/nosql-data-migration-masterclass-register?siteplacement=devto" rel="noopener noreferrer"&gt;"NoSQL Database Migration Masterclass"&lt;/a&gt; for free event tomorrow (22/08)! I'll be there on the chat learning more about the topic and invite you to be there with me!&lt;/p&gt;

&lt;p&gt;I hope that this tutorial could help you to understand briefly this topic! And please, let me know in the comments which things that I should learn about. &lt;/p&gt;

&lt;p&gt;Don't forget to like and share this article with your friends and go fill your water bottle! Let's keep in touch: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/danielhe4rt" rel="noopener noreferrer"&gt;Twitter DanielHe4rt PT-BR&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/danielhe4rtless" rel="noopener noreferrer"&gt;Twitter DanielHe4rt EN&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitch.tv/danielhe4rt" rel="noopener noreferrer"&gt;Twitch Channel&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>scylladb</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Database 101: Twitch Bot in a higher level for beginners.</title>
      <dc:creator>Daniel Reis</dc:creator>
      <pubDate>Tue, 18 Jul 2023 16:44:21 +0000</pubDate>
      <link>https://dev.to/scylladb/database-101-twitch-bot-in-a-higher-level-for-beginners-i91</link>
      <guid>https://dev.to/scylladb/database-101-twitch-bot-in-a-higher-level-for-beginners-i91</guid>
      <description>&lt;p&gt;I kinda like the idea of writing about social media + databases and it seems the readers here are too. So, let’s explore it in a new direction: looking at Twitch.tv or any type of platform with instant messaging.&lt;/p&gt;

&lt;p&gt;If you’re just getting started with databases in general or databases + social media in particular, you might want to start off by reading my initial post, &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database 101: Data Consistency for Beginners&lt;/a&gt; for Beginners. That article captures my own exploration of how many database paradigms exist as I look far beyond my previous experience with just SQL and MySQL. I’m keeping track of my studies in this &lt;strong&gt;Database 101&lt;/strong&gt; series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. Prologue&lt;/li&gt;
&lt;li&gt;2. The Project: Twitch Sentinel&lt;/li&gt;
&lt;li&gt;
3. Making Good Decisions

&lt;ul&gt;
&lt;li&gt;3.1 ACID Acronym&lt;/li&gt;
&lt;li&gt;3.2 BASE Acronym&lt;/li&gt;
&lt;li&gt;3.3 Concluding a Good Decision&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

4. Modeling our Ideas into Queries

&lt;ul&gt;
&lt;li&gt;4.1 Number of messages per user (top 5)&lt;/li&gt;
&lt;li&gt;4.2 Number of unique users per stream(er)&lt;/li&gt;
&lt;li&gt;4.3 Earliest and latest message of a given user&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;5. Twitch as our payload!&lt;/li&gt;

&lt;li&gt;6. Make it BURN!&lt;/li&gt;

&lt;li&gt;7. Final Considerations&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Prologue
&lt;/h2&gt;

&lt;p&gt;One of my tasks as a Developer Advocate is to inspire developers to create and try new things. I also need to challenge myself to create interesting things that will engage people.&lt;/p&gt;

&lt;p&gt;In the past few weeks, I was working on a talk called &lt;strong&gt;“ScyllaDB from the Developer Perspective: Building Applications.”&lt;/strong&gt; My goal was to build a simple application with a database that would consume 1k/3k operations. In this article, I’ll share what I learned. By the end of this article, a Beginner Developer should know how to build an application with a cool database to consume tons of IOPS (or maybe not – , let me know in the comments).&lt;/p&gt;

&lt;p&gt;And at the end of your reading, you, a &lt;strong&gt;Beginner Developer&lt;/strong&gt; (or maybe not, let me know on the comments) can build an application with a cool database to consume tons of IO/s. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Project: Twitch Sentinel
&lt;/h2&gt;

&lt;p&gt;Some things that I really like to do in my free time are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create useless PoCs (Proof of Concepts) that help me understand more about concepts in &lt;strong&gt;computer science&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Do daily live coding at Twitch.tv (&lt;a href="https://twitch.tv/danielhe4rt" rel="noopener noreferrer"&gt;here is my channel&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Write blog posts about my learnings and experiences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reflecting on that, I decided to start a Twitch project that involves high data ingestion; let’s call it &lt;code&gt;Twitch Sentinel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The idea is simple: collect &lt;strong&gt;all the messages&lt;/strong&gt; from as &lt;strong&gt;many channels&lt;/strong&gt; as possible on Twitch.tv, store the data, and retrieve some metrics from it.&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuvib8bcltl32xv9xnye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuvib8bcltl32xv9xnye.png" width="551" height="803"&gt;&lt;/a&gt;
    Screenshot of my Twitch chat saying "hi" to you. lol
&lt;/p&gt;

&lt;p&gt;Can you imagine how to store more than 1k messages per second? It sounds like a cool challenge. Here are a few metrics that you can generate for your study:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many messages a specific streamer receives on their chat daily.&lt;/li&gt;
&lt;li&gt;How many messages a user sends per day.&lt;/li&gt;
&lt;li&gt;The most probable hour that a streamer or user is online, based on their messages.&lt;/li&gt;
&lt;li&gt;Peak of messages by hour, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But you might be asking, "Why should I create that if Twitch gives me the Analytics Panel?" The answer is: &lt;strong&gt;USELESS PoCs that will teach you something new!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever created an analytics dashboard before? With a really fast database? Thinking of new ways to model it? If the answer is &lt;strong&gt;YES&lt;/strong&gt;, drop a like on this post! And if not, let me teach you what I've learned about this subject!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Making Good Decisions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xb5g9cy95hvkxu07kw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xb5g9cy95hvkxu07kw2.png" alt="Good Decisions queue" width="520" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you start a new project, you should know that every part of the code and the tools that you will use to deliver it will directly impact the future of your project.&lt;/p&gt;

&lt;p&gt;Also, the database that you choose for it directly impacts = the performance of the entire application.&lt;/p&gt;

&lt;p&gt;If you read the first post of my series &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database 101&lt;/a&gt;, you probably remember discussions about the &lt;strong&gt;CAP Theorem&lt;/strong&gt; and &lt;strong&gt;Database Paradigms&lt;/strong&gt;. Now we're about to check some properties related to how fast or consistent a database can be.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 ACID Acronym
&lt;/h3&gt;

&lt;p&gt;If you're interested in Data Modeling, you’ve probably already heard about ACID. If not, ACID is an acronym for &lt;code&gt;Atomicity&lt;/code&gt;, &lt;code&gt;Consistency&lt;/code&gt;, &lt;code&gt;Isolation&lt;/code&gt; and &lt;code&gt;Durability&lt;/code&gt;. Each item of this acronym makes up something called a &lt;code&gt;Transaction&lt;/code&gt; in Relational Databases.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Transaction&lt;/code&gt; is a single operation that tells your Database that if any of these concepts of ACID fails, your query will not succeed. Let's explore this more: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Atomicity:&lt;/strong&gt; Each piece of your transaction is unique and if any of these pieces fails, it will fail and rollback to the original state of that piece of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Guarantee that your data will only be accepted if it's in the right shape of that model, based on: table modeling, triggers, constraints etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; All transactions are independent and will not interfere with a running transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durability:&lt;/strong&gt; If the transaction is finished, the data will be persisted even if the system fails after that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, ACID gives you an interesting safety guard for all your data, but this requires some tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's &lt;strong&gt;EXPENSIVE&lt;/strong&gt; to maintain, since for each transaction you will LOCK (Isolation) your data. It will need many nodes&lt;/li&gt;
&lt;li&gt;If your goal is to speed things up with higher throughput, ACID will keep you from that since all these operations need to be succeed.&lt;/li&gt;
&lt;li&gt;You can't even think in &lt;strong&gt;Flexible Schemas&lt;/strong&gt;, a.k.a "Documents" since it broke the Consistency property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what? We just start to write JSON or TXT as our database? Well, it's not a bad thing but let's do it with &lt;strong&gt;Non Relational Databases&lt;/strong&gt; (a.k.a &lt;strong&gt;NoSQL&lt;/strong&gt;.).&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 BASE Acronym
&lt;/h3&gt;

&lt;p&gt;When we jump into NoSQL databases, probably the most important thing to know is: there are paradigms and each paradigm is strong for something specific.&lt;/p&gt;

&lt;p&gt;But something that is common between most NoSQL Database Paradigms is the &lt;strong&gt;BASE&lt;/strong&gt; properties. This acronym stands for: &lt;strong&gt;Basically Available&lt;/strong&gt;, &lt;strong&gt;Soft State&lt;/strong&gt; and &lt;strong&gt;Eventually Consistency&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Now things get interesting because those properties allow you to do "less validation" on your data because you guarantee the integrity from the developer side. But before talking about that,  let's understand why and all the meanings: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Basically Available:&lt;/strong&gt; You can Read/Write data from your database anytime you want, but it doesn't mean that this data is updated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft State:&lt;/strong&gt; You can shape your schema on the fly mostly and it turns into a Developer task, not a DB Administrator one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventually Consistency:&lt;/strong&gt; All the data will be synced between many datacenters until it gets the &lt;strong&gt;Consistency Level&lt;/strong&gt; needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seems, &lt;strong&gt;BASE&lt;/strong&gt; properties give us a database that can receive any type of data with respect to data modeling. Also it's always available to query it and it's a good option for high data ingestions. But there are also tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need strong data consistency, it may be not the best approach;&lt;/li&gt;
&lt;li&gt;Higher complexity since the DB Modeling is delegated to the Developer;&lt;/li&gt;
&lt;li&gt;Data Conflicts can be a pain if you need to sync the nodes around the clusters that you have.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we know more about a few properties that a Database can have generally have, so it's time to decide based on our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Concluding a Good Decision
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;ACID&lt;/strong&gt; vs &lt;strong&gt;BASE&lt;/strong&gt;? Who wins? The type of project decides! Often you can use multiple databases in a project, so it’s not a problem at all. But if you need to choose just one, choose wisely. To be clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACID&lt;/strong&gt; should be chosen when you &lt;strong&gt;NEED TO HAVE&lt;/strong&gt;  Data Consistency with Transactions and performance isn’t a key consideration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BASE&lt;/strong&gt; should be chosen when you have a higher demand for IOPS and are sure about your Data Modeling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our project, we're going to receive a huge amount of of messages from Twitch.tv. We need to store them quickly and  to handle all those messages. So, of course we're going to abandon the &lt;code&gt;ACID 'Safety Guard'&lt;/code&gt; and jump into &lt;code&gt;BASE 'Do Whatever Wisely'&lt;/code&gt; xD&lt;/p&gt;

&lt;p&gt;Thinking about that, I decided to use &lt;strong&gt;CQL&lt;/strong&gt; and &lt;strong&gt;ScyllaDB&lt;/strong&gt; since it handles our idea of receiving millions of messages per second and at the same time has the &lt;strong&gt;Consistency&lt;/strong&gt; and support for &lt;strong&gt;ACID&lt;/strong&gt;. If &lt;a href="https://discord.com/blog/how-discord-stores-trillions-of-messages" rel="noopener noreferrer"&gt;Discord uses ScyllaDB it for storing messages&lt;/a&gt;, why not use it with Twitch? :D&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Modeling our Ideas into Queries
&lt;/h2&gt;

&lt;p&gt;
    &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrs6zzjlcf8grhi62tnr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrs6zzjlcf8grhi62tnr.png" width="800" height="380"&gt;&lt;/a&gt;
    Screenshot of my Twitch chat saying "hi" to you. lol
&lt;/p&gt;

&lt;p&gt;When you're using ScyllaDB, your main focus needs to be on which query you want to run. Thinking on that, we need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store messages and read them when necessary.&lt;/li&gt;
&lt;li&gt;Store and read often from a streamer list.&lt;/li&gt;
&lt;li&gt;Count all messages sent by chat users in a specific stream;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So our data modeling should be like: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq787zl9tiie24fe77weq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq787zl9tiie24fe77weq.png" alt="aaa" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No big deal here, it's supposed to be simple. We need the fastest throughput possible and complex queries don't allow us to have such a performance.&lt;/p&gt;

&lt;p&gt;Here are a few queries that we can run with the data retrieved from this model:&lt;/p&gt;
&lt;h3&gt;
  
  
  4.1 Number of messages per user (top 5)
&lt;/h3&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="n"&gt;chatter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chatter_username&lt;/span&gt;&lt;span class="p"&gt;,&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;AS&lt;/span&gt; &lt;span class="n"&gt;msg_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;dev_sentinel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;chatter_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chatter_username&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;msg_count&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;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4.2 Number of unique users per stream(er)
&lt;/h3&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="n"&gt;streamer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;chatter_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;unique_chatters&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;dev_sentinel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;streamer_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;unique_chatters&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4.3 Earliest and latest message of a given user
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    min(sent_at) AS earliest_msg,
    max(sent_at) AS latest_msg
FROM
    dev_sentinel.messages
WHERE
    chatter_username = 'danielhe4rt'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is very similar to  the modeling I discussed in the post. There, I have a more detailed explanation of a few pieces of the code. Feel free to take a look :p&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Twitch as our payload!
&lt;/h2&gt;

&lt;p&gt;Ok, now we have the Database concept modeled and now we need the "real world" payload. I don't know if you like these tutorials that just &lt;strong&gt;mock all the data&lt;/strong&gt; just to show you a huge number that doesn't even mean anything at the end...  I simply don't  – that's and that's why I wanted to bring something real for you to explore.&lt;/p&gt;

&lt;p&gt;On Twitch's streaming platform, they have a bunch of APIs that can interact with the streamer chat. The most notorious is called &lt;strong&gt;'TMI'&lt;/strong&gt; - Twitch Messaging Interface - which is a client that connects directly to any Twitch Streamer chat that you want. Here's a list of clients for you to check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tmijs.com/" rel="noopener noreferrer"&gt;tmi.js&lt;/a&gt; - Interface for NodeJS&lt;/li&gt;
&lt;li&gt;
&lt;a href="//tmiphp.com/"&gt;tmi.php&lt;/a&gt; - Interface for PHP (Easily integrated to Laravel)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/pytmi/" rel="noopener noreferrer"&gt;pytmi&lt;/a&gt; - Interface for Python&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.rs/twitch-irc/latest/twitch_irc/" rel="noopener noreferrer"&gt;twitch-irc-rs&lt;/a&gt; Interface for Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, the idea is the same for all of these clients: you need to choose a channel and connect to it. The code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'connection'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'secure'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'reconnect'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'rejoin'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'channels'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'danielhe4rt'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]));&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MessageEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;MessageEvent&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'display-name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Twitch payload has the "tags" array, that brings us a JSON with all the data related to that specific message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"badge-info"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"subscriber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"58"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"badges"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"broadcaster"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"subscriber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3036"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"partner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"client-nonce"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3e00905ed814fb4d846e8b9ba6a9c1da"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#8A2BE2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"display-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"emotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first-msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b40513ae-efed-472b-9863-db34cf0baa98"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"returning-chatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"room-id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"227168488"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"subscriber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tmi-sent-ts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1686770892358"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"turbo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user-id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"227168488"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"emotes-raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"badge-info-raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"subscriber/58"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"badges-raw"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"broadcaster/1,subscriber/3036,partner/1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"danielhe4rt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chat"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this payload, we gonna need only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;room-id&lt;/strong&gt;: Identifier related to the specific broadcaster channel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;user-id&lt;/strong&gt;: Identifier related to the user who sent the message&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmi-sent-at&lt;/strong&gt;: message timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the Message interface, you will also receive a string with the message&lt;/p&gt;

&lt;p&gt;This is a simple project, but seriously, try to abstract more ideas from that and let me know! I'll gladly help you to create something bigger!&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Make it BURN!
&lt;/h2&gt;

&lt;p&gt;As I told you at the beginning of this article, my goal with this project was to build a highly scalable application with a really cool database that handles our needs by receiving a huge chunk of payload per second.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof5jtvh63dxxu4z2gilq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof5jtvh63dxxu4z2gilq.png" alt="Image description" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we connected on the ~20k most accessed chats on Twitch.tv and got an average of 1,700 ~ 2,000 messages per second. This gives us an average of &lt;strong&gt;6 MILLION&lt;/strong&gt; messages per hour. Have you ever coded something that had such a high data ingestion?&lt;/p&gt;

&lt;p&gt;While the application is receiving all this data and posting it to ScyllaDB, here are some statistics of a &lt;strong&gt;T3-Micro Cluster&lt;/strong&gt;, the cheapest instance at AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrvh8y8q55jxnu4q88rx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhrvh8y8q55jxnu4q88rx.png" alt="Image description" width="605" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It handles 1k requests/s like it's nothing with latency sub millisecond P99 latency. Also, the load of this lightweight machine for 1k/s is just 8%, so you can do something monstrously faster if you want.&lt;/p&gt;

&lt;p&gt;Most of the time, it depends on how many streamers will be connected to your bot and how many messages viewers send per second.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Final Considerations
&lt;/h2&gt;

&lt;p&gt;This project taught me a lot about how important it is to choose the right tool for the right job. In this specific case, the database needs to be something that you will use while thinking at a higher scale.&lt;/p&gt;

&lt;p&gt;Just remember that it is totally fine to have more than one database inside a project. Each one resolves a generic or a specific problem inside the development environment. Always do proper research and a PoC with as many tools as possible if you have time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4qdqt86wg3jjcz6b3eo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4qdqt86wg3jjcz6b3eo.png" alt="Killercoda tutorial" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to build by yourself, check this tutorial on &lt;a href="https://killercoda.com/scylladb/scenario/twitch_sentinel_lab" rel="noopener noreferrer"&gt;Killercoda&lt;/a&gt; and don't forget to follow me at socials!&lt;/p&gt;

&lt;p&gt;Stay hydrated and see you soon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/danielhe4rt" rel="noopener noreferrer"&gt;Twitter DanielHe4rt PT-BR&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/danielhe4rtless" rel="noopener noreferrer"&gt;Twitter Danielhe4rt EN&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitch.tv/danielhe4rt" rel="noopener noreferrer"&gt;Twitch Channel (Daily LiveCoding Sessions)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://instagram.com/danielhe4rt" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;br&gt;
&lt;a href="https://youtube.com/danielhe4rt" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>scylladb</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Database 101: How social media “likes” are stored in a database</title>
      <dc:creator>Daniel Reis</dc:creator>
      <pubDate>Mon, 08 May 2023 17:17:03 +0000</pubDate>
      <link>https://dev.to/scylladb/database-101-how-social-media-likes-are-stored-in-a-database-3oii</link>
      <guid>https://dev.to/scylladb/database-101-how-social-media-likes-are-stored-in-a-database-3oii</guid>
      <description>&lt;p&gt;Did you ever think about how Instagram, Twitter, Facebook or any social media platforms track who liked your posts? Let's figure it out in this post!&lt;/p&gt;

&lt;p&gt;If you’re just starting working with databases, you might want to start off by reading my initial post, &lt;a href="https://dev.to/danielhe4rt/database-101-why-so-interesting-1344"&gt;Database 101: Data Consistency for Beginners&lt;/a&gt;. That captures my own exploration of how many database paradigms exist as I look far beyond my previous experience with just SQL and MySQL. I’m keeping track on my studies in this &lt;strong&gt;Database 101&lt;/strong&gt; series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/#s1"&gt;1. Prologue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/#s2"&gt;2. Let's Research&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/#s3"&gt;3. Researching Data Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/#s4"&gt;4. Properly Modeling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/#s5"&gt;5. Final Considerations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Prologue &lt;a id="s1"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Recently, I was invited to speak at an event called "CityJS." But here's the thing: I'm the PHP guy. I don't do JS at all, but I accepted the challenge. To pull it off, I needed to find a good example to show how a highly scalable and low latency database works.&lt;/p&gt;

&lt;p&gt;So, I asked one of my coworkers for examples. He told me to look for high numbers inside any platform, like counters or something like that. At that point I realized  that any type of metrics can fit this example. Likes, views, comments, follows, etc. could be queried as counters. In this article, you will find my studies of how do proper data modeling for these using &lt;em&gt;ScyllaDB.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Let's Research &lt;a id="s2"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyd0snfv4b8pd1kk7s0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyd0snfv4b8pd1kk7s0h.png" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First things first, right? After  deciding what to cover in my talk, I needed to understand how to build this data model.&lt;/p&gt;

&lt;p&gt;We'll need a &lt;code&gt;posts&lt;/code&gt; table and also a &lt;code&gt;post_likes&lt;/code&gt; table that relates who liked each post. So far, it seems enough to do our likes counter.&lt;/p&gt;

&lt;p&gt;My first bet for a query to count all likes was something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ok and if I just do a query with &lt;code&gt;SELECT count(*) FROM social.post_likes&lt;/code&gt; it can work, right?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, it worked but it was not as performant as expected when I did a test with a couple thousands of likes in a post. As the number of likes grows, the query becomes slower and slower...&lt;/p&gt;

&lt;p&gt;"But ScyllaDB can handle thousands of rows easily… why isn’t it performant?" That's probably what you're thinking right now (or maybe not).&lt;/p&gt;

&lt;p&gt;ScyllaDB – even as a cool database with cool features –  will not solve the problem of  bad data modeling. We need to think about how to make things faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Researching Data Types &lt;a id="s3"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Ok, let's think straight: the data needs to be stored and we need the relation between who liked our post, but we can't use it for count. So what if I create a new row as &lt;code&gt;integer&lt;/code&gt; in the &lt;code&gt;posts&lt;/code&gt; table and increment/decrement it every time?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikibyvt4g2jn575zctxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fikibyvt4g2jn575zctxm.png" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that seems like a good idea, but there's a problem: we need to keep track of every change on the  posts table and if we start to INSERT or UPDATE data there, we'll probably create a bunch of nonsense records in our database.&lt;/p&gt;

&lt;p&gt;Using ScyllaDB, every time that you need to update something, you actually create new data.&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="n"&gt;scylla&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cqlsh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;edd5f1d&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a3e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;af1a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;adbb41e2129&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Such a cool event P99 Conf!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'https://i.imgur.com/Xp8gi7t.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2023-04-23 15:02:49'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;scylla&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cqlsh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;edd5f1d&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a3e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;af1a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;adbb41e2129&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Such a cool event P99 Conf!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'https://i.imgur.com/Xp8gi7t.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2023-04-23 15:02:50'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;scylla&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;cqlsh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;edd5f1d&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a3e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;af1a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;adbb41e2129&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Such a cool event P99 Conf!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'https://i.imgur.com/Xp8gi7t.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2023-04-23 15:02:51'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scylla@cqlsh:socials&amp;gt; SELECT * from posts;

 id                                 | user_id                           | created_at                    | description               | image_url                     | likes
--------------------------------------+--------------------------------------+---------------------------------+-----------------------------+---------------------------------+-------
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:48.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    1
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:50.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    2
 4d18bb8c-9c57-44fe-827a-4a2d65f331e5 | 3edd5f1d-67e9-4a3e-af1a-9adbb41e2129 | 2023-04-23 15:02:51.000000+0000 | Such a cool event P99 Conf! | https://i.imgur.com/Xp8gi7t.jpg |    3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have to track everything that changes in your data. So, for each increase, there will be one more row unless you don't change your clustering keys or don't care about timestamps (a really stupid idea).&lt;/p&gt;

&lt;p&gt;After that, I went into &lt;a href="https://docs.scylladb.com/stable/using-scylla/counters.html" rel="noopener noreferrer"&gt;ScyllaDB docs&lt;/a&gt; and found out that there's a type called &lt;code&gt;counter&lt;/code&gt; that fit our needs and is also &lt;a href="https://en.wikipedia.org/wiki/Atomicity_(database_systems)" rel="noopener noreferrer"&gt;ATOMIC&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qvneewo4y6i91whiz07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qvneewo4y6i91whiz07.png" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, it fit our needs but not our data modeling. To use this type, we have to follow a few rules  but let's focus on the ones that are causing trouble for us right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The only other columns in a table with a counter column can be columns of the primary key (which cannot be updated).&lt;/li&gt;
&lt;li&gt;No other kinds of columns can be included.&lt;/li&gt;
&lt;li&gt;You need to use UPDATE queries to handle tables that own a counter data type.&lt;/li&gt;
&lt;li&gt;You only can INCREMENT or DECREMENT values, setting a specific value is not permitted.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This limitation safeguards correct handling of counter and non-counter updates by not allowing them in the same operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, we can use this counter but not on the posts table... Ok then, it seems that we're finding a way to get it done.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Properly Modeling &lt;a id="s4"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;With the information that &lt;code&gt;counter&lt;/code&gt; type should not be "mixed" with other data types in a table, the only option that is left to us is create a &lt;strong&gt;NEW TABLE&lt;/strong&gt; and store this type of data.&lt;/p&gt;

&lt;p&gt;So, I made a new table called &lt;code&gt;post_analytics&lt;/code&gt; that will hold only &lt;code&gt;counter&lt;/code&gt; types. For the moment, let's work with only likes since we have a &lt;strong&gt;Many to Many&lt;/strong&gt; relation (post_likes) created already.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F427q2nf4jy5v17qazhd3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F427q2nf4jy5v17qazhd3.png" width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These next queries are what you probably will run for this example that we created:&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="o"&gt;##&lt;/span&gt; &lt;span class="n"&gt;Social&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_analytics&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_likes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;liked_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;edd5f1d&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a3e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;af1a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;adbb41e2129&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2023-04-23 15:02:50'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Social&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;dislike&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;

&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_likes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;edd5f1d&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="n"&gt;e9&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a3e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;af1a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;adbb41e2129&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;socials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_analytics&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;likes&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;d18bb8c&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="n"&gt;c57&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;827&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;a2d65f331e5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you might have new unanswered questions in your mind like: "So every time that I need a new counter related to some data, I'll need a new table?" Well, it depends on your use case. In the social media case, if you want to store who saw the post, you will probably need a &lt;code&gt;post_viewers&lt;/code&gt; table with session_id and a bunch of other stuff.&lt;/p&gt;

&lt;p&gt;Having these simple queries that can be done without &lt;strong&gt;joins&lt;/strong&gt; can be way faster than having  &lt;code&gt;count(*)&lt;/code&gt; queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Final Considerations &lt;a id="s5"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpq16uwab5b492yru6a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpq16uwab5b492yru6a5.png" alt="Me talking at CityJS stage" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;center&gt;Me at CityJS stage saying a bunch of nonse data modeling using TS&lt;/center&gt;

&lt;p&gt;I learned a lot not only by studying new ways of data modeling but also having to learn TypeScript to create the CityJS presentation and build this use case.&lt;/p&gt;

&lt;p&gt;As everything is brand new for me, I'll do my best to keep sharing my studies. Please feel free to correct me in the comments! Discussing is the best way to learn new things.&lt;/p&gt;

&lt;p&gt;Don't forget to like this post, follow me on the socials and fill your water bottle xD&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/danielhe4rt" rel="noopener noreferrer"&gt;Twitter DanielHe4rt PT-BR&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/danielhe4rtless" rel="noopener noreferrer"&gt;Twitter DanielHe4rt EN&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitch.tv/danielhe4rt" rel="noopener noreferrer"&gt;Twitch Channel&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>scylladb</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
