Working with Infotables
Infotables are stored in memory as a single object in the cache. For this reason, in ThingWorx 8.5 and later, direct manipulation of infotables is not supported since it can result in unexpected behaviors and performance issues. When you retrieve an infotable property from a Thing, a proxy object is created, which talks back to the original object. Direct changes will only occur to the local copy, but function calls will update the original object. If you need a full clone of the object so that no changes are proxied until all data is updated, use the resource’s clone option. In the examples below for single-row infotables, we use a wrapper object, but for multi-row infotables, we use the full clone option since we do not want to update every row change.
The following creates a local proxy object (method calls will update original object and save to cache):
Things["data1"] searches for a “data1” Thing by name.
Things["data1"].info the “info” infotable property on the “data1” Thing
var myinfo = Things["data1"].info
The following creates a full clone with no changes proxied back to the original object:
var myinfo = Resources["InfoTableFunctions"].Clone({ t1: Things["data1"].info });
Property data in ThingWorx 8.5 and later is treated differently than it was in previous releases. All data is now stored in a cache layer so that it can be used locally or remotely in a High Availability clustering environment. In previous environments, the stored data was directly modified, but in the cache layer, data has to be moved in and out of the cache as it is changed. In a clustering environment, the cache is distributed; therefore, additional network latency and wire serialization adds time to each request that is made.
Storing large quantities of data in a property as an infotable is now very expensive and can cause system crashes in clustered environments. The infotable is now fully read and written to the property when changed. In a clustered environment, the full object is also serialized, deserialized, and sent across the wire. You should move large infotables into the database, not store them in a single property. They can be converted to user-defined tables or placed in a data table.
In a clustered environment, Apache Ignite is used. Pushing large objects can cause Ignite to become non-responsive. Ignite queues up writes and backups, and when large values are pushed, it can take time to handle them and cause processing to back up. If the backup gets large, Ignite system threads will start to block, which can cause Ignite to shut down a node thinking it is non-responsive. The data may become more and more out of date.
To work well in a clustered environment, applications may have to be changed to reduce the size of the data being stored in properties. This is usually caused by infotables with thousands of rows.
Example: Sorting infotables
This example used to work before the caching layer was added. It used to directly update the table in memory.
var sort = new Object();
sort.name = yourFieldName;
sort.ascending = booleanValue;
me.yourInfoTable.Sort(sort);
However, this should now be done as follows:
Create a local infotable, make your changes, and then assign it back to the Thing infotable property.
This will pull the object from cache and make a copy. You can then change the copy without impact to the original property until the set is done.
If this was not true, every touch to the infotable would cause the entire object to be written back to the cache which would cause performance degradation.
var localInfoTable = me.yourInfoTable;
var sort = new Object();
sort.name = yourFieldName;
sort.ascending = booleanValue;
localInfoTable.Sort(sort);
me.yourInfoTable = localInfoTable;
Example: Updating field values for single-row infotables
Wrong way:
Each line in this script will get the entire infotable from the cache, change one value, and then put it back to the cache. Round trips to the cache take time since it processes the entire infotable every time. This will perform six calls to the cache, which in a remote system, can really slow down the script.
Things["data1"].info.v1 = 1;
Things["data1"].info.v2 = 2;
Things["data1"].info.v3 = 3;
Right way:
This example will get a clone of the infotable from the cache, update all data locally, and then put the infotable back in the cache. It reduces the cache calls to only two calls.
var myinfo = Things["data1"].info
myinfo.v1 = 1
myinfo.v2 = 2
myinfo.v3 = 3
Things["data1"].info = myinfo
Example: Updating field values across single-row infotables
In the example below, we show that when an infotable is assigned to a local variable, it is a clone. Changes to the original object will not be represented in the cloned value.
// creates a clone of InfoTable
var myinfo = Things["data1"].info;

// sets value of v1 to 5 in local InfoTable
myinfo.v1 = 5;

// change the orignal data1's v1 field value to 4, will not update the local value, it is still 5
Things["data1"].info.v1 = 4;

// assigning local info table to data2 so its v1 will have a value of 5
Things["data2"].info = myinfo;
Example: Behavior change with multi-row infotables
Below you can see that the clone of the infotable is still a proxy object. Methods called on the proxy object will take effect on the main object. The following is consistent with the proxy, but it differs from ThingWorx 8.4 behavior. In ThingWorx 8.4, if we did me.p2 = prop2, it would break the reference to the original object. It does not in ThingWorx 9 since they are treated as the same object proxy.
// An InfoTable local object for the "v1" field
var newEntry = new Object();
newEntry.v1 = 1;

// creates a proxy object from local object prop1 -> me.p1
var prop1 = me.p1;
prop1.v1=4;

// creates a proxy object from local object prop2 -> me.p2
var prop2 = me.p2;
prop2.v1=5;

// updates the me.p1 object, this breaks the proxy between prop1 -> me.p1
me.p1=prop2;

// prop2 reference is unchanged, still proxied
me.p2=prop2;

// this will not change me.p1 since the reference was broken when the object assignement to prop2 was done
prop1.AddRow(newEntry);

// this will update me.p2, the object reference is the same
prop2.AddRow(newEntry);
Example: Building a multi-row infotable
In this example, we want to add rows to an infotable:
Wrong way:
Although this looks correct because it is using a method on the database AddRow, it is actually proxying that change to the original object for every iteration in the loop. This will cause the infotable to be written to the cache for every iteration of the loop which is not desirable.
var myinfo = Things["data1"].info;
for (i=0; i < 10; i++) {
var row = new Object();
row.v1 = i;
row.v2 = i * 2;
row.v3 = i * 3;
myinfo.AddRow(row);
}
Things["data1"].info = myinfo;
Right way:
In this example, we cloned the object so that we can manipulate it without having any effect until it is assigned back to the original object.
var myinfo = Resources["InfoTableFunctions"].Clone({ t1: Things["data1"].info });
for (i=0; i < 10; i++) {
var row = new Object();
row.v1 = i;
row.v2 = i * 2;
row.v3 = i * 3;
myinfo.AddRow(row);
}
Things["data1"].info = myinfo;
The following example also works. Since we are not using the proxy object methods, it will not change the original object until it is assigned.
var myinfo = Things["data1"].info;
for (i=0; i < 10; i++) {
var row = new Object();
row.v1 = i;
row.v2 = i * 2* 10;
row.v3 = i * 3* 100;
myinfo.rows[i] = row;
}
Things["data1"].info = myinfo;
Was this helpful?