💎

それ、F#でやってみました - Microsoft Garnetの起動とアクセス

2024/04/08に公開

Microsoftが開発しているRedis互換の高速キャッシュストアGarnetのサーバ起動とクライアントでのアクセスをF#でやってみました。

環境は以下の通りです

  • .NET8.0
  • Garnet 1.0.4
  • git
  • Linux / Windows (バス区切りの記号などは適宜読み替えてください)

Garnetのビルド

Garnetのビルドは公式ドキュメント[1]を参考にして以下のコマンドで行いました。

git clone git@github.com:microsoft/garnet.git -b v1.0.4
cd garnet
dotnet restore
dotnet build -c Release

Getnetサーバの起動

Gatnetサーバをコマンドで起動するときは以下を実行しました。

(pwd: garnet)
cd main/GarnetServer
dotnet run -c Release -f net8.0

サーバが正常に起動すると以下が表示されます。停止はCtrl+Cでできました。

    _________
   /_||___||_\      Garnet 1.0.4 64 bit; standalone mode
   '. \   / .'      Port: 3278
     '.\ /.'        https://aka.ms/GetGarnet
       '.'

F#でのサーバの起動と停止は以下のようにしました。Nugetに接続できればGarnetServerクラスを生成してStartメソッドを実行するだけでした。

#r "nuget: Microsoft.Garnet"
open Garnet             // サーバ用

// サーバ起動
let server = new GarnetServer([||])
server.Start()

// ... クライアントで接続 ...

// サーバ停止、オブジェクト削除
server.Dispose()

Garnetクライアントによるサーバ接続

GarnetはRedis互換のため、redis-cliによる接続もできますが、F#からでもGarnetClientクラスでできます。なお、以下はC#で書かれたGarnetに含まれているサンプルプログラム[2]を参考にしていますが、awaitの部分はasync式やAsyncクラスなどは使用せず、Taskクラスのメソッドやプロパティを使用しています。

Garnetサーバへの接続はConnectAsync、切断はQuitAsyncの各メソッドで行いました。

#r "nuget: Microsoft.Garnet"
open Garnet.client      // クライアント用

// クライアント初期設定
let db = new GarnetClient("127.0.0.1", 3278)    // デフォルト設定
db.ConnectAsync().Wait()    // Garnetサーバに接続
printfn "Garnet Connected: %A" db.IsConnected   // 接続できたらtrue

// ... データの登録、削除など ...

db.QuitAsync().Wait()   // サーバから切断

Garnetに文字列を登録

文字列の登録はStringSetAsync、取得はStringGetAsyncで行いました。

db.StringSetAsync("key1", "MSGarnet").Wait()    // ~.Resultの値は正常ならtrue
printfn "key1 = %A" (db.StringGetAsync("key1").Result)

APIを実行

GarnetのAPIはExecuteForLongResultAsyncExecuteForStringResultAsyncメソッドで実行しました。戻る値によってメソッドが異なります。APIの引数は配列で設定しています。

printfn "EXISTS key1 = %A" (db.ExecuteForLongResultAsync("EXISTS", [| "key1" |]).Result)    // 1L
printfn "RENAME key1 newkey1: %A" (db.ExecuteForStringResultAsync("RENAME", [| "key1"; "newkey1" |]).Result)    // "OK"

データの削除

データの削除はKeyDeleteAsyncメソッドで行いました。

printfn "key1 delete"
db.KeyDeleteAsync("key1").Wait()    // ~.Resultの値は正常ならtrue

数値データの増減

数値データを1増やすときはStringIncrement、1減らすときはStringDecrementメソッドで行いました。

db.StringSetAsync("key2", "100").Wait()   // ~.Resultの値は正常ならtrue
printfn "key2 = %A" (db.StringGetAsync("key2").Result)

// 数値データの増減
printfn "key2 incr. = %A" (db.StringIncrement("key2").Result)
printfn "key2 decr. = %A" (db.StringDecrement("key2").Result)

ソース一覧

garnet_example.fsx
//
// F#でMicrosoft Garnetのサーバ起動とクライアントでのアクセス
// dotnet fsi garnet_example.fsx
//

#r "nuget: Microsoft.Garnet"
open System
open System.Text
open Garnet             // サーバ用
open Garnet.client      // クライアント用

// サーバ起動
let server = new GarnetServer([||])
server.Start()

// クライアント初期設定
let db = new GarnetClient("127.0.0.1", 3278)
// サーバに接続
printfn "client connect"
db.ConnectAsync().Wait()    // Garnetサーバに接続
printfn "Garnet Connected: %A" db.IsConnected   // 接続できたらtrue
// Ping
printfn "Ping: %A" (db.PingAsync().Result)

// 文字列データの登録と読み込み
printfn "----------"
printfn "key1 set"
db.StringSetAsync("key1", "MSGarnet").Wait()    // ~.Resultの値は正常ならtrue
printfn "key1 = %A" (db.StringGetAsync("key1").Result)

// データの存在を確認
printfn "EXISTS key1 = %A" (db.ExecuteForLongResultAsync("EXISTS", [| "key1" |]).Result)    // 1L
printfn "RENAME key1 newkey1: %A" (db.ExecuteForStringResultAsync("RENAME", [| "key1"; "newkey1" |]).Result)  // "OK"
printfn "RENAME newkey1 key1: %A" (db.ExecuteForStringResultAsync("RENAME", [| "newkey1"; "key1" |]).Result)  // "OK"

// データの削除
printfn "key1 delete"
db.KeyDeleteAsync("key1").Wait()    // ~.Resultの値は正常ならtrue
printfn "EXISTS key1 = %A" (db.ExecuteForLongResultAsync("EXISTS", [| "key1" |]).Result)    // 0L

// 数値データの登録と読み込み
printfn "----------"
printfn "key2 set"
db.StringSetAsync("key2", "100").Wait()   // ~.Resultの値は正常ならtrue
printfn "key2 = %A" (db.StringGetAsync("key2").Result)

// 数値データの増減
printfn "key2 incr. = %A" (db.StringIncrement("key2").Result)
printfn "key2 incr. = %A" (db.StringIncrement("key2").Result)
printfn "key2 decr. = %A" (db.StringDecrement("key2").Result)
printfn "key2 decr. = %A" (db.StringDecrement("key2").Result)

// データの削除
printfn "key2 delete"
db.KeyDeleteAsync("key2").Wait()    // ~.Resultの値は正常ならtrue

// System.Memoryによるデータの登録
printfn "----------"
printfn "key3 set"
let key = new Memory<byte>(Array.zeroCreate 4)
Encoding.UTF8.GetBytes("key3".AsSpan(), key.Span)

let value = new Memory<byte>(Array.zeroCreate 8)
Encoding.UTF8.GetBytes("MSGarnet".AsSpan(), value.Span)

db.StringSetAsync(key, value).Wait()

// System.Memoryによるデータの取得
let result = db.StringGetAsMemoryAsync(key).Result
printfn "%s = %s" (Encoding.UTF8.GetString(key.Span)) (Encoding.UTF8.GetString(result.Span))

// データの削除
printfn "key3 delete"
db.KeyDeleteAsync("key3").Wait()    // ~.Resultの値は正常ならtrue

// サーバから切断
printfn "----------"
printfn "client quit"
db.QuitAsync().Wait()

// サーバ停止、オブジェクト削除
server.Dispose()
結果
client connect
Garnet Connected: true
Ping: "PONG"
----------
key1 set
key1 = "MSGarnet"
EXISTS key1 = 1L
RENAME key1 newkey1: "OK"
RENAME newkey1 key1: "OK"
key1 delete
EXISTS key1 = 0L
----------
key2 set
key2 = "100"
key2 incr. = 101L
key2 incr. = 102L
key2 decr. = 101L
key2 decr. = 100L
key2 delete
----------
key3 set
key3 = MSGarnet
key3 delete
----------
client quit

GarnetのgithubリポジトリにはGarnetServerGarnetClient以外にもさまざまなクラスやメソッドがあるようです。いろいろと探ってみてください。


脚注
  1. Build and Test ↩︎

  2. garnet/samples/GarnetClientSample/GarnetClientSamples.cs ↩︎

Discussion