|
キャッシュエントリの有効期限ポリシー
|
説明
|
|---|---|
|
なし
|
キャッシュエントリは、キャッシュ内での滞在時間の長さに基づいて除去されることはありません。ただし、キャッシュが最大サイズコンフィギュレーションに達したり、DeleteEntry サービスが使用されたりした場合に、追い出される可能性は残っています。Never 有効期限ポリシーが設定されている場合、「キャッシュエントリの有効期限 (秒)」設定は無視されます。
|
|
最後のアクセス以来の有効期限 (時間)
|
指定された有効期限内に読み取られなかったエントリは除去されます。
|
|
作成以来の有効期限 (時間)
|
作成されてからの経過時間が、指定された時間に達したエントリは除去されます。
|
|
最終修正後の有効期限 (時間)
|
指定された有効期限内に修正されなかったエントリは除去されます。
|
|
最後に使用して以来の有効期限 (時間)
|
指定された有効期限内にアクセスまたは修正されなかったエントリは除去されます。
|
|
サービス名
|
説明
|
|---|---|
|
PutEntry
|
• キャッシュにエントリを追加します。
• 入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。
|
|
GetEntry
|
• キャッシュから 1 行のインフォテーブル結果を返します。
• エントリが見つからず (キャッシュミス)、かつキャッシュで「Cache Performs Loading of Missing Entries」が設定されていない場合は、空のインフォテーブルが返されます。キャッシュでそのオプションが設定されている場合は、LoadEntry サービスが自動的に呼び出され、その結果が返されます。
• キャッシュミスは、エントリがキャッシュに追加されていない場合、追い出された場合、または期限切れになった場合に発生する可能性があります。
• 入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。Primary Key 値のみを指定する必要があります。
|
|
GetEntryByKey
|
• キャッシュから 1 行のインフォテーブル結果を返します。
• エントリが見つからない場合は、空のインフォテーブルが返されます。キャッシュミスは、エントリがキャッシュに追加されていない場合、追い出された場合、または期限切れになった場合に発生する可能性があります。
• 入力値 - 取得するエントリのプライマリキーを表す文字列。このサービスは、データシェイプが単一のプライマリキーを持つ場合にのみ使用できます。これは文字列のプライマリキーで最適に機能しますが、明確な文字列変換に対応したほとんどのタイプもサポートしています。
|
|
LoadEntry
|
• キャッシュミスが発生した場合に、データソースからキャッシュデータを自動的にフェッチするために使用されます。
• 「Cache Performs Loading of Missing Entries」が有効になっている場合、このサービスをオーバーライドする必要があります。
|
|
DeleteEntry
|
• キャッシュからエントリを削除します。
• 高可用性環境では、これは DeleteEntry サービスが呼び出されたノードにのみ影響します。
• 入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。Primary Key 値のみを指定する必要があります。
|
|
DeleteEntryByKey
|
• キャッシュからエントリを削除します。
• 高可用性環境では、これは DeleteEntryByKey サービスが呼び出されたノードにのみ影響します。
• 入力値 - 取得するエントリのプライマリキーを表す文字列。このサービスは、データシェイプが単一のプライマリキーを持つ場合にのみ使用できます。
|
|
PurgeCache
|
• すべてのキャッシュエントリを除去します。
• 高可用性環境では、これは PurgeCache サービスが呼び出されたノードにのみ影響します。
|
|
GetDataShape
|
キャッシュの設定済みデータシェイプを返します。
|
|
SetDataShape
|
キャッシュのコンフィギュレーションテーブルのデータシェイプフィールドを設定します。この値を変更すると、キャッシュが自動的にパージされます。
|
|
GetEstimatedEntryCount
|
キャッシュ内の推定エントリ数を返します。進行中の PUT 操作や DELETE 操作、および有効期限プロセスや追い出しプロセスが精度に影響を及ぼす可能性があります。
|
|
EstimateEntrySize
|
• エントリがキャッシュに追加された場合の推定サイズを返します。これによってエントリがキャッシュに追加されるわけではなく、キャッシュ内にすでにエントリが存在している必要もありません。このサービスは、適切な「キャッシュ最大サイズ (MB)」を推定したり、大きなエントリがキャッシュに追加されるのを回避したりするのに役立ちます。
• 入力値 - CacheThing の設定済みデータシェイプを使用した 1 行のインフォテーブル。
|
|
キャッシュ判定基準
|
説明
|
|---|---|
|
thingworx_cache_hit_rate
|
キャッシュリクエストのうち、キャッシュ内でエントリが見つかったリクエストの割合
|
|
thingworx_cache_hits
|
キャッシュ検索メソッドがキャッシュされた値を返した回数
|
|
thingworx_cache_request_count
|
キャッシュ検索メソッドがキャッシュされた値またはキャッシュされていない値を返した回数
|
|
thingworx_cache_miss_rate
|
キャッシュされていない (新たにロードされた) 値または Null を返したキャッシュ検索メソッドの割合
|
|
thingworx_cache_misses
|
キャッシュ検索メソッドがキャッシュされていない (新しくロードされた) 値または Null を返した回数
|
|
thingworx_cache_eviction_count
|
エントリが追い出された回数
|
|
thingworx_cache_average_load_penalty
|
新しい値のロードにかかった平均時間
|
|
thingworx_cache_weighted_size
|
現在のキャッシュのおおよそのサイズ (バイト単位)
|
|
thingworx_cache_max_weight
|
追い出し前のキャッシュの最大サイズ (バイト単位)
|
|
thingworx_cache_estimated_entry_count
|
キャッシュ内の推定エントリ数
|
|
thingworx_cache_global_max_size
|
すべてのキャッシュに対して設定されているグローバル最大サイズ (バイト単位)
|
// Input is x as a LONG
// Output is result as LONG
// MyTimesTwoCache CacheThing is configured with a DataShape with two fields:
// operand: (LONG, primaryKey)
// timesTwoValue: (LONG)
result = -1;
// Check the cache first, to see if we have already calculated x*2
// GetEntryByKey is a convenience method that takes the String value the configured DataShape's Primary Key ("key" in this example)
// cacheResult is an InfoTable with the DataShape of the cache
let cacheResult = Things["MyTimesTwoCache"].GetEntryByKey({ operand: x.toString() });
if (cacheResult.getRowCount() === 0) {
// Cache didn't have a result, so calculate it
let timesTwo = x*2;
// Put the result in the cache for the next time we need this multiple
// operand and value are from the Cache's configured DataShape
let cacheInput = getInfoTableForPut(x, timesTwo);
Things["MyTimesTwoCache"].PutEntry({
values: cacheInput
});
// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("PutEntry the following entry into the MyTimesTwoCache: {operand: " + x + ", timesTwoValue: " + timesTwo + "}");
// Set the global result Output variable
result = timesTwo;
} else {
// If results are found, they are always in the first row of the InfoTable
let row = cacheResult.getRow(0);
logger.debug("Found the following entry already in MyTimesTwoCache: {operand: " + row.operand + ", timesTwoValue: " + row.timesTwoValue + "}");
result = row.timesTwoValue;
}
function getInfoTableForPut(operand, timesTwoValue) {
let infoTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: Things["MyTimesTwoCache"].GetDataShape()
});
infoTable.AddRow({"operand": operand, "timesTwoValue ": timesTwoValue });
return infoTable;
}
/*
This Service queries QueryImplementingThingsV2 and caches the results as needed
Service Name: CachingQueryImplementingThingsV2 (Overridden on CacheThing)
Input: values InfoTable with Cached_QueryImplementingThingsV2_DataShape
Cached_QueryImplementingThingsV2_DataShape:
maxItems: NUMBER, Primary Key
nameMask: STRING, Primary Key
query: QUERY, Primary Key
isSortFirst: BOOLEAN, Primary Key
result: INFOTABLE, DataShape RootEntityListV2 (NOT a Primary Key!)
*/
let cacheEntryInfoTable = getCacheEntryInfoTable();
cacheEntryInfoTable.AddRow({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */
});
let getEntryResult = me.GetEntry({values: cacheEntryInfoTable});
if (getEntryResult.getRowCount() === 0) {
// Cache didn't have results, so call QueryImplementingThingsV2
let queryResult = ThingTemplates["GenericThing"].QueryImplementingThingsV2({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */
});
// Add the result to the cache, with the primaryKey parameters used to uniquely identify the query.
let newCacheEntryInfoTable = getCacheEntryInfoTable();
newCacheEntryInfoTable.AddRow({
maxItems: maxItems /* NUMBER */,
nameMask: nameMask /* STRING */,
query: query /* QUERY */,
isSortFirst: isSortFirst /* BOOLEAN */,
result: queryResult /* INFOTABLE */
});
me.PutEntry({
values: newCacheEntryInfoTable
});
logger.debug("Entry not found in Cache, retrieved from QueryImplementingThingsV2:" + queryResult.ToJSON());
result = queryResult;
} else {
logger.debug("Found entry from Cache:" + getEntryResult.ToJSON());
let row = getEntryResult.getRow(0);
result = row.result;
}
function getCacheEntryInfoTable() {
return Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "Cached_QueryImplementingThingsV2_DataShape"
});
}
/*
This Service simply proxies a query to QueryImplementingThingsV2 for Read Through caching
Service Name: LoadEntry (Overridden on CacheThing)
Input: values InfoTable with Cached_QueryImplementingThingsV2_DataShape
Cached_QueryImplementingThingsV2_DataShape:
maxItems: NUMBER, Primary Key
nameMask: STRING, Primary Key
query: QUERY, Primary Key
isSortFirst: BOOLEAN, Primary Key
result: INFOTABLE, DataShape RootEntityListV2 (NOT a Primary Key!)
*/
let queryResult = ThingTemplates["GenericThing"].QueryImplementingThingsV2({
maxItems: values.maxItems /* NUMBER */,
nameMask: values.nameMask /* STRING */,
query: values.query /* QUERY */,
isSortFirst: values.isSortFirst /* BOOLEAN */
});
// Copy the results into a new InfoTable.
// This prevents some internal ThingWorx casting issues with some InfoTables
let resultTable = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: me.GetDataShape() // Returns Cached_QueryImplementingThingsV2_DataShape
});
resultTable.AddRow({
maxItems: values.maxItems /* NUMBER {"defaultValue":500} */,
nameMask: values.nameMask /* STRING */,
query: values.query /* QUERY */,
isSortFirst: values.isSortFirst /* BOOLEAN */,
result: queryResult
});
let result = resultTable;
このサービスは、クエリー対象の日付範囲のデータテーブルに、デモデータを自動的に取り込みます。 |
/* Reads the Cached results of BetweenDatesDataTableQuery Service.
If Cache doesn't have the result, call BetweenDatesDataTableQuery and load results into Cache.
Service Name: CachingBetweenDatesDataTableQuery
Inputs: StartDate[DATETIME, required], EndDate[DATETIME, required]
Output: LONG
*/
// This example uses CacheThing.GetEntry, which requires a one-row infoTable populated with the CacheThing's DataShapes primaryKey getEntryByKey
let cacheEntryInfoTable = getCacheEntryInfoTable();
cacheEntryInfoTable.AddRow({"startDate": StartDate, "endDate": EndDate});
let getEntryResult = Things["CacheThingDemo"].GetEntry({
values: cacheEntryInfoTable
});
if (getEntryResult.getRowCount() === 0) {
// Cache didn't have results, so go to backing DataTable, by calling the BetweenDatesDataTableQuery Service
let queryResult = Things["CacheThingDemo"].BetweenDatesDataTableQuery({
StartDate: StartDate,
EndDate: EndDate
});
// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("PutEntry called for BetweenDatesQueryCache: {StartDate: " + StartDate + ", EndDate: " + EndDate + " value: " + queryResult + "}");
if (!queryResult || queryResult < 0) {
// BetweenDatesDataTableQuery should always populate with demo data if no data is in the StartDate - EndDate range
throw new Error("BetweenDatesDataTableQuery did not return results");
}
let newCacheEntryInfoTable = getCacheEntryInfoTable();
newCacheEntryInfoTable.AddRow({"startDate": StartDate, "endDate": EndDate, "longValue": queryResult});
Things["CacheThingDemo"].PutEntry({
values: newCacheEntryInfoTable
});
result = queryResult;
} else {
let row = getEntryResult.getRow(0);
logger.debug("Found the following entry already in Cache : {StartDate: " + row.startDate + ", EndDate: " + row.endDate + ", LongValue: " + row.longValue + "}");
result = row.longValue;
}
function getCacheEntryInfoTable() {
return Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "TimeSpanValueCacheDataShape"
});
}
/* This service queries a range of dates, based on the dateKey column, and returns the greatest longKey value found.
If there are no entries between StartDate and EndDate this Service dynamically populates demo data into demoDataTable, and returns that result.
Service Name: BetweenDatesDataTableQuery
Inputs: StartDate[DATETIME, required], EndDate[DATETIME, required]
Output: LONG
*/
if (StartDate >= EndDate) {
throw Error("StartDate must be before EndDate");
}
let dataTable = Things["DemoDataTable"];
let queryResult = queryGreatestLongKeyBetweenDates(dataTable, StartDate, EndDate);
if (queryResult.getRowCount() === 0) {
// If we don't get any results, first populate the date range with random values, then retry
// Turn ScriptLogger to debug mode to see the logger messages
logger.debug("No values found between [" + StartDate + "] and [" + EndDate + "], populating 100 demo values and re-querying");
populateDemoDataTable(dataTable, StartDate, EndDate);
// Re-query the now populated DateTable
queryResult = queryGreatestLongKeyBetweenDates(dataTable, StartDate, EndDate);
result = queryResult.getRow(0).get("longKey");
} else {
result = queryResult.getRow(0).get("longKey");
}
// Queries the DataTable for the largest longKey column value between the given dates
// Returns: One-row InfoTable with the found row.
function queryGreatestLongKeyBetweenDates(dateTable, startDate, endDate) {
let greatestBetweenDatesQuery = {
"filters": {
"type": "Between",
"fieldName": "dateKey",
"from": startDate,
"to": endDate
},
"sorts": [{
"fieldName": "longKey",
"isAscending": false
}]
};
// Run the Query to find the greatest Value Between Two Dates
let queryResult = dataTable.QueryDataTableEntries({
maxItems: 1,
query: greatestBetweenDatesQuery,
});
logger.debug("Queried between [" + StartDate + "] and [" + EndDate + "], found:" + queryResult.toJSON());
return queryResult;
}
function populateDemoDataTable(dataTable, startDate, endDate) {
// add 100 random entries between startDate and endDate
for (let entry = 0; entry < 100; entry++) {
let randomTimeBetween = randomDate(startDate, endDate);
let entry = Resources["InfoTableFunctions"].CreateInfoTableFromDataShape({
infoTableName: "InfoTable",
dataShapeName: "TimeValueDataShape"
});
entry.AddRow({
"longKey": Math.floor(Math.random() * 1000),
"dateKey": randomTimeBetween
});
Things["DemoDataTable"].AddDataTableEntry({
values: entry /* INFOTABLE */ ,
});
}
}
function randomDate(startDate, endDate) {
let randomTime = Math.random() * (endDate.getTime() - startDate.getTime()) + startDate.getTime();
return new Date(randomTime);
}