Write Data
Use the TypeScript client to write to a Synnax cluster.
The Synnax TypeScript client supports multiple methods for writing data to a cluster. We can write directly to a channel, or we can write to multiple channels in a streaming fashion using a writer.
Writing to a Channel
Writing to a channel requires us to write timestamps to it’s index channel before we write the data. We’ll create the following channels to use as examples:
import { DataType } from "@synnaxlabs/client";
const timeChannel = await client.channels.create({
name: "time",
dataType: DataType.TIMESTAMP,
isIndex: true,
});
const temperatureChannel = await client.channels.create({
name: "temperature",
dataType: DataType.FLOAT32,
index: timeChannel.key
});
We can then write data to timeChannel
and temperatureChannel
. We need to write to
the index channel before writing to the regular channel, so we will write to
timeChannel
first. Writing data via the channel’s write
method requires a typed
array:
import { TimeStamp, TimeSpan } from "@synnaxlabs/client";
const start = TimeStamp.now();
const timestamps = new BigInt64Array([
start.valueOf(), // valueOf() converts a TimeSpan to a bigint.
start.add(TimeSpan.seconds(1)).valueOf(),
start.add(TimeSpan.seconds(2)).valueOf(),
start.add(TimeSpan.seconds(3)).valueOf(),
start.add(TimeSpan.seconds(4)).valueOf()
]);
const temperatures = new Float32Array([20.0, 20.1, 20.2, 20.3, 20.4]);
// Write the timestamps to the index first
await timeChannel.write(start, timestamps);
// Then write the data
await temperatureChannel.write(start, temperatures);
Notice how we align the two arrays by using the common start
timestamp. This
tells Synnax that the first sample in the temperatures
array is associated
with the first timestamp in the timestamps
array, and so on.
Synnax will raise a ValidationError
if the index does not contain a
corresponding timestamp for every sample in the data array. After all, it
wouldn’t make sense to have a temperature reading without an associated
timestamp.
Using a Writer
While the above methods are great for writing static, existing data, it’s common
to want to write data in a streaming fashion for use in control sequences and
live dashboards. The Writer
class is designed to handle this use case (and is
actually used under the hood by the above methods).
Writers are a bit more complicated to operate, so we recommend reading the concepts page to learn more about how they work.
Opening a Writer
We’ll create the following index-indexed pair to use with our writer:
import { DataType } from "@synnaxlabs/client";
const timeChannel = await client.channels.create({
name: "time",
dataType: DataType.TIMESTAMP,
isIndex: true,
});
const temperatureChannel = await client.channels.create({
name: "temperature",
dataType: DataType.FLOAT32,
index: timeChannel.key
});
Then, we’ll open a writer for both of these channels by using the openWriter
method on the client:
import { TimeStamp, Series, Frame } from "@synnaxlabs/client";
const writer = await client.openWriter({
start: TimeStamp.now(),
channels: [timeChannel.key, temperatureChannel.key],
});
try {
for (let i = 0; i < 100; i++) {
const start = TimeStamp.now();
const timeSeries = BigInt64Array.from({ length: 10 }, (_, i) => (
start.add(TimeSpan.milliseconds(i)).valueOf()
))
const dataSeries = Float32Array.from({ length: 10 }, (_, i) => (
Math.sin(i / 100)
))
await writer.write(new Frame({
[timeChannel.key]: new Series(timeSeries),
[temperatureChannel.key]: new Series(dataSeries)
}));
await new Promise(resolve => setTimeout(resolve, 100));
}
await writer.commit():
} finally {
await writer.close()
}
This example will write 100 batches of 10 samples to the temperature
channel, each
roughly 100ms apart, and will commit all writes when finished.
Instead of passing in series to the frame, you may also pass in singular samples if you wish to only write one sample:
await writer.write(new Frame({
[timeChannel.key]: TimeStamp.now(),
[temperatureChannel.key]: i,
}));
It’s typical to write and commit millions of samples over the course of hours or days, intermittently calling commit to ensure that the data is safely stored in the cluster.
Alternatively, you could initiate with auto-commit enabled by passing in the
configuration argument enableAutoCommit: true
. When auto-commit is enabled, a
writer will effectuate a commit after every write, automatically saving the data
to the database.
It’s very important to free the writer resources when finished by calling the
close
method. If close
is not called at the end of the writer, other writers
may not be able to write to the same channels. We typically recommend placing
the writer operations inside a try-finally block to ensure that the writer is
always closed.