|
快取項目到期原則
|
描述
|
|---|---|
|
「從不」
|
系統從不根據快取項目在快取中花費的時間量來移除快取項目。但是,仍可在快取達到最大大小組態的情況下或使用 DeleteEntry 服務來收回。配置 Never 到期原則時,會略過「快取項目到期時間 」設定。
|
|
「自上次存取以來的到期時間」
|
如果在指定到期時間內未讀取項目,則會將其移除。
|
|
「自建立以來的到期時間」
|
建立後,項目會在經過指定時間後移除。
|
|
「自上次修改後的到期時間」
|
如果在指定到期時間內未修改項目,則會將其移除。
|
|
「自上次接觸後的到期時間」
|
如果在指定到期時間內未存取或修改項目,則會將其移除。
|
|
服務名稱
|
描述
|
|---|---|
|
PutEntry
|
• 將項目新增至快取。
• 輸入值 - 使用 CacheThing 的已配置資料形式的一列資料負載。
|
|
GetEntry
|
• 從快取傳回一列資料負載結果。
• 如果找不到項目 (快取未命中),且未使用「快取執行未命中項目的載入」配置快取,則會傳回空資料負載。如果使用該選項配置快取,則會自動呼叫 LoadEntry 服務,並傳回其結果。
• 如果項目從未新增至快取、遭到收回或到期,則可能會發生快取未命中的情況。
• 輸入值 - 使用 CacheThing 的已配置資料形式的一列資料負載。只應填入 Primary Key 值。
|
|
GetEntryByKey
|
• 從快取傳回一列資料負載結果。
• 如果找不到項目,會傳回空資料負載。如果項目從未新增至快取、遭到收回或到期,則可能會發生快取未命中的情況。
• 輸入值 - 字串表示所擷取項目的主索引鍵。只有在資料形式具有單一主索引鍵時,此服務才可供使用。該服務最適合字串主索引鍵,但也支援具有明確字串轉換的大多數類型。
|
|
LoadEntry
|
• 用於在發生快取未命中時,自動從資料來源擷取快取資料。
• 如果啟用「快取執行未命中項目的載入」,則必須取代此服務。
|
|
DeleteEntry
|
• 從快取中刪除項目。
• 在高可用性環境中,這僅影響呼叫 DeleteEntry 服務的節點。
• 輸入值 - 使用 CacheThing 的已配置資料形式的一列資料負載。只應填入 Primary Key 值。
|
|
DeleteEntryByKey
|
• 從快取中刪除項目。
• 在高可用性環境中,這僅影響呼叫 DeleteEntryByKey 服務的節點。
• 輸入值 - 字串表示所擷取項目的主索引鍵。只有在資料形式具有單一主索引鍵時,此服務才可供使用。
|
|
PurgeCache
|
• 移除所有快取項目。
• 在高可用性環境中,這僅影響呼叫 PurgeCache 服務的節點。
|
|
GetDataShape
|
傳回快取的已配置資料形式。
|
|
SetDataShape
|
設定快取組態表中的「資料形式」欄位。變更此值將會自動清除快取。
|
|
GetEstimatedEntryCount
|
傳回快取中的預估項目數。正在進行的 PUT 和 DELETE 操作以及到期或收回流程可能會影響準確性。
|
|
EstimateEntrySize
|
• 如果項目已新增至快取,會傳回其預估大小。這樣並不會將項目放進快取中,也不需要在快取中已有任何項目。此服務可用於預估適當的「快取最大大小 (MB)」,或防止將大型項目新增至快取。
• 輸入值 - 使用 CacheThing 的已配置資料形式的一列資料負載。
|
|
快取指標
|
描述
|
|---|---|
|
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);
}