|
Richtlinie für den Ablauf von Cache-Einträgen
|
Beschreibung
|
|---|---|
|
Nie
|
Der Cache-Eintrag wird nie entfernt, basierend auf der Zeit, die er im Cache verbracht hat. Er kann jedoch weiterhin entfernt werden, wenn der Cache die maximale Konfiguration erreicht, oder mithilfe des DeleteEntry-Diensts. Die Einstellung Ablaufzeit des Zwischenspeicher-Eintrags (Sekunden) wird ignoriert, wenn die Never-Ablaufrichtlinie konfiguriert ist.
|
|
Ablaufzeit seit letztem Zugriff
|
Der Eintrag wird entfernt, wenn er nicht innerhalb der angegebenen Ablaufzeit gelesen wurde.
|
|
Ablaufzeit seit Erstellung
|
Der Eintrag wird entfernt, nachdem die angegebene Zeit seit seiner Erstellung vergangen ist.
|
|
Ablaufzeit seit der letzten Änderung
|
Der Eintrag wird entfernt, wenn er nicht innerhalb der angegebenen Ablaufzeit geändert wurde.
|
|
Ablaufzeit seit der letzten Berührung
|
Der Eintrag wird entfernt, wenn er nicht innerhalb der angegebenen Ablaufzeit aufgerufen oder geändert wurde.
|
|
Dienstname
|
Beschreibung
|
|---|---|
|
PutEntry
|
• Fügt dem Cache einen Eintrag hinzu.
• Eingabewert – Einzeilige Infotable, die den konfigurierten DataShape des CacheThing verwendet.
|
|
GetEntry
|
• Gibt ein einzeiliges Infotable-Ergebnis aus dem Cache zurück.
• Wenn der Eintrag nicht gefunden wird (ein Cache-Fehler) und der Cache nicht mit Cache Performs Loading of Missing Entries konfiguriert ist, wird eine leere Infotable zurückgegeben. Wenn der Cache mit dieser Option konfiguriert ist, wird der LoadEntry-Dienst automatisch aufgerufen, und das Ergebnis wird zurückgegeben.
• Cache-Fehler können auftreten, wenn der Eintrag nie zum Cache hinzugefügt wurde, entfernt wurde oder abgelaufen ist.
• Eingabewert – Einzeilige Infotable, die den konfigurierten DataShape des CacheThing verwendet. Nur die Werte von Primary Key sollten ausgefüllt werden.
|
|
GetEntryByKey
|
• Gibt ein einzeiliges Infotable-Ergebnis aus dem Cache zurück.
• Wenn der Eintrag nicht gefunden wird, wird eine leere Infotable zurückgegeben. Cache-Fehler können auftreten, wenn der Eintrag nie zum Cache hinzugefügt wurde, entfernt wurde oder abgelaufen ist.
• Eingabewert – Zeichenfolge, die den primären Schlüssel des abgerufenen Eintrags wiedergibt. Dieser Dienst ist nur verfügbar, wenn der DataShape über einen einzelnen primären Schlüssel verfügt. Dies funktioniert am besten mit primären Zeichenfolgenschlüsseln, unterstützt aber auch die meisten Typen mit einer eindeutigen Zeichenfolgenkonvertierung.
|
|
LoadEntry
|
• Wird verwendet, um Cache-Daten automatisch aus der Datenquelle abzurufen, wenn ein Cache-Fehler auftritt.
• Dieser Dienst muss überschrieben werden, wenn Cache Performs Loading of Missing Entries aktiviert ist.
|
|
DeleteEntry
|
• Löscht einen Eintrag aus dem Zwischenspeicher.
• In einer Hochverfügbarkeitsumgebung wirkt sich dies nur auf den Knoten aus, auf dem der DeleteEntry-Dienst aufgerufen wird.
• Eingabewert – Einzeilige Infotable, die den konfigurierten DataShape des CacheThing verwendet. Nur die Werte von Primary Key sollten ausgefüllt werden.
|
|
DeleteEntryByKey
|
• Löscht einen Eintrag aus dem Zwischenspeicher.
• In einer Hochverfügbarkeitsumgebung wirkt sich dies nur auf den Knoten aus, auf dem der DeleteEntryByKey-Dienst aufgerufen wird.
• Eingabewert – Zeichenfolge, die den primären Schlüssel des abgerufenen Eintrags wiedergibt. Dieser Dienst ist nur verfügbar, wenn der DataShape über einen einzelnen primären Schlüssel verfügt.
|
|
PurgeCache
|
• Entfernt alle Cache-Einträge.
• In einer Hochverfügbarkeitsumgebung wirkt sich dies nur auf den Knoten aus, auf dem der PurgeCache-Dienst aufgerufen wird.
|
|
GetDataShape
|
Gibt den konfigurierten DataShape des Cache zurück.
|
|
SetDataShape
|
Legt das DataShape-Feld in der Konfigurationstabelle des Cache fest. Wenn Sie diesen Wert ändern, wird der Cache automatisch bereinigt.
|
|
GetEstimatedEntryCount
|
Ruft die geschätzte Anzahl von Einträgen im Cache ab. Fortlaufende PUT- und DELETE-Operationen sowie Ablauf- oder Entfernungsprozesse können sich auf die Genauigkeit auswirken.
|
|
EstimateEntrySize
|
• Gibt die geschätzte Größe eines Eintrags zurück, wenn er dem Cache hinzugefügt wird. Dadurch wird der Eintrag nicht in den Cache gestellt, und es werden keine bereits im Cache enthaltenen Einträge benötigt. Dieser Dienst kann nützlich sein, um eine angemessene Maximale Cache-Größe (MB) zu schätzen oder um zu verhindern, dass große Einträge zum Cache hinzugefügt werden.
• Eingabewert – Einzeilige Infotable, die den konfigurierten DataShape des CacheThing verwendet.
|
|
Cache-Metriken
|
Beschreibung
|
|---|---|
|
thingworx_cache_hit_rate
|
Anteil der Cache-Anforderungen, die dazu geführt haben, dass ein Eintrag im Cache gefunden wurde
|
|
thingworx_cache_hits
|
Häufigkeit, mit der Cache-Lookup-Methoden einen zwischengespeicherten Wert zurückgegeben haben
|
|
thingworx_cache_request_count
|
Häufigkeit, mit der Cache-Lookup-Methoden entweder einen zwischengespeicherten oder nicht zwischengespeicherten Wert zurückgegeben haben
|
|
thingworx_cache_miss_rate
|
Anteil der Cache-Lookup-Methoden, die einen nicht zwischengespeicherten (neu geladenen) Wert oder Null zurückgegeben haben
|
|
thingworx_cache_misses
|
Häufigkeit, mit der Cache-Lookup-Methoden einen nicht zwischengespeicherten (neu geladenen) Wert oder Null zurückgegeben haben
|
|
thingworx_cache_eviction_count
|
Häufigkeit, mit der ein Eintrag entfernt wurde
|
|
thingworx_cache_average_load_penalty
|
Durchschnittliche Zeit, die für das Laden neuer Werte aufgewendet wurde
|
|
thingworx_cache_weighted_size
|
Ungefähre aktuelle Größe des Cache in Byte
|
|
thingworx_cache_max_weight
|
Maximale Größe des Cache in Byte vor der Entfernung
|
|
thingworx_cache_estimated_entry_count
|
Geschätzte Anzahl der Einträge im Cache
|
|
thingworx_cache_global_max_size
|
Globale, maximal konfigurierte Größe für alle Caches in Byte
|
// 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;
Dieser Dienst füllt automatisch die Datentabelle im abgefragten Datumsbereich mit Demodaten aus. |
/* 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);
}