This tutorial is a step-by-step guide to build an Media Player project connected to ScyllaDB Cloud.
After completing the project, you will be able to create back-end services that share data through a ScyllaDB Cloud cluster.
# build-with-csharp.md
# Quick Start: Csharp
In this tutorial we’re gonna build a simple Media Player to store our songs and build playlists
## 1. Setup the Environment
### 1.1 Downloading .NET dependecies:
If you don’t have csharp installed already on your machine, you can install from the following source:
1. [.NET main website](https://dotnet.microsoft.com/en-us/download/)
### 1.2 Starting the project
Now with the .NET installed, let’s create a new project with the following command:
```sh
dotnet new console -n media_player && cd media_player
```
### 1.3 Setting the project dependencies
First we’ll install the required package to connect to scyllaDB with the following command:
```sh
dotnet add package ScyllaDBCSharpDriver
```
This package can be found at [github](https://github.com/scylladb/csharp-driver/)
> Disclaimer: This package require system wide dependencies with the cassandra client, so it’s required to install on your system (or run the whole application under a docker image). You can find the installation guide at: https://cassandra.apache.org/doc/latest/cassandra/getting_started/installing.html
## 2. Connecting to the Cluster
Make sure to get the right credentials on your [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) in the tab `Connect`.
```csharp
using Cassandra;
namespace Program;
public class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
}
}
```
> If the connection got refused, check if your IP Address is added into allowed IPs.
## 3. Handling Queries
Using the `cassandra` package you can instantiate a session and then run fully queries.
```csharp
using Cassandra;
namespace Program;
public class Program
{
static void Main(string[] args)
{
var session = cluster.Connect();
var rs = session.Execute("SELECT address, port, connection_stage FROM system.clients LIMIT 5");
foreach (var row in rs)
{
Console.Write($"IP -> {row["address"]}, Port -> {row["port"]}, CS -> {row["connection_stage"]}");
}
}
}
```
The output should look something like:
```default
IP -> 172.17.0.1, Port -> 52830, CS -> READY
```
### 3.1 Creating a Keyspace
The `keyspace` inside the ScyllaDB ecossystem can be interpreted as your `database` or `collection`.
On your connection boot, you don’t need to provide it but you will use it later and also is able to create when you need.
```csharp
using Cassandra;
namespace Program;
public class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
var session = cluster.Connect();
var ps = session.Prepare("select keyspace_name from system_schema.keyspaces WHERE keyspace_name=?");
var statement = ps.Bind(keyspace);
var result = session.Execute(statement);
var rows = result
.GetRows()
.ToList();
if(rows.Count == 0)
{
StringBuilder sb = new();
sb.Append($"CREATE KEYSPACE {keyspace};");
session.Execute(sb.ToString());
Console.WriteLine("Keyspace created!");
}
else
{
Console.WriteLine("Keyspace already created!");
}
// Reconnecting to the cluster with the correct keyspace
session = cluster.Connect("media_player")
}
}
```
### 3.2 Creating a table
A table is used to store part or all the data of your app (depends on how you will build it).
Remember to add your `keyspace` into your connection and let’s create a table to store our liked songs.
```csharp
using Cassandra;
namespace Program;
public class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
string table = "playlist";
var session = cluster.Connect(keyspace);
var ps = session.Prepare("select keyspace_name, table_name from system_schema.tables where keyspace_name = ? AND table_name = ?");
var statement = ps.Bind(keyspace, table);
var result = session.Execute(statement);
var rows = result
.GetRows()
.ToList();
if(rows.Count == 0)
{
StringBuilder sb = new();
sb.Append($"CREATE TABLE {keyspace}.{table} (id uuid, title text, album text, artist text, created_at timestamp, PRIMARY KEY(id, created_at));");
session.Execute(sb.ToString());
Console.WriteLine("Table created!");
}
else
{
Console.WriteLine("Table already created!");
}
}
}
```
### 3.3 Inserting data
Now that we have the keyspace and a table inside of it, we need to bring some good songs and populate it.
```csharp
using Cassandra;
namespace Program;
public class Program
{
public record Song(string Title, string Album, string Artist, DateTime CreatedAt);
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
string table = "playlist";
var session = cluster.Connect(keyspace);
List songs = new()
{
new Song("Bohemian Rhapsody", "A night at the Opera", "Queen", DateTime.Now),
new Song("Closer to the Edge", "This Is War", "Thirty Seconds to Mars", DateTime.Now),
new Song("I Write Sins Not Tragedies", "A Fever You Can't Sweat Out", "Panic! at the Disco", DateTime.Now),
};
var ps = session.Prepare($"INSERT INTO {table} (id, title, album, artist, created_at) VALUES (?, ?, ?, ?, ?)");
foreach(Song song in songs)
{
var uuid = Guid.NewGuid();
var statement = ps.Bind(uuid,song.Title, song.Album, song.Artist, song.CreatedAt);
session.Execute(statement);
}
}
}
```
### 3.4 Reading data
Since probably we added more than 3 songs into our database, let’s list it into our terminal.
```csharp
using Cassandra;
namespace Program;
public class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
string table = "playlist";
var session = cluster.Connect(keyspace);
var result = session.Execute($"SELECT id, title, album, artist FROM {table}");
var rows = result
.GetRows()
.ToList();
foreach(var row in rows)
{
Console.WriteLine($"ID: {row["id"]} | Song: {row["title"]} | Album: {row["album"]} | Artist: {row["artist"]}" );
}
}
}
```
The result will look like:
```default
ID: 0d617157-57aa-48aa-98dc-f850b74e6aba | Song: Bohemian Rhapsody | Album: A night at the Opera | Artist: Queen
ID: 1b9a90fa-ec7a-4469-babb-f375fb8635cf | Song: Closer to the Edge | Album: This Is War | Artist: Thirty Seconds to Mars
ID: e59da413-d185-4065-9b7c-ae36f5203e90 | Song: I Write Sins Not Tragedies | Album: A Fever You Can't Sweat Out | Artist: Panic! at the Disco
```
### 3.5 Updating data
Ok, almost there! Now we’re going to learn about update but here’s a disclaimer:
> INSERT and UPDATES are not equals!
There’s a myth in ScyllaDB/Cassandra community that it’s the same for the fact that you just need the `Partition Key` and `Clustering Key` (if you have one) and query it.
If you want to read more about it, [click here.](https://docs.scylladb.com/stable/using-scylla/cdc/cdc-basic-operations.html)
As we can see, the `UPDATE QUERY` takes two fields on `WHERE` (PK and CK). Check the snippet below:
```csharp
using Cassandra;
using System.Globalization;
namespace Program;
public class Program
{
public record Song(string Title, string Album, string Artist, DateTime CreatedAt);
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
string table = "playlist";
var session = cluster.Connect(keyspace);
Song song = new(
"Bohemian Rhapsody Updated",
"A night at the Opera Updated",
"Queen Updated",
DateTime.ParseExact("2023-09-16 18:22:56.397000+0000", "yyyy-MM-dd HH:mm:ss.ffffffzzz", CultureInfo.InvariantCulture));
var ps = session.Prepare($"UPDATE {keyspace}.{table} SET title = ?, album = ?, artist = ? where id = ? and created_at = ?");
var statement = ps.Bind(song.Title, song.Album, song.Artist, new Guid("0d617157-57aa-48aa-98dc-f850b74e6aba"), song.CreatedAt);
session.Execute(statement);
Console.WriteLine("Song Updated");
}
}
```
After updated, let’s query for the ID and see the results:
```default
scylla@cqlsh:media_player> select * from media_player.playlist where id = 0d617157-57aa-48aa-98dc-f850b74e6aba;
id | created_at | album | artist | title
--------------------------------------+---------------------------------+-------------------+-----------------+---------------------------------
0d617157-57aa-48aa-98dc-f850b74e6aba | 2023-09-16 18:22:56.397000+0000 | 2023-09-16 18:22:56.397000+0000 | Queen Updated | Bohemian Rhapsody Updated
(1 rows)
```
It only “updated” the field `title`, `album` and `artist`(that is our Clustering Key) and since we didn’t inputted the rest of the data, it will not be replicated as expected.
### 3.5 Deleting data
Let’s understand what we can DELETE with this statement. There’s the normal `DELETE` statement that focus on `ROWS` and other one that delete data only from `COLUMNS` and the syntax is very similar.
```sql
-- Deletes a single row
DELETE FROM songs WHERE id = 0d617157-57aa-48aa-98dc-f850b74e6aba;
-- Deletes a whole column
DELETE artist FROM songs WHERE id = 0d617157-57aa-48aa-98dc-f850b74e6aba;
```
If you want to erase a specific column, you also should pass as parameter the `Clustering Key` and be very specific in which register you want to delete something.
On the other hand, the “normal delete” just need the `Partition Key` to handle it. Just remember: if you use the statement “DELETE FROM keyspace.table_name” it will delete ALL the rows that you stored with that ID.
```csharp
using Cassandra;
using System.Globalization;
namespace Program;
public class Program
{
public record Song(string Title, string Album, string Artist, DateTime CreatedAt);
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("node-0.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-1.aws-sa-east-1.xxx.clusters.scylla.cloud")
.AddContactPoints("node-2.aws-sa-east-1.xxx.clusters.scylla.cloud")
.WithCredentials("scylla", "a-very-secure-password")
.Build();
string keyspace = "media_player";
string table = "playlist";
var session = cluster.Connect(keyspace);
Song song = new(
"Bohemian Rhapsody Updated",
"A night at the Opera Updated",
"Queen Updated",
DateTime.ParseExact("2023-09-16 18:22:56.397000+0000", "yyyy-MM-dd HH:mm:ss.ffffffzzz", CultureInfo.InvariantCulture));
var ps = session.Prepare($"DELETE FROM {keyspace}.{table} where id = ? and created_at = ?");
var statement = ps.Bind(new Guid("0d617157-57aa-48aa-98dc-f850b74e6aba"), song.CreatedAt);
session.Execute(statement);
Console.WriteLine("Song deleted!");
}
}
```
## Conclusion
Yay! You now have the knowledge to use the basics of ScyllaDB with Csharp.
If you thinks that something can be improved, please open an issue and let’s make it happen!
Did you like the content? Dont forget to star the repo and follow us on socials.
# build-with-elixir.md
# Quick Start: Elixir
## 1. Setup the Environment
### 1.1 Downloading Elixir and dependencies:
If you don’t have Elixir and Erlang installed already on your machine, you can install from two possible sources:
1. [Elixir main website](https://elixir-lang.org/install.html)
2. [asdf](https://asdf-vm.com/guide/getting-started.html)
> NOTE: After installing asdf correctly make sure to install [Erlang](https://github.com/asdf-vm/asdf-erlang) first and then [Elixir](https://github.com/asdf-vm/asdf-elixir).
### 1.2 Starting the project
Now with Elixir properly installed you can create a project using [mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html). To create our project just run:
```sh
mix new media_player
```
### 1.3 Setting the project dependencies
Let’s do a quick change into our `mix.exs` and add our project dependencies.
```exs
defp deps do
[
{:decimal, "~> 1.0"},
{:xandra, "~> 0.14"}
]
end
```
- [Decimal](https://hexdocs.pm/decimal/readme.html): Arbitrary precision decimal arithmetic
- [Xandra](https://github.com/lexhide/xandra): Fast, simple, and robust Cassandra/ScyllaDB driver for Elixir
To carry out modifications, use the module already created in `lib/media_player.ex`, as this is where we are going to carry out some modifications.
> NOTE: In this tutorial we are going to prepare an interactive project so that you can perform tests using Elixir’s interactive shell. On every code update, don’t forget to restart Elixir’s interactive shell to perform the recompilation.
## 2. Connecting to the Cluster
Make sure to get the right credentials on your [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) in the tab `Connect`.
```ex
def start_link do
options = [username: "scylla", password: "a-very-secure-password"]
{:ok, cluster} =
Xandra.Cluster.start_link(
sync_connect: :infinity,
authentication: {Xandra.Authenticator.Password, options},
nodes: [
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud"
],
pool_size: 10
)
cluster
end
```
When starting a cluster notice that there is an option with the name `sync_connect` being informed. This option is used to inform that we are dealing with an asynchronous connection, saying to wait the necessary time (`:infinity`) to make the complete connection of the cluster.
To test our connection let’s initialize Elixir’s interactive shell:
```sh
iex -S mix
```
Then you will see a screen waiting for some input, with in the bottom left corner a message that looks like `iex(1)>` (this means we are ready to test our first module). To test it now, let’s run:
```ex
MediaPlayer.start_link
```
A process will be started, so the return should be nothing other than something like `#PID<0.230.0>`. Don’t worry, in the next topic we’ll run our first query and see a real result. This function will be used every time we need to start a connection link with our cluster.
> If the connection got refused, check if your IP Address is added into allowed IPs.
## 3. Handling Queries
With `Xandra` you can run queries and save their returns in maps, making it possible to parse this information and manipulate it as you decide. First of all, let’s create a function that will simply execute queries, receiving information from the cluster and the query to be executed as parameters. If the return is `:ok`, it means that the query executed successfully, so we return it. If the return is `:error`, it means that we had an error, so let’s inspect it. An important detail is for the address, which instead of bringing a simple text brings a tuple with four integers.
```ex
def run_query(cluster, query) do
case Xandra.Cluster.execute(cluster, query) do
{:ok, result} ->
result
{:error, error} ->
IO.inspect(error)
end
end
def handling_queries do
statement = "SELECT address, port, connection_stage FROM system.clients LIMIT 5;"
run_query(start_link(), statement)
|> Enum.each(fn %{
"address" => address,
"connection_stage" => connection_stage,
"port" => port
} ->
# `address` is a tuple of 4 integers
address_formated =
address
|> Tuple.to_list()
|> Enum.map(&Integer.to_string/1)
|> Enum.join(".")
IO.puts("IP -> #{address_formated}, Port -> #{port}, CS -> #{connection_stage}")
end)
end
```
The output should look something like:
```default
IP -> 123.123.123.69, Port -> 61667, CS -> READY
IP -> 123.123.123.69, Port -> 62377, CS -> AUTHENTICATING
IP -> 123.123.123.69, Port -> 63221, CS -> AUTHENTICATING
IP -> 123.123.123.69, Port -> 65225, CS -> READY
```
### 3.1 Creating a Keyspace
The `keyspace` inside the ScyllaDB ecossystem can be interpreted as your `database` or `collection`.
On your connection boot, you don’t need to provide it but you will use it later and also is able to create when you need.
```ex
def keyspace_exists?(keyspace_name) do
cluster = start_link()
# In this case I won't use the `run_query` function because I want to
# show the possibility of using maps to bind its parameters.
%Xandra.Page{} =
page =
Xandra.Cluster.run(cluster, fn conn ->
prepared =
Xandra.prepare!(
conn,
"SELECT * FROM system_schema.keyspaces WHERE keyspace_name = :keyspace_name;"
)
Xandra.execute!(conn, prepared, %{"keyspace_name" => keyspace_name})
end)
Enum.to_list(page) != []
end
def create_keyspace(keyspace_name) do
case keyspace_exists?(keyspace_name) do
true ->
IO.puts("Keyspace already exists")
false ->
cluster = start_link()
query = "CREATE KEYSPACE IF NOT EXISTS #{keyspace_name};"
run_query(cluster, query)
IO.puts("Keyspace created")
end
end
```
To test run your interactive shell again with `iex -S mix` and then run:
```ex
iex(1)> MediaPlayer.create_keyspace("media_player")
```
Done! Now your keyspace is officially created!
### 3.2 Creating a table
A table is used to store part or all the data of your app (depends on how you will build it). Remember to add your `keyspace` into your connection and let’s create a table to store our liked songs.
```ex
def table_exists?(keyspace_name, table_name) do
cluster = start_link()
%Xandra.Page{} =
page =
Xandra.Cluster.run(cluster, fn conn ->
prepared =
Xandra.prepare!(
conn,
"SELECT keyspace_name, table_name FROM system_schema.tables WHERE keyspace_name = :keyspace_name AND table_name = :table_name;"
)
Xandra.execute!(conn, prepared, %{
"keyspace_name" => keyspace_name,
"table_name" => table_name
})
end)
Enum.to_list(page) != []
end
def create_table(keyspace_name, table_name) do
case table_exists?(keyspace_name, table_name) do
true ->
IO.puts("Table already exists")
false ->
cluster = start_link()
query = "CREATE TABLE IF NOT EXISTS #{keyspace_name}.#{table_name} (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
PRIMARY KEY (id, created_at)
);"
run_query(cluster, query)
IO.puts("Table created")
end
end
```
We added a check if the table exists, passing the keyspace name and the table name as parameters, working in the same way as the keyspace check. To test your table creation, open the interactive shell again and run:
```ex
iex(1)> MediaPlayer.create_table("media_player", "playlist")
```
Done! Now your playlist table is officially created!
### 3.3 Inserting data
Now that we have the keyspace and a table inside of it, we need to bring some good songs and populate it.
First of all, let’s add a dependency to our `mix.exs` to work with UUID, then just run `mix deps.get` to update the dependencies!
```exs
{:elixir_uuid, "~> 1.2"}
```
To execute our query, let’s create another `run_query` function that will receive three parameters (the cluster, the query and the other parameters) to prepare our execution. In Elixir, we can have functions with the same name but different number of parameters, in which it will be understood that they are different functions.
```ex
def run_query(cluster, query, params) do
prepared = Xandra.Cluster.prepare!(cluster, query)
case Xandra.Cluster.execute(cluster, prepared, params) do
{:ok, result} ->
result
{:error, error} ->
IO.inspect(error)
end
end
def insert_songs(keyspace, table) do
cluster = start_link()
song_list = [
%{
id: UUID.uuid4(),
title: "Getaway Car",
album: "Reputation",
artist: "Taylor Swift",
created_at: DateTime.utc_now()
},
%{
id: UUID.uuid4(),
title: "Still Into You",
album: "Paramore",
artist: "Paramore",
created_at: DateTime.utc_now()
},
%{
id: UUID.uuid4(),
title: "Stolen Dance",
album: "Sadnecessary",
artist: "Milky Chance",
created_at: DateTime.utc_now()
}
]
Enum.each(song_list, fn %{
id: id,
title: title,
album: album,
artist: artist,
created_at: created_at
} ->
query =
"INSERT INTO #{keyspace}.#{table} (id, title, album, artist, created_at) VALUES (?, ?, ?, ?, ?);"
run_query(cluster, query, [id, title, album, artist, created_at])
end)
end
```
The need to create a function was to properly prepare our query with our arguments. To test just run the interactive shell again with `iex -S mix` and run:
```ex
iex(1)> MediaPlayer.insert_songs("media_player", "playlist")
```
### 3.4 Reading data
Since probably we added more than 3 songs into our database, let’s list it into our terminal.
```ex
def read_data(keyspace, table) do
cluster = start_link()
query = "SELECT id, title, album, artist, created_at FROM #{keyspace}.#{table};"
run_query(cluster, query)
|> Enum.each(fn %{
"id" => id,
"title" => title,
"album" => album,
"artist" => artist,
"created_at" => created_at
} ->
IO.puts(
"ID: #{id} | Title: #{title} | Album: #{album} | Artist: #{artist} | Created At: #{created_at}"
)
end)
end
```
To test just run the interactive shell again with `iex -S mix` and run:
```ex
iex(1)> MediaPlayer.read_data("media_player", "playlist")
```
The result will look like:
```default
ID: 09679e47-0898-40fd-b114-52b27de5a21c | Title: Stolen Dance | Album: Sadnecessary | Artist: Milky Chance | Created At: 2023-09-07 22:26:56.798Z
ID: 56fac772-dc54-4518-86df-2a628a2a45f6 | Title: Still Into You | Album: Paramore | Artist: Paramore | Created At: 2023-09-07 22:26:56.798Z
ID: 11bbeed9-c9a8-45cc-9842-c60483b4cb67 | Title: Getaway Car | Album: Reputation | Artist: Taylor Swift | Created At: 2023-09-07 22:26:56.798Z
```
### 3.5 Updating data
Ok, almost there! Now we’re going to learn about update but here’s a disclaimer:
> INSERT and UPDATES are not equals!
There’s a myth in ScyllaDB/Cassandra community that it’s the same for the fact that you just need the `Partition Key` and `Clustering Key` (if you have one) and query it.
If you want to read more about it, [click here.](https://docs.scylladb.com/stable/using-scylla/cdc/cdc-basic-operations.html)
As we can see, the `UPDATE QUERY` takes two fields on `WHERE` (PK and CK). Check the snippet below:
```ex
def update_data(keyspace, table) do
cluster = start_link()
query =
"UPDATE #{keyspace}.#{table} SET title = ?, album = ?, artist = ? WHERE id = ? AND created_at = ?;"
{:ok, date_formated, _} = DateTime.from_iso8601("2023-09-07 22:26:56.798Z")
run_query(cluster, query, [
"Getaway Car UPDATED",
"Reputation",
"Taylor Swift",
"11bbeed9-c9a8-45cc-9842-c60483b4cb67",
date_formated
])
end
```
Note that we had to convert the saved date format to iso8601. To test just run the interactive shell again with `iex -S mix` and run:
```ex
iex(1)> MediaPlayer.update_data("media_player", "playlist")
```
So to check if it’s been updated:
```ex
iex(2)> MediaPlayer.read_data("media_player", "playlist")
```
The result will look like:
```default
ID: 09679e47-0898-40fd-b114-52b27de5a21c | Title: Stolen Dance | Album: Sadnecessary | Artist: Milky Chance | Created At: 2023-09-07 22:26:56.798Z
ID: 56fac772-dc54-4518-86df-2a628a2a45f6 | Title: Still Into You | Album: Paramore | Artist: Paramore | Created At: 2023-09-07 22:26:56.798Z
ID: 11bbeed9-c9a8-45cc-9842-c60483b4cb67 | Title: Getaway Car UPDATED | Album: Reputation | Artist: Taylor Swift | Created At: 2023-09-07 22:26:56.798Z
```
### 3.5 Deleting data
Let’s understand what we can DELETE with this statement. There’s the normal `DELETE` statement that focus on `ROWS` and other one that delete data only from `COLUMNS` and the syntax is very similar.
```sql
-- Deletes a single row
DELETE FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
-- Deletes a whole column
DELETE artist FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
```
If you want to erase a specific column, you also should pass as parameter the `Clustering Key` and be very specific in which register you want to delete something. On the other hand, the “normal delete” just need the `Partition Key` to handle it. Just remember: if you use the statement “DELETE FROM keyspace.table_name” it will delete ALL the rows that you stored with that ID.
```ex
def delete_data(keyspace, table) do
cluster = start_link()
query = "DELETE FROM #{keyspace}.#{table} WHERE id = ? AND created_at = ?;"
{:ok, date_formated, _} = DateTime.from_iso8601("2023-09-07 22:26:56.798Z")
run_query(cluster, query, [
"11bbeed9-c9a8-45cc-9842-c60483b4cb67",
date_formated
])
end
```
To test just run the interactive shell again with `iex -S mix` and run:
```ex
iex(1)> MediaPlayer.delete_data("media_player", "playlist")
```
So to check if it’s been updated:
```ex
iex(2)> MediaPlayer.read_data("media_player", "playlist")
```
The result will look like:
```default
ID: 09679e47-0898-40fd-b114-52b27de5a21c | Title: Stolen Dance | Album: Sadnecessary | Artist: Milky Chance | Created At: 2023-09-07 22:26:56.798Z
ID: 56fac772-dc54-4518-86df-2a628a2a45f6 | Title: Still Into You | Album: Paramore | Artist: Paramore | Created At: 2023-09-07 22:26:56.798Z
```
## Conclusion
Yay! You now have the knowledge to use the basics of ScyllaDB with Elixir.
If you thinks that something can be improved, please open an issue and let’s make it happen!
Did you like the content? Dont forget to star the repo and follow us on socials.
# build-with-golang.md
# Quick Start: Golang
In this tutorial we’re going to build a simple Media Player to store our songs and build playlists.
## 1. Setup the Environment
### 1.1 Downloading Golang
If you don’t have Golang installed already on your machine, you can install it from the following sources:
1. [Golang](https://go.dev/)
### 1.2 Cloning the project
Clone the repository and navigate to the Go example:
```sh
git clone https://github.com/scylladb/scylla-cloud-getting-started.git
cd scylla-cloud-getting-started/go
```
### 1.3 Configuring credentials
Copy `.env.example` to `.env` and fill in your ScyllaDB Cloud credentials:
```sh
cp .env.example .env
```
Edit `.env` with your cluster details:
```sh
NODES=node-0.aws-us-east-1..clusters.scylla.cloud
CLUSTER_USERNAME=scylla
CLUSTER_PASSWORD=your-password
# Datacenter name from the ScyllaDB Cloud dashboard (Connect tab), e.g. AWS_US_EAST_1
CLUSTER_REGION=AWS_US_EAST_1
MIGRATE_PATH=./internal/database/migrations/migrate.cql
```
> You can find the node hostnames and datacenter name in the [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) under the **Connect** tab.
### 1.4 Installing dependencies and running the project
```sh
go run ./cmd/...
```
## 2. Connecting to the Cluster
The connection is set up in `internal/database/connection.go`. It reads credentials from environment variables and configures a DC-aware load balancing policy for optimal performance:
```go
func Connect() (*gocqlx.Session, error) {
nodes := os.Getenv("NODES")
username := os.Getenv("CLUSTER_USERNAME")
password := os.Getenv("CLUSTER_PASSWORD")
region := os.Getenv("CLUSTER_REGION")
hosts := strings.Split(nodes, ",")
cluster := gocql.NewCluster(hosts...)
cluster.Authenticator = gocql.PasswordAuthenticator{Username: username, Password: password}
cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.DCAwareRoundRobinPolicy(region))
session, err := gocqlx.WrapSession(cluster.CreateSession())
if err != nil {
return nil, err
}
return &session, nil
}
```
> If the connection is refused, check that your IP address is added to the allowed IPs in the ScyllaDB Cloud dashboard.
## 3. Handling Queries
Using the `gocqlx` package you can instantiate a session and run CQL queries.
```go
type Song struct {
Id string
Title string
Artist string
Album string
Created_at time.Time
}
func (s Song) String() string {
return fmt.Sprintf("Id: %s\nTitle: %s\nArtist: %s\nAlbum: %s\nCreated At: %s\n",
s.Id, s.Title, s.Artist, s.Album, s.Created_at)
}
```
### 3.1 Creating a Keyspace
The `keyspace` in ScyllaDB is equivalent to a database or schema. The migration file at `internal/database/migrations/migrate.cql` creates it automatically on startup:
```cql
CREATE KEYSPACE IF NOT EXISTS media_player
WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': '3'}
AND durable_writes = true;
```
### 3.2 Creating a table
A table stores the data for your app. The migration also creates the playlist table:
```sql
CREATE TABLE IF NOT EXISTS media_player.playlist (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
PRIMARY KEY (id, created_at)
) WITH CLUSTERING ORDER BY (created_at DESC);
```
### 3.3 Inserting data
Now that we have the keyspace and a table, we can add songs to the playlist:
```go
func (c *SongController) Insert(song *database.Song) error {
q := c.Session.Query(
`INSERT INTO media_player.playlist (id, title, artist, album, created_at) VALUES (now(), ?, ?, ?, ?)`,
[]string{":title", ":artist", ":album", ":created_at"}).
BindMap(map[string]interface{}{
":title": song.Title,
":artist": song.Artist,
":album": song.Album,
":created_at": time.Now(),
})
if err := q.Exec(); err != nil {
return fmt.Errorf("error in exec query to insert a song in playlist %w", err)
}
return nil
}
```
Use the `!add` command in the CLI to add a song interactively.
### 3.4 Reading data
List all songs stored in the playlist:
```go
func (c *SongController) List() ([]database.Song, error) {
songs := []database.Song{}
q := c.Session.Query("SELECT * FROM media_player.playlist", nil)
if err := q.SelectRelease(&songs); err != nil {
return songs, fmt.Errorf("error in exec query to list playlists: %w", err)
}
return songs, nil
}
```
Use the `!list` command in the CLI to display all songs. Each entry will look like:
```default
Id: a1500b3b-5a38-11ee-97d6-4495929e9df0
Title: My Favourite Song
Artist: Some Artist
Album: Great Album
Created At: 2023-09-23 17:42:56.541 +0000 UTC
```
### 3.5 Deleting data
Delete a row by its partition key:
```go
func (c *SongController) Delete() error {
songs, err := c.List()
if err != nil {
return err
}
index, err := c.selectSongToDelete(songs)
if err != nil {
return err
}
if index >= 0 && index < len(songs) {
songToDelete := songs[index]
q := c.Session.Query(`DELETE FROM media_player.playlist WHERE id = ?`,
[]string{":id"}).
BindMap(map[string]interface{}{
":id": songToDelete.Id,
})
if err := q.Exec(); err != nil {
return fmt.Errorf("error to exec delete query %w", err)
}
}
return nil
}
```
Use the `!delete` command in the CLI to select and delete a song interactively.
### 3.6 Stress testing
The `!stress` command inserts 100,000 records concurrently to test cluster throughput:
```go
func (c *StressController) Stress() error {
fmt.Println("Inserting 100,000 records into the database...")
start := time.Now()
var wg sync.WaitGroup
sem := make(chan bool, 550)
for i := 0; i < 100_000; i++ {
sem <- true
wg.Add(1)
go func() {
defer func() {
<-sem
wg.Done()
}()
q := c.Session.Query(
`INSERT INTO media_player.playlist (id, title, artist, album, created_at) VALUES (now(), ?, ?, ?, ?)`,
[]string{":title", ":artist", ":album", ":created_at"}).
BindMap(map[string]interface{}{
":title": "title teste",
":artist": "artist teste",
":album": "album teste",
":created_at": time.Now(),
})
if err := q.Exec(); err != nil {
fmt.Println(err.Error())
}
}()
}
wg.Wait()
fmt.Println("Time taken:", time.Since(start))
return nil
}
```
## Conclusion
You now have the knowledge to use the basics of ScyllaDB with Golang.
If you think something can be improved, please open an issue and let’s make it happen!
Did you like the content? Don’t forget to star the repo and follow us on socials.
# build-with-java.md
# Quick start: Java
In this tutorial you’ll build a Media Player to store your songs and build playlists.
## 1. Getting the Driver
Add the [ScyllaDB Java Driver](https://java-driver.docs.scylladb.com/) to your `pom.xml`:
```xml
com.scylladbjava-driver-core4.18.0.0
```
To package your application as a fat JAR (all dependencies included), use the `maven-shade-plugin`:
```xml
org.apache.maven.pluginsmaven-shade-plugin3.5.1packageshade${project.build.directory}/app.jarcom.scylladb.Appreference.conf
```
## 2. Connecting to the cluster
Get your database credentials from your [ScyllaDB Cloud Cluster View](https://cloud.scylladb.com/clusters) in the tab `Connect`.
> Add your machine’s IP Address to the list of allowed IP addresses in ScyllaDB Cloud. Otherwise, your connection will get refused.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
}
}
```
## 3. Handling Queries
With the ScyllaDB Java driver 4.x, use `session.execute(query)` to run CQL statements.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
ResultSet result = session.execute("SELECT * FROM system.clients LIMIT 10");
for (Row row : result) {
System.out.println(row.toString());
}
session.close();
}
}
```
### 3.1 Create a keyspace
A keyspace in ScyllaDB is a collection of tables with attributes which define how data is replicated on nodes.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
session.execute(
"CREATE KEYSPACE IF NOT EXISTS media_player " +
"WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': '3'} " +
"AND durable_writes = true"
);
session.close();
}
}
```
### 3.2 Create table
A table stores part or all of your app data (depending on how you structure your database schema).
Add the `keyspace` prefix in your table name and define a CQL string that creates a table to store your favorite songs.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
session.execute(
"CREATE TABLE IF NOT EXISTS media_player.playlist (" +
" id uuid," +
" title text," +
" album text," +
" artist text," +
" created_at timestamp," +
" PRIMARY KEY (id, created_at)" +
") WITH CLUSTERING ORDER BY (created_at DESC)"
);
session.close();
}
}
```
### 3.3 Insert data
Now that you have created a keyspace and a table, insert some songs to populate the table.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import java.net.InetSocketAddress;
import java.time.Instant;
import java.util.UUID;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
PreparedStatement ps = session.prepare(
"INSERT INTO media_player.playlist (id, title, artist, album, created_at) VALUES (?, ?, ?, ?, ?)"
);
session.execute(ps.bind(
UUID.randomUUID(),
"Stairway to Heaven",
"Led Zeppelin",
"Led Zeppelin IV",
Instant.now()
));
session.close();
}
}
```
### 3.4 Read data
Let’s read the songs from the database and print them to the terminal.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import java.net.InetSocketAddress;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
ResultSet rs = session.execute(
"SELECT id, title, album, artist, created_at FROM media_player.playlist PER PARTITION LIMIT 1 LIMIT 100"
);
for (Row row : rs) {
System.out.printf(
"ID: %s -> Song: %s -> Artist: %s -> Album: %s -> Created At: %s%n",
row.getUuid("id"),
row.getString("title"),
row.getString("artist"),
row.getString("album"),
row.getInstant("created_at")
);
}
session.close();
}
}
```
### 3.5 Update data
The `UPDATE` query takes two fields in the `WHERE` clause (`partition key` and `clustering key`). See the snippet below:
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import java.net.InetSocketAddress;
import java.time.Instant;
import java.util.UUID;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
UUID songId = UUID.fromString("d754f8d5-e037-4898-af75-44587b9cc424");
PreparedStatement ps = session.prepare(
"UPDATE media_player.song_counter SET times_played = times_played + 1 WHERE song_id = ?"
);
session.execute(ps.bind(songId));
session.close();
}
}
```
After the data gets inserted, query all columns and filter by the ID:
```default
scylla@cqlsh:media_player> select * from playlist where id = d754f8d5-e037-4898-af75-44587b9cc424;
id | created_at | album | artist | title
--------------------------------------+---------------------------------+-----------------+-------------+------------------
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-03-02 22:00:00.000000+0000 | Led Zeppelin IV | Led Zeppelin | Stairway to Heaven
```
### 3.6 Delete Data
Last things last! Let’s understand what we can DELETE with this statement. There’s the regular `DELETE` statement that focuses on `ROWS` and the other one that deletes data only from `COLUMNS`. The syntax is very similar.
```sql
-- Deletes all rows for a partition
DELETE FROM media_player.playlist WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
-- Deletes a specific cell
DELETE artist FROM media_player.playlist WHERE id = d754f8d5-e037-4898-af75-44587b9cc424 AND created_at = '2023-03-02 22:00:00+0000';
```
If you want to remove a specific column, you also should pass the `Clustering Key` as parameter and be specific about which row you want to delete something from.
On the other hand, deleting by partition key only will delete ALL the rows stored with that ID.
```java
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import java.net.InetSocketAddress;
import java.util.UUID;
public class App {
public static void main(String[] args) {
CqlSession session = CqlSession.builder()
.addContactPoint(new InetSocketAddress("node-0.aws-us-east-1.xxxx.clusters.scylla.cloud", 9042))
.withAuthCredentials("scylla", "your-awesome-password")
.withLocalDatacenter("AWS_US_EAST_1")
.build();
UUID songId = UUID.fromString("d754f8d5-e037-4898-af75-44587b9cc424");
PreparedStatement ps = session.prepare(
"DELETE FROM media_player.playlist WHERE id = ?"
);
session.execute(ps.bind(songId));
session.close();
}
}
```
## Conclusion
Yay! You now know how to get started with ScyllaDB in Java.
There is a simple project with this structure that you can check out [here](https://github.com/scylladb/scylla-cloud-getting-started/tree/main/java).
If you think something can be improved, please open an issue and let’s make it happen!
Did you like the content? Don’t forget to star the repo and follow us on socials.
# build-with-javascript.md
# Quick start: JavaScript (Node.js)
In this tutorial you’ll build a Media Player to store your songs and build playlists.
## 1. Getting the Driver
Install the [ScyllaDB Node.js RS Driver](https://github.com/scylladb/nodejs-rs-driver), a native ScyllaDB driver built on top of the ScyllaDB Rust driver.
```sh
$ npm install @scylladb/driver
$ yarn add @scylladb/driver
```
## 2. Connect to the cluster
Get your database credentials from your [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) in the tab `Connect`.
> Add your machine’s IP Address to the list of allowed IP addresses in ScyllaDB Cloud. Otherwise, your connection will get refused.
```js
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
// keyspace: 'your_keyspace' // optional
})
```
## 3. Handling Queries
With the NodeJS driver, you can use the function inside your cluster connection called `execute(query)` and build the query you want to execute inside your database/keyspace.
```js
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'}
})
const results = await cluster.execute('SELECT * FROM system.clients LIMIT 10')
console.log(results);
results.rows.forEach(row => console.log(JSON.stringify(row)))
```
### 3.1 Create a keyspace
The `keyspace` inside the ScyllaDB ecosystem can be interpreted as your `database` or `collection`.
On your connection boot, you don’t need to provide it but you will use it later and also is able to create when you need.
```js
async function runKeyspace () {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
})
const newKeyspace = (keyspaceName) =>
`CREATE KEYSPACE IF NOT EXISTS ${keyspaceName} WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': '3'};`;
await cluster.execute(newKeyspace('media_player'))
await cluster.shutdown()
}
runKeyspace();
```
> After that you probably will need to re-create your connection poiting which `keyspace` you want to use.
### 3.2 Creating a Table
A table is used to store part or all of your app data (depending on how structure your database schema).
Add the `keyspace` as a parameter in the connection object and define a CQL string that creates a table to store your favorite songs.
```js
async function runKeyspace (keyspace = null) {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: keyspace ?? 'media_player'
})
const createSongsTableQuery = `
CREATE TABLE IF NOT EXISTS songs (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (id, updated_at)
)
`;
await cluster.execute(createSongsTableQuery)
await cluster.shutdown()
}
runKeyspace('media_player');
```
### 3.3 Inserting data
Now that you have created a keyspace and a table, you need to insert some songs to populate the table.
```js
async function insertSongs (keyspace = 'media_player') {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: keyspace
})
let songList = [
{
id: cassandra.types.Uuid.random(),
title: 'Stairway to Heaven',
album: 'Led Zeppelin IV',
artist: 'Led Zeppelin',
createdAt: '2023-03-02 22:00:00',
updatedAt: '2023-03-02 22:00:00',
},
{
id: 'd754f8d5-e037-4898-af75-44587b9cc424',
title: 'Glimpse of Us',
album: 'Smithereens',
artist: 'Joji',
createdAt: '2023-03-02 22:00:00',
updatedAt: '2023-03-02 22:00:00',
},
{
id: cassandra.types.Uuid.random(),
title: 'Vegas',
album: 'From Movie ELVIS',
artist: 'Doja Cat',
createdAt: '2023-03-02 22:00:00',
updatedAt: '2023-03-02 22:00:00',
},
];
const newSongQuery = (song) => {
return `INSERT INTO songs (id, title, album, artist, created_at, updated_at)
VALUES (${song.id}, '${song.title}', '${song.album}', '${song.artist}', '${song.createdAt}', '${song.updatedAt}')`
}
for (let i in songList) {
await cluster.execute(newSongQuery(songList[i]))
}
await cluster.shutdown()
}
```
### 3.3 Reading data
Since probably we added more than 3 songs into our database, let’s list it into our terminal.
```js
const listSongs = async (keyspace = 'media_player') => {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: keyspace
})
let results = await cluster.execute("SELECT * FROM songs");
let rows = results.rows;
for (let i in rows) {
console.log(rows[i])
console.log(rows[i].id.toString())
}
await cluster.shutdown()
}
```
The result looks like this:
```default
$ node index.js
Row {
id: Uuid {
buffer:
},
title: 'Glimpse of Us',
album: 'Smithereens',
artist: 'Joji'
}
...
```
> Remeber to decode your Uuid if needed using the function `.toString()`
### 3.4 Updating Data
Ok, almost there! Now we’re going to learn about `UPDATE` but here’s a disclaimer:
> INSERT and UPDATES are not the same!
There’s a myth in ScyllaDB/Cassandra community that it’s the same for the fact that you just need the `Partition Key` and `Clustering Key` (if you have one) and query it.
Read more about [`INSERT` and `UPDATE`](https://docs.scylladb.com/stable/using-scylla/cdc/cdc-basic-operations.html)
As you can see, the `UPDATE` query takes two fields in the `WHERE` clause (PK and CK). Check the snippet below:
```js
const songToUpdate = {
id: 'd754f8d5-e037-4898-af75-44587b9cc424',
title: 'Glimpse of Us',
album: 'Smithereens',
artist: 'Joji',
};
const updateSong = async (song) => {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: 'media_player'
})
let results = await cluster.execute("SELECT * FROM songs");
let rows = results.rows;
let songToUpdate = rows.find((row) => row.id.toString() === song.id)
await cluster.execute(`UPDATE songs set title = 'Glimpse of US - Inutilismo' where id = ${songToUpdate.id} AND updated_at = '2023-03-02 23:10:00.00+0000';`);
await cluster.shutdown()
}
```
After the data gets inserted, query all columns and filter by the ID:
```default
scylla@cqlsh:media_player> select * from songs where id = d754f8d5-e037-4898-af75-44587b9cc424;
id | updated_at | album | artist | created_at | title
--------------------------------------+---------------------------------+-------------+--------+---------------------------------+----------------------------
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-03-02 22:00:00.000000+0000 | Smithereens | Joji | 2023-03-02 22:00:00.000000+0000 | Glimpse of Us
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-03-02 23:10:00.000000+0000 | null | null | null | Glimpse of US - Inutilismo
```
It only updated the field `title` and `updated_at` (the Clustering Key) and since we didn’t input the rest of the data, it will not be replicated as expected.
### 3.5 Deleting Data
Last things last! Let’s understand what we can DELETE with this statement. There’s the normal `DELETE` statement that focus on `ROWS` and other one that delete data only from `COLUMNS` and the syntax is very similar.
```sql
// Deletes a single row
DELETE FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
// Deletes a whole column
DELETE artist FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
```
If you want to erase a specific column, you also should pass as parameter the `Clustering Key` and be very specific in which register you want to delete something.
On the other hand, the “normal delete” just need the `Partition Key` to handle it. Just remember: if you use the statement `DELETE FROM
` it will delete ALL the rows that you stored with that ID.
```js
const deleteColumnFromSong = async (song) => {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: 'media_player'
})
await cluster.execute(`DELETE artist FROM songs WHERE id = ${song.id} AND updated_at = '${song.updatedAt}'`)
await cluster.shutdown()
}
const deleteSong = async (song) => {
const cluster = new cassandra.Client({
contactPoints: ["your-node-url.clusters.scylla.cloud", "your-node-url.clusters.scylla.cloud", ...],
localDataCenter: 'your-data-center', // Eg: AWS_SA_EAST_1
credentials: {username: 'scylla', password: 'your-awesome-password'},
keyspace: 'media_player'
})
await cluster.execute(`DELETE FROM songs WHERE id = ${song.id}`)
await cluster.shutdown()
}
```
## Conclusion
Yay! You now know how get started with ScyllaDB in Node.js.
If you think something can be improved, please open an issue and let’s make it happen!
Did you like the content? Don’t forget to star the repo and follow us on socials.
# build-with-php.md
# ScyllaDB Alternator: PHP Example
Using the “PHP AWS SDK” we can easily connect our project on any ScyllaDB instance (Local/Cloud) that is configured with Alternator flag.
## Quick Start
Create a new folder
If you’re starting a new project, make sure to require the SDK package in your application.
```shell
composer require aws/aws-sdk-php
```
You can instantiate a new DynamoDB Client by using:
```php
use Aws\DynamoDb\DynamoDbClient;
$alternatorClient = new DynamoDbClient([
'endpoint' => 'http://localhost:8000', // ScyllaDB Alternator LocalHost URL
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
```
## Queries
Here’s the DynamoDB queries used on this project so far:
### Creating a Table
```php
use Aws\DynamoDb\DynamoDbClient;
$alternatorClient = new DynamoDbClient([
'endpoint' => 'http://localhost:8000', // ScyllaDB Alternator LocalHost URL
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
$alternatorClient->createTable(['TableName' => 'songs',
'KeySchema' => [
['AttributeName' => 'id', 'KeyType' => 'HASH'],
['AttributeName' => 'created_at', 'KeyType' => 'RANGE'],
],
'AttributeDefinitions' => [
['AttributeName' => 'id', 'AttributeType' => 'S'],
['AttributeName' => 'created_at', 'AttributeType' => 'S'],
['AttributeName' => 'title', 'AttributeType' => 'S'],
['AttributeName' => 'artist', 'AttributeType' => 'S'],
['AttributeName' => 'album', 'AttributeType' => 'S'],
],
'ProvisionedThroughput' => [
'ReadCapacityUnits' => 10,
'WriteCapacityUnits' => 10
]
]);
```
### Adding Items
```php
use Aws\DynamoDb\DynamoDbClient;
$alternatorClient = new DynamoDbClient([
'endpoint' => 'http://localhost:8000', // ScyllaDB Alternator LocalHost URL
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
$alternatorClient->putItem('PutRequest' => [
'TableName' => 'songs',
'Item' => [
'id' => ['S' => 'string'],
'created_at' => ['S' => 'string'],
'title' => ['S' => 'string'],
'album' => ['S' => 'string'],
'artist' => ['S' => 'string'],
]
]);
```
### Listing Items
```php
use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Marshaler;
$marshaler = new Marshaler();
$alternatorClient = new DynamoDbClient([
'endpoint' => 'http://localhost:8000', // ScyllaDB Alternator LocalHost URL
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
$results = $client->scan(['TableName' => 'songs']);
foreach ($results['Items'] as $item) {
$parsedItem = $marshaler->unmarshalItem($item);
var_dump($parsedItem['title'])
}
```
### Deleting Items
```php
use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\Marshaler;
$marshaler = new Marshaler();
$alternatorClient = new DynamoDbClient([
'endpoint' => 'http://localhost:8000', // ScyllaDB Alternator LocalHost URL
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
$client->deleteItem([
'TableName' => 'songs',
'Key' => [
'id' => ['S' => 'some-uuid-here'],
'created_at' => ['S' => 'Y-m-d H:i:s'],
],
]);
```
# build-with-python.md
# Quick start: Python
In this tutorial you’ll build a Media Player app to store your songs and create playlists.
## 1. Get the development environment ready
### 1.1 Prerequisites:
* [Python 3.7+](https://www.python.org/downloads/)
* [Virtualenv](https://virtualenv.pypa.io/en/latest/how-to/install.html)
### 2.2 Set up the environment
Create a new folder for the project:
```sh
mkdir scylladb-cloud-python
cd scylladb-cloud-python
```
Create a new virtual environment and activate it:
```bash
virtualenv env
source env/bin/activate
```
Install the [Python ScyllaDB Driver](https://pypi.org/project/scylla-driver/).
```bash
pip install scylla-driver
```
## 2. Connect to the Cluster
Get your database credentials from your [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) in the tab `Connect`.
> Add your machine’s IP Address to the list of allowed IP addresses in ScyllaDB Cloud. Otherwise, your connection will get refused.
```python
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
```
## 3. Handling Queries
With the Python driver, you can use the function inside your cluster connection called `execute(query)` and build the query you want to execute inside your database/keyspace. You also can use the `execute_async()` for asynchronous queries.
```python
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect()
results = session.execute('SELECT * FROM system.clients LIMIT 10')
for item in results:
print(item)
print(item.address)
```
Response will be a ResultSet of Rows:
```default
Row(address='123.123.123.123', port=46160, client_type='cql', connection_stage='AUTHENTICATING', driver_name='Scylla Python Driver', driver_version='3.26.2', hostname=None, protocol_version=4, shard_id=0, ssl_cipher_suite=None, ssl_enabled=None, ssl_protocol=None, username='scylla')
123.123.123.123
```
### 3.1 Create a Keyspace
The `keyspace` inside the ScyllaDB ecosystem can be interpreted as your `database` or `collection`.
On your connection boot, you don’t need to provide it but you will use it later and also is able to create when you need.
{% raw %}
```python
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect()
keyspaceName = "media_player"
session.execute("CREATE KEYSPACE {};".format(keyspaceName))
session.set_keyspace('media_player')
```
{% endraw %}
Unfortunately you can’t set a keyspace with `PreparedStatements`, so you will need to build the query by yourself.
> You can use the `session.set_keyspace()` function to switch between keyspaces.
### 3.2 Creating a Table
A table is used to store a part or all of your app data (depending on how you structure your database schema).
Add the `keyspace` as a parameter in the connection object and define a CQL string that creates a table to store your favorite songs.
```python
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect('media_player')
tableQuery = """
CREATE TABLE songs (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
PRIMARY KEY (id, created_at)
)
"""
session.execute(tableQuery)
```
### 3.3 Insert data
Now that you have created a keyspace and a table, insert some songs to populate the table.
```python
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
import uuid
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect('media_player')
songList = [
{
"id": uuid.UUID('d754f8d5-e037-4898-af75-44587b9cc424'),
"title": 'Glimpse of Us',
"album": 'Smithereens',
"artist": 'Joji',
"createdAt": datetime.now(),
},
{
"id": uuid.uuid4(),
"title": 'Stairway to Heaven',
"album": 'Led Zeppelin III',
"artist": 'Led Zeppelin',
"createdAt": datetime.now(),
},
{
"id": uuid.uuid4(),
"title": 'Vegas',
"album": 'From Movie ELVIS',
"artist": 'Doja Cat',
"createdAt": datetime.now(),
},
];
query = session.prepare("""
INSERT INTO songs (id, title, album, artist, created_at) VALUES (?, ?, ?, ?, ?)
""")
for music in songList:
session.execute(query, music.values())
```
### 3.3 Read data
Since probably we added more than 3 songs into our database, let’s list it into our terminal.
```python
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect('media_player')
query = session.prepare("SELECT * FROM songs");
results = session.execute(query);
for song in results:
print(song.id)
print(song.title)
```
The result looks like this:
```default
0a5d5cfb-0275-43a4-9523-45ad256666f8
Vegas
```
### 3.4 Update data
The `UPDATE` query in the fact is equals to `INSERT` regarding the syntax. You just need the `Partition Key` and `Clustering Key` (if you have one) and query it.
The `UPDATE` query takes two fields in the `WHERE` clause (PK and CK). See the snippet below:
```py
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
import uuid
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect('media_player')
songToUpdate = {
"id": uuid.UUID('d754f8d5-e037-4898-af75-44587b9cc424'),
"title": 'Glimpse of Us',
"album": '2022 Em Uma Música',
"artist": 'Lucas Inutilismo',
"createdAt": datetime.now()
}
session.execute("""
UPDATE songs SET
title = %(title)s,
album = %(album)s,
artist = %(artist)s
WHERE id = %(id)s AND created_at = %(createdAt)s
""", songToUpdate)
```
After the data gets inserted, query all columns and filter by the ID:
```default
scylla@cqlsh:media_player> select * from songs where id = d754f8d5-e037-4898-af75-44587b9cc424;
id | created_at | album | artist | title
--------------------------------------+---------------------------------+--------------------+------------------+---------------
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-07-11 14:22:43.329000+0000 | Smithereens | Joji | Glimpse of Us
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-07-11 14:23:31.630000+0000 | 2022 Em Uma Música | Lucas Inutilismo | Glimpse of Us
d754f8d5-e037-4898-af75-44587b9cc424 | 2023-07-11 14:23:51.996000+0000 | 2022 Em Uma Música | null | Glimpse of Us
(3 rows)
```
In the snippet above, we updated the data fully but as you can see in the `SELECT` query, I ran one more update but without the `artist` column. So, as said before: the only “not nullable” fields are the `Partition Keys` and `Clustering Keys`.
### 3.5 Delete Data
Let’s understand what we can DELETE with this statement. There’s the normal `DELETE` statement that focuses on `ROWS` and another one that deletes data only from `COLUMNS` and the syntax is very similar.
```sql
// Deletes a single row
DELETE FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
// Deletes a whole column
DELETE artist FROM songs WHERE id = d754f8d5-e037-4898-af75-44587b9cc424;
```
If you want to remove a specific column, you also should pass as parameter the `Clustering Key` and be very specific in which register you want to delete something.
On the other hand, the “normal delete” just need the `Partition Key` to handle it. Just remember: if you use the statement “DELETE FROM keyspace.table_name” it will delete ALL the rows that you stored with that ID.
```py
from cassandra.cluster import Cluster
from datetime import datetime
from cassandra.auth import PlainTextAuthProvider
import uuid
songToDelete = {
"id": uuid.UUID('d754f8d5-e037-4898-af75-44587b9cc424'),
"title": 'Glimpse of Us',
"album": '2022 Em Uma Música',
"artist": 'Lucas Inutilismo',
"createdAt": datetime.now()
}
cluster = Cluster(
contact_points=[
"your-node-url-1.clusters.scylla.cloud",
"your-node-url-2.clusters.scylla.cloud",
"your-node-url-3.clusters.scylla.cloud",
],
auth_provider=PlainTextAuthProvider(username='scylla', password='your-awesome-password')
)
session = cluster.connect('media_player')
deleteQuery = session.prepare("DELETE FROM songs WHERE id = ? ")
session.execute(deleteQuery, [songToDelete['id']])
```
## 4. Conclusion
Yay! You now know how get started with ScyllaDB in Python.
If you think something can be improved, please open an issue and let’s make it happen!
There is a sample project that you can learn more about the concepts and also have a good time testing our ScyllaDB Cloud Cluster, check it out [here](https://github.com/scylladb/scylla-cloud-getting-started/tree/main/python).
Did you like the content? Don’t forget to star the repo and follow us on socials.
# build-with-python.md
# ScyllaDB Alternator: Python Example
Using the “Python AWS SDK” we can easily connect our project on any ScyllaDB instance (Local/Cloud) that is configured with Alternator flag.
## Quick Start
Create a new folder
If you’re starting a new project, make sure to require the SDK package in your application.
```shell
sudo pip install --upgrade boto3
```
You can instantiate a new DynamoDB Client by using:
```python
import boto3
alternator = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
```
## Queries
Here’s the DynamoDB queries used on this project so far:
### Creating a Table
```python
import boto3
alternator = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
table = alternator.create_table(
TableName="songs",
KeySchema=[
{'AttributeName': 'id', 'KeyType': 'HASH'},
{'AttributeName': 'created_at', 'KeyType': 'RANGE'},
],
AttributeDefinitions=[
{'AttributeName': 'id', 'AttributeType': 'S'},
{'AttributeName': 'created_at', 'AttributeType': 'S'},
{'AttributeName': 'title', 'AttributeType': 'S'},
{'AttributeName': 'artist', 'AttributeType': 'S'},
{'AttributeName': 'album', 'AttributeType': 'S'},
],
ProvisionedThroughput={'ReadCapacityUnits': 10, 'WriteCapacityUnits': 10}
);
table.wait_until_exists()
table.put_item(Item={
'id': str(uuid.uuid4()),
'created_at': str(datetime.now()),
'title': 'Song 1',
'album': 'Album 1',
'artist': 'artistName',
})
```
### Adding Items
```python
alternator = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
table = alternator.Table('songs')
table.put_item(Item={
'id': str(uuid.uuid4()),
'created_at': str(datetime.now()),
'title': 'Song 1',
'album': 'Album 1',
'artist': 'artistName',
})
```
### Listing Items
```python
alternator = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
table = alternator.Table('songs')
listSongs = table.scan()
enumSongList = list(enumerate(listSongs['Items']))
for rowIndex, song in enumSongList:
print(f"Index: {rowIndex} | Title: {song['title']} | Album: {song['album']} ")
```
### Delete Item
```python
alternator = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
table = alternator.Table('songs')
listSongs = table.scan()
table.delete_item(Key={
'id': 'id',
'created_at': 'y-m-d H:i:s'
})
```
# build-with-rust.md
# Quick start: Rust (Crablang)
In this tutorial you’ll build a simple Media Player to store your songs and manage a playlist via the command line.
## 1. Setup the Environment
Let’s download Rust and the dependencies needed for this project.
### 1.1 Downloading Rust and Dependencies
If you don’t have Rust installed yet, run the command below — it installs Rust and Cargo.
```default
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
### 1.2 Starting the project
Now with Rust and Cargo installed, create a new project:
```sh
cargo new media_player
cd media_player
```
### 1.3 Setting the project dependencies
Update `Cargo.toml` with the project dependencies:
```toml
[package]
name = "media_player"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.72"
chrono = "0.4.26"
clap = { version = "4.3.21", features = ["derive"] }
futures = "0.3.28"
rand = "0.8.5"
scylla = { version = "1.0", features = ["chrono-04"] }
serde = { version = "1.0.174", features = ["derive", "serde_derive"] }
[dependencies.tokio]
version = "1"
features = ["full"]
[dependencies.uuid]
version = "1.4.1"
features = [
"v4",
"fast-rng",
"macro-diagnostics",
]
```
* [Scylla](https://crates.io/crates/scylla): ScyllaDB Rust driver. `chrono-04` enables serialization of `chrono` types.
* [Clap](https://crates.io/crates/clap): CLI argument parsing.
* [Uuid](https://crates.io/crates/uuid): UUID generation.
* [Tokio](https://crates.io/crates/tokio): Async runtime for Rust.
* [Anyhow](https://crates.io/crates/anyhow): Idiomatic error handling.
* [Chrono](https://crates.io/crates/chrono): DateTime/Timestamp handling.
* [Futures](https://crates.io/crates/futures): Utilities for working with Rust futures.
## 2. Connecting to the Cluster
Make sure to get the right credentials on your [ScyllaDB Cloud Dashboard](https://cloud.scylladb.com/clusters) in the tab `Connect`.
The app accepts connection details as CLI arguments. Define the argument struct with [clap](https://crates.io/crates/clap):
```rust
use clap::Parser;
#[derive(Parser, Default, Debug)]
#[clap(author, version, about)]
pub struct ConnectionDetails {
/// Scylla Cloud Node URL's (one or more, space- or comma-separated)
#[arg(num_args = 1.., value_parser, value_delimiter = ',')]
pub nodes: Vec,
/// Cluster Username
#[arg(short, long)]
username: String,
/// Cluster Password
#[arg(short, long)]
password: String,
}
```
Then build the session in `database.rs`:
```rust
use anyhow::Context;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::time::Duration;
pub async fn new_session(config: &ConnectionDetails) -> Result {
let nodes = config
.nodes
.iter()
.filter(|i| !i.is_empty())
.collect::>();
let session: Session = SessionBuilder::new()
.known_nodes(nodes)
.connection_timeout(Duration::from_secs(5))
.user(config.username.to_string(), config.password.to_string())
.build()
.await
.context("Connection Refused. Check your credentials and your IP linked on the ScyllaDB Cloud.")?;
Ok(session)
}
```
> If the connection is refused, check that your IP address is added to the allowed IPs list in your ScyllaDB Cloud dashboard.
Run the app with:
```sh
cargo run -- --username scylla --password your-cluster-password \
node-0.aws-sa-east-1.xxx.clusters.scylla.cloud \
node-1.aws-sa-east-1.xxx.clusters.scylla.cloud \
node-2.aws-sa-east-1.xxx.clusters.scylla.cloud
```
## 3. Handling Queries
The ScyllaDB Rust driver supports prepared statements (recommended for repeated queries) and unpaged/iter queries.
\\\\
Use `session.prepare()` to prepare a statement once, then `execute_unpaged` or `execute_iter` to run it.
```rust
use futures::TryStreamExt;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::net::IpAddr;
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(30))
.user("scylla", "your-awesome-password")
.build()
.await
.expect("connection refused");
let query = "SELECT address, port, connection_stage FROM system.clients LIMIT 5";
session
.query_iter(query, &[])
.await?
.rows_stream::<(IpAddr, i32, String)>()?
.try_for_each(|row| async move {
println!("IP -> {}, Port -> {}, CS -> {}", row.0, row.1, row.2);
Ok(())
})
.await?;
Ok(())
}
```
Output should look like:
```default
IP -> 123.123.123.69, Port -> 61667, CS -> READY
IP -> 123.123.123.69, Port -> 62377, CS -> AUTHENTICATING
```
### 3.1 Creating a Keyspace
The `keyspace` in ScyllaDB can be interpreted as your `database`. You can check whether it already exists using the driver’s cluster metadata API and create it if needed.
```rust
use anyhow::Result;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
let keyspace = String::from("prod_media_player");
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(5))
.user("scylla", "your-password")
.build()
.await
.unwrap();
// Use the driver's metadata API to check if the keyspace already exists.
// Normally you'd use CREATE KEYSPACE IF NOT EXISTS, but this showcases the metadata API.
let has_keyspace = session
.get_cluster_state()
.get_keyspace(&keyspace)
.is_some();
if !has_keyspace {
let create_ks = format!(
"CREATE KEYSPACE {} WITH REPLICATION = {{'class': 'NetworkTopologyStrategy', 'replicationFactor': '3'}};",
keyspace
);
session.query_unpaged(create_ks, &[]).await?;
println!("Keyspace {} created!", &keyspace)
} else {
println!("Keyspace {} already exists!", &keyspace)
}
Ok(())
}
```
### 3.2 Creating a Table
A table stores your application data. Here we create the `songs` table in the `prod_media_player` keyspace.
```rust
use anyhow::Result;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::time::Duration;
static KEYSPACE: &str = "prod_media_player";
static TABLE: &str = "songs";
#[tokio::main]
async fn main() -> Result<()> {
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(30))
.user("scylla", "****")
.build()
.await
.expect("connection refused");
// Use the driver's metadata API to check if the table already exists.
let has_table = session
.get_cluster_state()
.get_keyspace(KEYSPACE)
.and_then(|ks| ks.tables.get(TABLE))
.is_some();
if !has_table {
let create_table = format!(
"CREATE TABLE {}.{} (
id uuid,
title text,
album text,
artist text,
created_at timestamp,
PRIMARY KEY (id, created_at)
)",
KEYSPACE, TABLE
);
session.query_unpaged(create_table, &[]).await?;
println!("Table {} created!", TABLE)
} else {
println!("Table {} already exists!", TABLE)
}
Ok(())
}
```
### 3.3 Inserting data
Now that we have the keyspace and table, let’s populate it with songs. We use prepared statements for efficiency.
```rust
use anyhow::Result;
use chrono::Utc;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::str::FromStr;
use std::time::Duration;
use uuid::Uuid;
#[tokio::main]
async fn main() -> Result<()> {
let keyspace = "prod_media_player";
let table = "songs";
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(5))
.user("scylla", "your-password")
.build()
.await
.unwrap();
let now = Utc::now();
let song_list = vec![
(
Uuid::new_v4(),
"Stairway to Heaven",
"Led Zeppelin IV",
"Led Zeppelin",
now,
),
(
Uuid::from_str("d754f8d5-e037-4898-af75-44587b9cc424").unwrap(),
"Glimpse of Us",
"Smithereens",
"Joji",
now,
),
(Uuid::new_v4(), "Vegas", "From Movie ELVIS", "Doja Cat", now),
];
let insert_query = format!(
"INSERT INTO {}.{} (id, title, album, artist, created_at) VALUES (?,?,?,?,?)",
keyspace, table
);
let prepared = session.prepare(insert_query).await?;
for song in &song_list {
session.execute_unpaged(&prepared, song).await?;
println!("Inserted: {}", song.1);
}
Ok(())
}
```
### 3.4 Reading data
List songs from the database using `execute_iter` with a prepared statement:
```rust
use anyhow::Result;
use chrono::{DateTime, Utc};
use futures::TryStreamExt;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::time::Duration;
use uuid::Uuid;
#[tokio::main]
async fn main() -> Result<()> {
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(5))
.user("scylla", "your-awesome-password")
.build()
.await
.unwrap();
let prepared = session
.prepare(
"SELECT id, title, album, artist, created_at FROM prod_media_player.songs LIMIT 10",
)
.await?;
session
.execute_iter(prepared, &[])
.await?
.rows_stream::<(Uuid, String, String, String, DateTime)>()?
.try_for_each(|row| async move {
println!("ID: {} | Song: {} | Album: {} | Created At: {}", row.0, row.1, row.2, row.4);
Ok(())
})
.await?;
Ok(())
}
```
The result will look like this:
```default
ID: d754f8d5-e037-4898-af75-44587b9cc424 | Song: Glimpse of Us | Album: Smithereens | Created At: 2023-08-09 02:11:55 UTC
ID: 72f940de-1c8b-46ce-a09d-8c52e66fa21f | Song: Stairway to Heaven | Album: Led Zeppelin IV | Created At: 2023-08-09 02:11:55 UTC
```
### 3.5 Deleting Data
Delete a row by its partition key (`id`). Since `id` is the partition key, this removes all rows with that UUID:
```rust
use anyhow::Result;
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use std::str::FromStr;
use std::time::Duration;
use uuid::Uuid;
#[tokio::main]
async fn main() -> Result<()> {
let session: Session = SessionBuilder::new()
.known_nodes(&[
"node-0.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-1.aws-sa-east-1.xxx.clusters.scylla.cloud",
"node-2.aws-sa-east-1.xxx.clusters.scylla.cloud",
])
.connection_timeout(Duration::from_secs(5))
.user("scylla", "your-awesome-password")
.build()
.await
.unwrap();
let song_id = Uuid::from_str("d754f8d5-e037-4898-af75-44587b9cc424").unwrap();
let prepared_query = session
.prepare("DELETE FROM prod_media_player.songs WHERE id = ?")
.await?;
session
.execute_unpaged(&prepared_query, (song_id,))
.await?;
println!("Song deleted!");
Ok(())
}
```
## 4. Running the Full Application
Clone the repository and run the complete Media Player app:
```sh
git clone https://github.com/scylladb/scylla-cloud-getting-started.git
cd scylla-cloud-getting-started/rust
cargo run -- --username scylla --password your-cluster-password \
node-0.aws-sa-east-1.xxx.clusters.scylla.cloud \
node-1.aws-sa-east-1.xxx.clusters.scylla.cloud \
node-2.aws-sa-east-1.xxx.clusters.scylla.cloud
```
Once started, you’ll see a prompt with available commands:
```default
------------------------------------
Here some possibilities
!add - add new song
!list - list all songs
!delete - delete a specific song
!stress - stress testing with mocked data
!q - quit
------------------------------------
Type any command:
```
## Conclusion
You now have the knowledge to use the basics of ScyllaDB with Rust.
If you think something can be improved, please open an issue and let’s make it happen!
Did you like the content? Don’t forget to star the repo and follow us on socials.
# design-and-data-model.md
# Design and Data Model
You can learn more about Data Modeling in ScyllaDB (and NoSQL) by taking
[this course](https://university.scylladb.com/courses/data-modeling/) on ScyllaDB
University. The main goal of data modeling in ScyllaDB is to perform queries fast,
even if we sometimes have to duplicate data.
Let’s build our schema around the queries we are going to run against our domain
entities. When creating the data model, you need to consider both the conceptual
data model and the application workflow: which queries will be performed by
which users and how often.
To achieve that, we want:
- Even data distribution
- To minimize the number of partitions accessed in a read query.
On the other hand, our focus won’t be on avoiding data duplication or minimizing
the number of writes. You’re probably familiar with the steps defined here:

## Conceptual Data Model
Starting with the conceptual data model, we need to identify the key entities
and the relationships between them. Our application is focused in a single entity called ‘songs’. The concept is to fill with many songs as we want.

## Application Workflow
Next, we move on to the Application Workflow. In this part, we identify the main
queries or what questions we will ask the database. This part is important in
ScyllaDB, and other NoSQL databases and, as opposed to relational databases is
performed early on in the data modeling process. Remember that our data modeling
is built around the queries.
## Application Features
* Insert a Song
* List all songs
* Delete a song
* Stress Testing
## Queries
Now we can detail the above queries in
[CQL](https://university.scylladb.com/courses/data-modeling/lessons/basic-data-modeling-2/topic/cql-cqlsh-and-basic-cql-syntax/):
Q1: Create a Keyspace
```none
CREATE KEYSPACE prod_media_player;
```
Q2: Create a Table
```none
CREATE TABLE songs (
id int,
title text,
album text,
artist text,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (id, updated_at)
);
```
Q3: Insert a new song
```none
INSERT INTO prod_media_player.songs (id,title,artist,album,created_at) VALUES (?,?,?,?,?);
```
Q4: List all songs
```none
SELECT * FROM songs;
```
Q5: Delete a specific song
```none
DELETE FROM songs WHERE id = ?
```
## Helpful Material
Some more advanced topics not covered in this guide are
[Collections](https://university.scylladb.com/courses/data-modeling/lessons/advanced-data-modeling/topic/common-data-types-and-collections/),
User-Defined[Types](https://university.scylladb.com/courses/data-modeling/lessons/advanced-data-modeling/topic/user-defined-types-udt/)
(UDT), expiring data with [time to live (TTL)](https://university.scylladb.com/courses/data-modeling/lessons/advanced-data-modeling/topic/expiring-data-with-ttl-time-to-live/),
and [Counters](https://university.scylladb.com/courses/data-modeling/lessons/advanced-data-modeling/topic/counters/).
To summarize, when data modeling with ScyllaDB, we have to know our data, think
about our queries, pay attention to the primary key and clustering key
selection, and not be afraid to duplicate data.
# getting-started.md
# Getting Started with ScyllaDB Cloud: A sample Media Player App
## Introduction
This guide will show you how to create a Cluster into ScyllaDB Cloud, create an Media Player app from scratch and configure it
to use ScyllaDB as the backend datastore. It’ll walk you through all the stages
of the development process, from gathering requirements to building and running
the application.
As an example, you will use an application called Media Player. Media Player stores all the tracks
that you want to listen and count how many times you listened to. The application consists of three parts:
- Validate if you have the Keyspace and Table needed to start storing your tracks/songs;
- Access to an REPL with commands to store/list/delete songs;
- Run a simple stresser into ScyllaDB Cloud.
## Requirements
### Prerequisites for Deploying the Application
The example application uses ScyllaDB Cloud to run a three-node ScyllaDB cluster. You can claim your free ScyllaDB Cloud account [here](https://scylladb.com/cloud).
### Performance Requirements
The application has two performance-related parts: sensors that write to
the database (throughput sensitive) and a backend dashboard that reads from
the database (latency sensitive).
* This example assumes 99% writes (songs) and 1% reads (backend dashboard).
* SLA:
- Writes: throughput of 100K operations per second.
- Reads: latency of up to 10 milliseconds for the
[99th percentile](https://www.scylladb.com/glossary/low-latency-database/).
* The application requires high availability and fault tolerance. Even if a
ScyllaDB node goes down or becomes unavailable, the cluster is expected to
remain available and continue to provide service. You can learn more about
ScyllaDB high availability in [this lesson](https://university.scylladb.com/courses/scylla-essentials-overview/lessons/high-availability/).
### Additional Resources
- [ScyllaDB Essentials](https://university.scylladb.com/courses/scylla-essentials-overview/) course on ScyllaDB University. It provides an introduction to ScyllaDB and explains the basics.
- [Data Modeling and Application Development](https://university.scylladb.com/courses/data-modeling/) course on ScyllaDB University. It explains basic and advanced data modeling techniques, including information on workflow application, query analysis, denormalization, and other NoSQL data modeling topics.
- [ScyllaDB Documentation](https://docs.scylladb.com/)
- ScyllaDB users [slack channel](http://slack.scylladb.com/)
# getting-started.md
# Getting Started with ScyllaDB Cloud + DynamoDB Compatible API: A sample Media Player App with Alternator
## 1. Introduction
This guide will show you how to create a cluster in ScyllaDB Cloud, create a Media Player app from scratch, and configure it
to use ScyllaDB as the backend datastore. It’ll walk you through all the stages
of the development process, from gathering requirements to building and running
the application.
As an example, you will use an application called Media Player. Media Player stores all the tracks
you want to listen to and counts how many times you have listened to them. The application consists of three parts:
- Validate if you have the Keyspace and Table needed to start storing your tracks/songs;
- Access a REPL with commands to store/list/delete songs;
- Run a simple stressor against ScyllaDB Cloud.
## 2. Requirements
Before starting your first project with the ScyllaDB Alternator API, we need to set up your environment.
### 2.1 Create a ScyllaDB Cloud account
Before you start coding the project, you should create an account at [ScyllaDB Cloud](https://cloud.scylladb.com) or login if you already have one.

### 2.2 Create a Cluster
After creating and logging in to your ScyllaDB Cloud account, click **Free Trial** (or **New Cluster** > **Dedicated VM**). There, you should:
- Give your cluster a name
- Select “Amazon DynamoDB API compatible” in the **ScyllaDB API** section
- Select the nearest region for your cluster
- Keep the default auto-scaling policy. ScyllaDB Cloud scales the cluster automatically, so you don’t need to pick a node size.
On the network tab, make sure your IP address is correct and click “Launch Cluster”.

## 3. Connecting to your cluster
Now that you have the ScyllaDB Alternator running, you will see a panel like the picture below. It will be a three node cluster.

As you can see on the first node, the IP “100.51.91.97” will be used to connect to the “Endpoint URL” in any DynamoDB SDK or API.
Examples:
```python
# Python Example
import boto3
alternator_client = boto3.resource('dynamodb',endpoint_url='http://localhost:8000',
region_name='None', aws_access_key_id='None', aws_secret_access_key='None')
```
```php
// PHP with AWS SDK
use Aws\DynamoDb\DynamoDbClient;
$alternatorClient = new DynamoDbClient([
'endpoint' => "18.231.92.93:8000", // Alternator
'credentials' => ['key' => 'None', 'secret' => 'None'],
'region' => 'None'
]);
```
Now you are able to send requests to your ScyllaDB Alternator instance. Select the SDK for the language of your choice and start building!
## 4. Next Steps
Now that you have everything ready, let’s move to the project Design and Data Model with DynamoDB spectations!