CacheThings
CacheThing bietet eine schnelle Schnittstelle im Arbeitsspeicher zum Speichern und Abrufen von Schlüsselwertdaten. Sie eignet sich hervorragend zum Zwischenspeichern von Ergebnissen aus teuren oder zeitaufwändigen Vorgängen, z.B. langsamen Abfragen.
Da CacheThing im Arbeitsspeicher ausgeführt wird, wird sein Inhalt gelöscht, wenn ThingWorx neu gestartet wird. In Hochverfügbarkeitsumgebungen (HA-Umgebungen) verwaltet jeder ThingWorx Knoten seinen eigenen unabhängigen Cache. Daher werden Cache-Einträge, die auf einem Knoten erstellt oder aktualisiert werden, nicht automatisch mit anderen Knoten geteilt. Dazu gehören Cache-Invalidierungsaufrufe wie PurgeCache, DeleteEntry und DeleteEntryByKey.
Sie können CacheThing zum Zwischenspeichern verschiedener Typen von Daten verwenden, u.a.:
Ergebnisse aufwendiger oder häufiger Abfragen von Datenbank-Dingen
Ergebnisse aufwendiger oder häufiger Abfragen von Wert-Streams oder Datentabellen
Ergebnisse langsamer Netzwerkaufrufe, z.B. Aufrufe mit ContentLoaderFunctions
Aufwendige berechnete Werte
Inhalt häufig verwendeter, kleiner Dateien aus Datei-Repositories
Die Verwendung von CacheThing kann die Leistung erheblich verbessern und die Ressourcenverwendung im Vergleich zum Zugriff auf externe Systeme wie Datenbanken oder Netzwerkressourcen reduzieren.
Verwendungsrichtlinien
CacheThing ist nicht als permanenter Datenspeicher vorgesehen. Alle in CacheThing zwischengespeicherten Daten sollten aus der ursprünglichen Quelle reproduzierbar sein.
Daten in CacheThing können entweder Abgelaufen oder Entfernt sein. Dienste, die CacheThing verwenden, sollten so konzipiert sein, dass sie Daten aus der ursprünglichen Quelle abrufen, wenn der Cache kein Ergebnis zurückgibt. Weitere Informationen finden Sie in den Beispielen.
Konfiguration
Jeder CacheThing muss mit einem DataShape konfiguriert werden, der über einen primären Schlüssel verfügt. Der primäre Schlüssel wird verwendet, um Einträge aus dem Cache abzurufen.
Zusammengesetzte primäre Schlüssel werden unterstützt, aber sie verhindern die Verwendung der unten aufgeführten zweckmäßigen ByKey-Dienste, der Ablaufrichtlinie und der globalen maximalen Größe.
Ablaufrichtlinie
Die Ablaufrichtlinie definiert die Time to live (TTL) für jeden Cache-Eintrag. Sie können die Ablaufzeit mit der Einstellung Ablaufzeit des Zwischenspeicher-Eintrags (Sekunden) konfigurieren. Nachdem ein Eintragsablauf abgelaufen ist, wird beim Abrufen des Werts ein leeres Ergebnis zurückgegeben, ähnlich wie wenn er aus dem Cache gelöscht wurde.
Unterstützte Ablaufrichtlinien:
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.
Weitere Informationen zur Ablaufrichtlinie für Schnittstellen finden Sie unter Interface ExpiryPolicy.
Maximale Cache-Größe (Entfernung)
Cache-Einträge werden automatisch entfernt (gelöscht), wenn der Cache die in der Einstellung Maximale Cache-Größe (MB) angegebene Größe erreicht hat. Der Entfernungsprozess entfernt die am seltensten verwendeten Einträge (LFU), um Platz für neue Einträge zu schaffen. Sobald ein Eintrag entfernt wurde, wird beim Abrufen des Eintrags ein leeres Ergebnis zurückgegeben, ähnlich wie nach dem Löschen.
Wenn ein großer Eintrag hinzugefügt wird, können mehrere kleinere Einträge entfernt werden, um ihn unterzubringen. Wenn Sie dies vermeiden möchten, verwenden Sie den EstimateEntrySize-Dienst zur Prüfung der Größe eines Eintrags, bevor er dem Cache hinzugefügt wird.
Cache-Entfernung und -Ablauf funktionieren unabhängig voneinander:
Die Entfernung basiert auf der Cache-Größe und der Verwendungshäufigkeit.
Der Ablauf basiert auf der Zeit, wie durch die konfigurierte Ablaufrichtlinie definiert.
Globale maximale Größe
Das Plattform-Untersystem enthält die Einstellung Maximale Größe, die zwischen allen Zwischenspeicher-Dingen pro ThingWorx Knoten geteilt wird (MB). Diese Einstellung begrenzt die kombinierte Größe aller CacheThings auf einem einzelnen Knoten, um eine übermäßige Speicherauslastung zu verhindern. Deaktivierte CacheThings sind in diesem Grenzwert nicht enthalten.
Diese Konfiguration verhindert die folgenden Aktionen:
Erstellen eines neuen CacheThing, das die maximale Größe überschreitet
Aktualisieren von Maximale Cache-Größe (MB) eines CacheThing, das die maximale Größe überschreitet
Importieren von CacheThings (entweder als Entitätsimporte oder als Teil einer Erweiterung), welche die maximale Größe überschreiten, wodurch der gesamte Import fehlschlägt
Reduzieren der maximalen Cache-Größe unter die aktuelle Maximale Cache-Größe (MB) aller CacheThings
Aktivieren eines CacheThing, das den globalen Grenzwert überschreitet
Das Ändern der Cache-Konfiguration
Bei jeder Änderung einer Cache-Konfiguration wird der Cache automatisch bereinigt, um die interne Konsistenz zu wahren. Durch Bearbeiten der folgenden Elemente wird der Cache bereinigt:
Data Shape – Umfasst das Ändern des konfigurierten DataShape, z.B. das Hinzufügen oder Entfernen von Feldern.
Richtlinie für den Ablauf von Cache-Einträgen
Ablaufzeit des Zwischenspeicher-Eintrags (Sekunden)
Maximale Cache-Größe (MB)
Cache Performs Loading of Missing Entries
Dienste
Die folgenden Dienste stehen für CacheThing zur Verfügung. In Hochverfügbarkeitsumgebungen (HA-Umgebungen) wirken sich diese Dienste auf den Cache auf dem ThingWorx Knoten aus, auf dem der Dienst ausgeführt wird.
Die nachfolgend aufgeführten praktischen *ByKey-Dienste sind nur verfügbar, wenn der DataShape des Cache über einen einzelnen primären Schlüssel verfügt. Wenn der DataShape des Cache mehrere primäre Schlüssel enthält, verwenden Sie die vollständige Infotable-Eingabeversion des Diensts.
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.
LoadEntry-Dienst überschreiben
Wenn Sie Cache Performs Loading of Missing Entries (Read-Through-Cache) aktivieren möchten, müssen Sie den LoadEntry-Dienst für CacheThing überschreiben. Dieser Dienst wird intern aufgerufen, wenn ein Cache-Fehler auftritt, damit der Cache Daten aus der Quelle abrufen und speichern kann.
Durch direktes Aufrufen des LoadEntry-Diensts werden keine Einträge zum Cache hinzugefügt. Einträge werden nur hinzugefügt, wenn LoadEntry während eines Cache-Fehlers intern von GetEntry aufgerufen wird.
Eingabe
Die Eingabe ist eine einzeilige Infotable, die den Parametern des entsprechenden GetEntry-Aufrufs entspricht. In der Regel werden nur die Felder des primären Schlüssels ausgefüllt.
Dienstimplementierung
Der LoadEntry-Dienst sollte den Wert, der zwischengespeichert werden soll, aus der Datenquelle abrufen oder generieren. Dies kann Datenbankabfragen, Webdienstaufrufe oder generierte Werte einschließen. Vermeiden Sie es, den Anwendungsstatus, wie Eigenschaften oder Dateien, innerhalb des Diensts zu aktualisieren.
Rufen Sie keine anderen Caching-Dienste wie GetEntry oder PutEntry aus LoadEntry auf. Der Cache speichert den zurückgegebenen Wert nach einem Cache-Fehler automatisch.
Ausgabe
Die Ausgabe muss eine Infotable mit demselben DataShape wie die Eingabe sein. Füllen Sie die nicht auf den primären Schlüssel bezogenen Felder mit den abgerufenen Daten aus. Alle Ausnahmen werden an den aufrufenden GetEntry-Aufruf weitergegeben.
Es empfiehlt sich, eine neue Infotable für die Ausgabe-Ergebnis-Infotable zu erstellen. Es ist zwar möglich, die Eingabewerte-Infotable wiederzuverwenden und mit Daten zu füllen, dies kann jedoch aufgrund von Einschränkungen bei der Verarbeitung bestimmter Basistypen in Javascript durch Infotables zu unerwartetem Verhalten führen. Weitere Informationen finden Sie in den Beispielen unter Verwendung von LoadEntry.
Read-Through-Caching
Durch Aktivieren von Cache Performs Loading of Missing Entries wird das Read-Through-Caching aktiviert. In diesem Modus lädt der Cache fehlende Eintragswerte automatisch mithilfe des LoadEntry-Diensts für das CacheThing, wenn ein Cache-Fehler auftritt.
Zur Unterstützung dieses Verhalten müssen Sie den LoadEntry-Dienst für CacheThing überschreiben, um die Quelldaten abzufragen und den entsprechenden Wert zurückzugeben. Wenn der GetEntry-Dienst keinen Wert im Cache findet, ruft der Cache den LoadEntry-Dienst auf, um den Wert abzurufen.
Das Read-Through-Caching kann in Szenarien mit hoher Anzahl an Daten nützlich sein. Wenn mehrere parallele GetEntry-Anforderungen für denselben Schlüssel gestellt werden, bevor er gefüllt wird, wird der LoadEntry-Sicherungsdienst nur einmal aufgerufen. Nach Abschluss des LoadEntry-Aufrufs geben alle ausstehenden GetEntry-Anforderungen den abgerufenen Wert zurück.
Details zur Bereitstellung einer LoadEntry-Dienstimplementierung finden Sie im Abschnitt "LoadEntry-Dienst überschreiben" oben.
Metriken
Es stehen mehrere cachebezogene Metriken zur Verfügung. In Hochverfügbarkeitsumgebungen (HA) sind Caches für jeden Knoten unabhängig. Metriken sollten pro Knoten mithilfe der Beschriftungen platform und cache_name verarbeitet werden.
Die folgenden Cache-Metriken sind verfügbar:
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
Warnungen (verfügbar in von PTC gehosteten Cloud-Umgebungen)
Die folgenden Warnungen sind in von PTC gehosteten Cloud-Umgebungen verfügbar:
thingworxEntityCacheMissRate – Warnt, wenn die Fehlrate eines Cache für 1 Stunde 80 % überschreitet.
thingworxEntityCacheEvictions – Warnt, wenn die Entfernungsrate eines Cache für 30 Minuten 80 % überschreitet.
Beispiele und optimale Vorgehensweisen
Die Implementierung von Diensten, die CacheThing effektiv verwenden, erfordert Sorgfalt bei den Details. Beachten Sie die folgenden optimalen Vorgehensweisen:
Der Cache wird gelöscht, wenn der ThingWorx Knoten neu gestartet wird.
Einträge können entfernt werden oder laufen ohne Eingreifen des Benutzers aus dem Cache ab. Gehen Sie nicht davon aus, dass Einträge auf unbestimmte Zeit im Cache verbleiben.
Verwenden Sie Cache-Metriken, um zu validieren, ob die Cache-Größe angemessen ist.
Vermeiden Sie es, alle Daten unter einem einzigen Eintrag zu speichern oder sehr große Cache-Einträge zu erstellen.
In Hochverfügbarkeitsumgebungen (HA-Umgebungen) verfügt jeder Knoten über einen unabhängigen Cache. Das Hinzufügen, Löschen oder Bereinigen eines Cache auf einem Knoten wirkt sich nicht auf alle ThingWorx Knoten aus.
Wenn Cache Performs Loading of Missing Entries deaktiviert ist (Nicht-Read-Through-Cache):
1. Rufen Sie mit dem primären Schlüssel einen Eintrag aus dem Cache ab.
2. Prüfen Sie, ob das Ergebnis ausgefüllt ist (die zurückgegebene Infotable hat eine Zeile).
3. Wenn das Ergebnis gefunden wird, verwenden Sie es.
4. Wenn das Ergebnis nicht gefunden wird:
Rufen Sie den erforderlichen Eintrag aus den Quelldaten (z.B. Datenbank-Ding, Datentabelle oder ContentLoader) ab, oder generieren Sie ihn.
Fügen Sie den Eintrag dem Cache mit PutEntry hinzu.
Rufen Sie den neu zwischengespeicherten Eintrag auf.
Wenn Cache Performs Loading of Missing Entries aktiviert ist (Read-Through-Cache):
1. Überschreiben Sie den LoadEntry-Dienst auf dem CacheThing.
2. Rufen Sie mit dem primären Schlüssel einen Eintrag aus dem Cache ab.
Wenn der Eintrag nicht gefunden wird, wird automatisch LoadEntry aufgerufen, um den Wert auszufüllen.
Beispiel für die Verwendung des Diensts "TimesTwo" 
Der Dienst "TimesTwo" übernimmt eine Zahl, multipliziert sie mit 2 und speichert das Ergebnis in einem Cache. Führen Sie die folgenden Schritte aus, um den Dienst einzurichten:
1. Erstellen Sie einen DataShape (TimesTwoDataShape) mit den folgenden Feldern: operand: (LONG, primaryKey) und timesTwoValue: (LONG).
2. Erstellen Sie ein CacheThing (MyTimesTwoCache) mit dem konfigurierten TimesTwoDataShape.
3. Fügen Sie den Dienst TimesTwo zu MyTimesTwoCache hinzu:
Eingabe – x (eine LONG-Zahl)
Ausgabe – result (eine LONG-Zahl)
// 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;
}
Beispiel für Caching-Dienst-Wrapper 
In diesem Beispiel wird gezeigt, wie die Ergebnisse des QueryImplementingThingsV2-Diensts manuell zwischengespeichert werden.
1. Erstellen Sie einen DataShape mit dem Namen Cached_QueryImplementingThingsV2_DataShape und den folgenden Feldern:
maxItems: (NUMBER, primaryKey)
nameMask: (STRING, primaryKey)
query: (QUERY, primaryKey)
isSortFirst: (BOOLEAN, primaryKey)
result: (INFOTABLE, DataShape RootEntityListV2)
2. Erstellen Sie ein CacheThing namens Cached_QueryImplementingThingsV2, und weisen Sie Cached_QueryImplementingThingsV2_DataShape als dessen DataShape zu.
3. Konfigurieren Sie eine kurze ExpirationTime, und legen Sie für das Cached_ReadThrough_QueryImplementingThingsV2 eine ExpirationPolicy fest, die nicht Never ist, um das Ablaufverhalten des Cache zu beobachten.
4. Erstellen Sie einen Dienst namens CachingQueryImplementingThingsV2 mit den folgenden Parametern:
Eingaben
maxItems: (NUMBER, required)
nameMask: (STRING, required)
query: (QUERY, required)
isSortFirst: (BOOLEAN, required)
Ausgabe result: (INFOTABLE, DataShape RootEntityListV2)
/* 
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"
});
}
Beispiel: Dienst-Wrapper als Read-Through-Cache zwischenspeichern 
In diesem Beispiel wird veranschaulicht, wie die Ergebnisse des QueryImplementingThingsV2-Diensts mithilfe eines Read-Through-Cache automatisch zwischengespeichert werden. Nachdem Sie den LoadEntry-Dienst überschrieben haben, können Sie den GetEntry-Dienst verwenden, um QueryImplementingThingsV2-Abfrageergebnisse automatisch in den Cache zu laden.
1. Erstellen Sie einen DataShape mit dem Namen Cached_QueryImplementingThingsV2_DataShape und den folgenden Feldern:
maxItems: (NUMBER, primaryKey)
nameMask: (STRING, primaryKey)
query: (QUERY, primaryKey)
isSortFirst: (BOOLEAN, primaryKey)
result: (INFOTABLE, DataShape RootEntityListV2)
2. Erstellen Sie ein CacheThing mit dem Namen Cached_ReadThrough_QueryImplementingThingsV2. Weisen Sie Cached_QueryImplementingThingsV2_DataShape als dessen DataShape zu, und aktivieren Sie die Option Cache Performs Loading of Missing Entries.
3. Konfigurieren Sie eine kurze ExpirationTime, und legen Sie für das Cached_ReadThrough_QueryImplementingThingsV2 eine ExpirationPolicy fest, die nicht Never ist, um das Ablaufverhalten des Cache zu beobachten.
4. Wechseln Sie zur Registerkarte Dienste, und wählen Sie Überschreiben neben dem LoadEntry-Dienst mit dem folgenden Beispiel aus:
/* 
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;
Beispiel für die Verwendung von DataTableQuery 
In diesem Beispiel wird gezeigt, wie Sie einen funktionierenden BetweenDatesDataTableQuery-Dienst verwenden, um den größten Wert in einer Datentabelle zwischen zwei Daten zu suchen und die Ergebnisse in einem Cache zu speichern. Eindeutige Aufrufe zur anfänglichen CachingBetweenDatesDataTableQuery-Abfrage fragen die zugrunde liegende Datentabelle ab, aber nachfolgende identische Aufrufe verwenden stattdessen den Cache.
Führen Sie die folgenden Schritte aus, um den Dienst einzurichten:
1. Erstellen Sie einen DataShape (TimeValueDataShape) mit den folgenden Feldern: longKey: (LONG, primaryKey) und dateKey: (DATETIME, primaryKey).
2. Erstellen Sie eine Datentabelle (DemoDataTable) mit dem DataShape TimeValueDataShape.
3. Erstellen Sie einen DataShape (TimeSpanValueCacheDataShape) mit den folgenden Feldern: startDate: (DATETIME, primaryKey), endDate: (DATETIME, primaryKey) und longValue: (LONG).
4. Erstellen Sie ein CacheThing (CacheThingDemo) mit dem konfigurierten DataShape TimeSpanValueCacheDataShape .
5. Erstellen Sie die folgenden Dienste für CacheThingDemo:
CachingBetweenDatesDataTableQuery – Eingaben: StartDate und EndDate als DATETIME. Ausgaben: result als LONG
BetweenDatesDataTableQuery – Eingaben: StartDate und EndDate als DATETIME. Ausgaben: result als LONG
* 
Dieser Dienst füllt automatisch die Datentabelle im abgefragten Datumsbereich mit Demodaten aus.
6. Optional. Konfigurieren Sie eine kurze ExpirationTime mit einer anderen ExpirationPolicy als Never für die CacheThingDemo, um Einträge zu beobachten, die im Cache abgelaufen sind.
CachingBetweenDatesDataTableQuery
/* 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"
});
}
BetweenDatesDataTableQuery
/*  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);
}
War dies hilfreich?