Momento Cache 4つのコレクションデータ その2 List
今日は前回の記事に引き続いて2つめのコレクションデータ側であるListをやっていきます。
前回の記事はこちらです。
List とは
順序付けられた要素のコレクションで、各要素が挿入された順序でソートです。前回のDictionaryでは順番は保証されておらず、時と場合よって順番はばらばらにアイテムの中に格納されているField
が出力されていましたが、Listは順番を挿入された順で維持するようです。
さっそくやってみる
まずtest.js
を以下に変更します。
// Declare the Momento SDK library
const {
Configurations,
CacheClient, CredentialProvider,
CacheListPushFront
} = require('@gomomento/sdk');
// Declate the dotenv library
const dotenv = require('dotenv');
// Run the config function to bring in the .env file
dotenv.config();
// Creates the Momento cache client object
async function createCacheClient() {
return await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: 'MOMENTO_API_KEY',
}),
defaultTtlSeconds: 600,
});
}
// A simple function that calls all functions in order. You probably want more error handling.
async function run() {
const cacheClient = await createCacheClient();
await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['a', 'b', 'c']);
const result = await cacheClient.listPushFront('demo-cache', 'test-list', 'x');
if (result instanceof CacheListPushFront.Success) {
console.log("Value 'x' added successfully to front of list 'test-list'");
} else if (result instanceof CacheListPushFront.Error) {
throw new Error(
`An error occurred while attempting to call cacheListPushFront on list 'test-list' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
);
}
}
run();
このサンプルでは
await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['a', 'b', 'c']);
でdemo-cache
というキャッシュ空間にtest-list
というキー名でアイテムを作成し、その値が['a', 'b', 'c']
となっています。
その後cacheClient.listPushFront
関数で先頭にx
を追加しています。
const result = await cacheClient.listPushFront('demo-cache', 'test-list', 'x');
反対にリストの末尾にデータを追記するのは
const result = await cacheClient.listPushBack('demo-cache', 'test-list', 'y');
です。
const result = await cacheClient.listRemoveValue('demo-cache', 'test-list', 'b');
で特定の値を持つ要素をListアイテムから削除させることもできます。
先頭の要素を取り出すのは以下です。
const result = await cacheClient.listPopBack('demo-cache', 'test-list');
const {
Configurations,
CacheClient, CredentialProvider,
CacheListPushFront,
CacheListPopFront
} = require('@gomomento/sdk');
<snip>
await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['a', 'b', 'c']);
const result = await cacheClient.listPopFront('demo-cache', 'test-list');
if (result instanceof CacheListPopFront.Hit) {
console.log(`First value was removed successfully from list 'test-list': ${result.valueString()}`);
} else if (result instanceof CacheListPopFront.Miss) {
console.log("List 'test-list' was not found in cache 'test-cache'");
} else if (result instanceof CacheListPopFront.Error) {
throw new Error(
`An error occurred while attempting to call cacheListPopFront on list 'test-list' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
);
}
<snip>
First value was removed successfully from list 'test-list': a
同様にListPopBack
では最後の要素を取り出すことが出来ます。
まとめて要素をアイテムに書き込むことも可能です。
const {
Configurations,
CacheClient, CredentialProvider,
CacheListPushFront,
CacheListPopFront,
CacheListConcatenateFront
} = require('@gomomento/sdk');
<snip>
await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['a', 'b', 'c']);
const result = await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['x', 'y', 'z']);
if (result instanceof CacheListConcatenateFront.Success) {
console.log(`Values added successfully to the front of the list 'test-list'. Result - ${result.toString()}`);
} else if (result instanceof CacheListConcatenateFront.Error) {
throw new Error(
`An error occurred while attempting to call cacheListConcatenateFront on list 'test-list' in cache 'test-cache': ${result.errorCode()}: ${result.toString()}`
);
}
<snip>
x,y,z,a,b,c
になります。
要素を専用ではなく末尾に追記する場合はListConcatenateBack
を使えます。
Dictionary と どちらが早いのか?
前述の通りDictionaryは順番を維持しませんが、Listは挿入順を維持します。どちらの方が高速に動作するのか、気になったのでやってみました。
まずはListで100要素書き込みを行ってみます。
// Declare the Momento SDK library
const {
Configurations,
CacheClient, CredentialProvider,
CacheListPushFront
} = require('@gomomento/sdk');
// Declate the dotenv library
const dotenv = require('dotenv');
// Run the config function to bring in the .env file
dotenv.config();
// Creates the Momento cache client object
async function createCacheClient() {
return await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: 'MOMENTO_API_KEY',
}),
defaultTtlSeconds: 600,
});
}
// A simple function that calls all functions in order. You probably want more error handling.
async function run() {
const cacheClient = await createCacheClient();
cacheClient.delete('demo-cache', 'test-dictionary');
await cacheClient.listConcatenateFront('demo-cache', 'test-list', ['a', 'b', 'c']);
let start = performance.now();
const loopTime = 100;
for (let i = 0; i < loopTime; i++) {
const result = await cacheClient.listPushBack('demo-cache', 'test-list', 'x');
}
let end = performance.now();
let duration = end - start;
console.log('実行時間 = ' + duration + 'ミリ秒');
}
run();
1アイテムあたり8ms程度でした。なお標準系アイテムの読み書きは約2~4msでしたので少し時間はかかるそうですが、これは自然で当たり前のことに思えます。
また、listConcatenateFront
の代わりにlistConcatenateBack
を使っても同じでしたし、 listPopFront
やlistPopBack
でも同じでした、読み書きでパフォーマンスが同じなのはキャッシュのMomentoならではでしょう。
では次にDictionaryも同様に100回書き込みを行います。
// Declare the Momento SDK library
const {
Configurations,
CacheClient, CredentialProvider,
CacheListPushFront
} = require('@gomomento/sdk');
// Declate the dotenv library
const dotenv = require('dotenv');
// Run the config function to bring in the .env file
dotenv.config();
// Creates the Momento cache client object
async function createCacheClient() {
return await CacheClient.create({
configuration: Configurations.Laptop.v1(),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: 'MOMENTO_API_KEY',
}),
defaultTtlSeconds: 600,
});
}
// A simple function that calls all functions in order. You probably want more error handling.
async function run() {
const cacheClient = await createCacheClient();
await cacheClient.delete('demo-cache', 'test-dictionary');
await cacheClient.dictionarySetField('demo-cache', 'test-dictionary', 'test-field', 'test-value');
let start = performance.now();
const loopTime = 100;
for (let i = 0; i < loopTime; i++) {
await cacheClient.dictionarySetField('demo-cache', 'test-dictionary', i, i);
}
let end = performance.now();
let duration = end - start;
console.log('実行時間 = ' + duration + 'ミリ秒');
}
run();
少し不思議なことが起きました。実際の書きこみは9ms程度と、ほとんどListと同様ですが、10回に1回程度2~3倍のレイテンシが発生します。これは技術特性としてDictionary利用時に抑えておいた方がよさそうです。
では読み込みはどうでしょうか。こちらも9ms程度と少しだけListより時間がかかりながら、同様にたまに長いレイテンシが発生しています。
結論と使い分け
List型は順番を維持する必要があるためアプリケーション側の考慮が必要です。Dictionary型は順番を意識する必要がないため雑多なデータストアとして利用可能です。開発上便利なのはDictionary型ですが、アプリケーションで考慮しないといけない分、少しだけList型が高速と言えます。
Discussion