Rust x Momento | client-sdk-rust でキャッシュを操作する
Momento Cache とは
Momento Cacheは、世界初の真のサーバーレスキャッシングサービスです。瞬時の弾力性、scale-to-zero機能、圧倒的な高速パフォーマンスを提供します。 容量の選択、管理、プロビジョニングが必要な時代は終わりました。Momento Cacheでは、SDKを入手し、エンドポイントを取得し、コードに数行入力するだけで、すぐに実行できます。
圧倒的な高速性と使いやすさが売りのサーバーレスのキャッシュサービスです。
client-sdk-rust でキャッシュを操作する
momentohq/client-sdk-rust
Rust の SDK も提供されています🎉
Cargo.toml
momento の SDK と非同期ランタイムである tokio を使用します。
[dependencies]
momento = "0.33.1"
tokio = { version = "1.36.0", features = ["full"] }
Momento キャッシュと API キー
Momento コンソール でキャッシュを作成し、環境変数 MOMENTO_CACHE_NAME
を設定して実行しています。
また、API キーは Momento コンソール から取得できます。
MOMENTO_CACHE_NAME={Cache name} MOMENTO_AUTH_TOKEN={API key} cargo run
Scalar
単純な文字列をキャッシュさせます。
set
と get
を使用しています。
use momento::response::Get;
use momento::{CredentialProvider, MomentoError, SimpleCacheClientBuilder};
use std::process;
use std::time::Duration;
pub async fn scalar() -> Result<(), MomentoError> {
// SimpleCacheClient の生成
let mut cache_client = match SimpleCacheClientBuilder::new(
CredentialProvider::from_env_var("MOMENTO_AUTH_TOKEN".to_string())?,
Duration::from_secs(60),
) {
Ok(client) => client,
Err(err) => {
eprintln!("{err}");
process::exit(1);
}
}
.build();
let cache_name = std::env::var("MOMENTO_CACHE_NAME").expect("Cache name must be set!");
// キー・バリュー(文字列)のセット
let key = "my_key".to_string();
let value = "my_value".to_string();
println!("Setting key: {key}, value: {value}");
match cache_client
.set(&cache_name, key.clone(), value.clone(), None)
.await
{
Ok(res) => {
println!("{:?}", res);
}
Err(err) => {
eprintln!("{err}");
}
};
// キー・バリュー(文字列)の取得
match cache_client.get(&cache_name, key.clone()).await {
Ok(r) => match r {
Get::Hit { value } => {
let v: String = value.try_into().expect("I stored a string!");
println!("Got value: {v}");
}
Get::Miss => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
Ok(())
}
Dictionary
構造体をキャッシュさせることもできます。
Rust では HashMap として扱います。
dictionary_fetch
ではキーを指定して取得でき、dictionary_get
では構造体のフィールドまで指定することで取得する値を絞り込むことができます。
use momento::response::{DictionaryFetch, DictionaryGet};
use momento::{CollectionTtl, CredentialProvider, MomentoError, SimpleCacheClientBuilder};
use std::collections::HashMap;
use std::process;
use std::time::Duration;
pub async fn dictionary() -> Result<(), MomentoError> {
// SimpleCacheClient の生成
let mut cache_client = match SimpleCacheClientBuilder::new(
CredentialProvider::from_env_var("MOMENTO_AUTH_TOKEN".to_string())?,
Duration::from_secs(60),
) {
Ok(client) => client,
Err(err) => {
eprintln!("{err}");
process::exit(1);
}
}
.build();
let cache_name = std::env::var("MOMENTO_CACHE_NAME").expect("Cache name must be set!");
// Dictionary のセット
let key = "my_dict".to_string();
let dict = HashMap::from_iter([
("my_dict_1", "my_dict_value_1"),
("my_dict_2", "my_dict_value_2"),
]);
println!("Setting key: {key}, value: {:?}", dict);
let ttl = CollectionTtl::default();
match cache_client
.dictionary_set(&cache_name, key.clone(), dict.clone(), ttl)
.await
{
Ok(res) => {
println!("{:?}", res);
}
Err(err) => {
eprintln!("{err}");
}
};
// Dictionary の取得
match cache_client
.dictionary_fetch(&cache_name, key.clone())
.await
{
Ok(r) => match r {
DictionaryFetch::Hit { value } => {
let v: HashMap<String, String> = value.try_into().expect("I stored a dictionary!");
println!("Got value: {:?}", v);
}
DictionaryFetch::Miss => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
// Dictionary の取得(キーでの絞り込み)
match cache_client
.dictionary_get(&cache_name, key.clone(), vec!["my_dict_1"])
.await
{
Ok(r) => match r {
DictionaryGet::Hit { value } => {
let v: HashMap<String, String> = value.try_into().expect("I stored a dictionary!");
println!("Got value: {:?}", v);
}
DictionaryGet::Miss => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
Ok(())
}
List
配列をキャッシュさせることもできます。
use momento::{CollectionTtl, CredentialProvider, MomentoError, SimpleCacheClientBuilder};
use std::process;
use std::time::Duration;
pub async fn list() -> Result<(), MomentoError> {
// SimpleCacheClient の生成
let mut cache_client = match SimpleCacheClientBuilder::new(
CredentialProvider::from_env_var("MOMENTO_AUTH_TOKEN".to_string())?,
Duration::from_secs(60),
) {
Ok(client) => client,
Err(err) => {
eprintln!("{err}");
process::exit(1);
}
}
.build();
let cache_name = std::env::var("MOMENTO_CACHE_NAME").expect("Cache name must be set!");
// List のセット
let key = "my_lst".to_string();
let list = ["my_list_1", "my_list_2", "my_list_3"];
println!("Setting key: {key}, value: {:?}", list);
let ttl = CollectionTtl::default();
match cache_client
.list_set(&cache_name, key.clone(), list.clone(), ttl)
.await
{
Ok(res) => {
println!("{:?}", res);
}
Err(err) => {
eprintln!("{err}");
}
};
// List の取得
match cache_client.list_fetch(&cache_name, key.clone()).await {
Ok(r) => match r {
Some(entry) => {
let values: Vec<String> = entry
.value()
.iter()
.map(|v| String::from_utf8(v.clone()).expect(""))
.collect();
println!("Got value: {:?}", values);
}
None => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
Ok(())
}
Set
Set というデータ型をキャッシュさせることができます。
セットとは、一意な要素の並び順のないコレクションのことで、それぞれが文字列形式になっている。
以下のコードでは、次のように出力されます。
Setting key: my_set, value: ["my_set_1", "my_set_2", "my_set_3", "my_set_3", "my_set_4", "my_set_5", "my_set_6", "my_set_6", "my_set_7", "my_set_8"]
()
Got value: ["my_set_2", "my_set_4", "my_set_6", "my_set_5", "my_set_3", "my_set_8", "my_set_7", "my_set_1"]
my_set_3
や my_set_6
の重複除去がされていることが分かります。
また、順番の保証がない点にも注意が必要です。
use momento::{CollectionTtl, CredentialProvider, MomentoError, SimpleCacheClientBuilder};
use std::process;
use std::time::Duration;
pub async fn set() -> Result<(), MomentoError> {
// SimpleCacheClient の生成
let mut cache_client = match SimpleCacheClientBuilder::new(
CredentialProvider::from_env_var("MOMENTO_AUTH_TOKEN".to_string())?,
Duration::from_secs(60),
) {
Ok(client) => client,
Err(err) => {
eprintln!("{err}");
process::exit(1);
}
}
.build();
let cache_name = std::env::var("MOMENTO_CACHE_NAME").expect("Cache name must be set!");
// Set のセット
let key = "my_set".to_string();
let sets = vec![
"my_set_1", "my_set_2", "my_set_3", "my_set_3", "my_set_4", "my_set_5", "my_set_6",
"my_set_6", "my_set_7", "my_set_8"
];
println!("Setting key: {key}, value: {:?}", sets);
let ttl = CollectionTtl::default();
match cache_client
.set_add_elements(&cache_name, key.clone(), sets.clone(), ttl)
.await
{
Ok(res) => {
println!("{:?}", res);
}
Err(err) => {
eprintln!("{err}");
}
};
// Set の取得
match cache_client.set_fetch(&cache_name, key.clone()).await {
Ok(r) => match r.value {
Some(hs) => {
let values: Vec<String> = hs
.into_iter()
.map(|v| String::from_utf8(v.clone()).expect(""))
.collect();
println!("Got value: {:?}", values);
}
None => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
Ok(())
}
Sorted Set
Sorted set というデータ型をキャッシュさせることができます。
Sorted setsは、値(文字列)とスコア(符号付きダブル64ビットフロート)のペアを持つユニークな要素のコレクションです。項目の要素はスコア値順に並べられます。
Set の順番を保証できるようにしたデータ型です。
sorted_set_fetch_by_index
は取得するインデックスの範囲を指定でき、sorted_set_fetch_by_score
は取得するスコアの範囲のを指定できます。
以下のコードでは、次のように出力されます。
Got value: ["my_sorted_set_50", "my_sorted_set_100"]
Got value: ["my_sorted_set_100", "my_sorted_set_200"]
use momento::response::SortedSetFetch;
use momento::sorted_set::{Order, SortedSetElement};
use momento::{CollectionTtl, CredentialProvider, MomentoError, SimpleCacheClientBuilder};
use std::process;
use std::time::Duration;
pub async fn sorted_set() -> Result<(), MomentoError> {
// SimpleCacheClient の生成
let mut cache_client = match SimpleCacheClientBuilder::new(
CredentialProvider::from_env_var("MOMENTO_AUTH_TOKEN".to_string())?,
Duration::from_secs(60),
) {
Ok(client) => client,
Err(err) => {
eprintln!("{err}");
process::exit(1);
}
}
.build();
let cache_name = std::env::var("MOMENTO_CACHE_NAME").expect("Cache name must be set!");
// Sorted set のセット
let key = "my_sorted_set".to_string();
let sorted_sets = vec![
SortedSetElement {
value: "my_sorted_set_200".into(),
score: 200.0,
},
SortedSetElement {
value: "my_sorted_set_100".into(),
score: 100.0,
},
SortedSetElement {
value: "my_sorted_set_50".into(),
score: 50.0,
},
SortedSetElement {
value: "my_sorted_set_1".into(),
score: 1.0,
},
];
println!("Setting key: {key}, value: {:?}", sorted_sets);
let ttl = CollectionTtl::default();
match cache_client
.sorted_set_put(&cache_name, key.clone(), sorted_sets.clone(), ttl)
.await
{
Ok(res) => {
println!("{:?}", res);
}
Err(err) => {
eprintln!("{err}");
}
};
// Sorted set のインデックスが 1 以上 2 以下までのデータを取得
match cache_client
.sorted_set_fetch_by_index(&cache_name, key.clone(), Order::Ascending, 1..=2)
.await
{
Ok(r) => match r {
SortedSetFetch::Hit { elements } => {
let values: Vec<String> = elements
.into_iter()
.map(|v| String::from_utf8(v.value).expect(""))
.collect();
println!("Got value: {:?}", values);
}
SortedSetFetch::Miss => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
// Sorted set のスコアが 100 以上 200 以下までのデータを取得
match cache_client
.sorted_set_fetch_by_score(&cache_name, key.clone(), Order::Ascending, 100.0..=200.0)
.await
{
Ok(r) => match r {
SortedSetFetch::Hit { elements } => {
let values: Vec<String> = elements
.into_iter()
.map(|v| String::from_utf8(v.value).expect(""))
.collect();
println!("Got value: {:?}", values);
}
SortedSetFetch::Miss => {
println!("Cache miss!");
}
},
Err(err) => {
eprintln!("{err}");
}
};
Ok(())
}
まとめ
気になっているサービスである Momento を Rust で検証してみました。
シンプルで非常に扱いやすいです!!
あと、キャラクターが可愛い🐿️🐿️🐿️
ソースコードを Github で公開しました。
Discussion