<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InfluxData Blog - Developer</title>
    <description>Posts from the Developer category on the InfluxData Blog</description>
    <link>https://www.influxdata.com/blog/category/tech/</link>
    <language>en-us</language>
    <lastBuildDate>Tue, 15 Sep 2026 00:00:00 +0000</lastBuildDate>
    <pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
    <ttl>1800</ttl>
    <item>
      <title>Telegraf 1.40 Release Notes</title>
      <description>
&lt;p&gt;You can find the binaries for the latest Telegraf release on our &lt;a href="https://influxdata.com/downloads"&gt;Downloads page&lt;/a&gt;. Many thanks to all the open source community members who contributed to this effort!&lt;/p&gt;

&lt;h2 id="new-plugins"&gt;New plugins&lt;/h2&gt;

&lt;p&gt;These are the newest plugins, first available in this version:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Databricks Zerobus output&lt;/strong&gt; (&lt;code class="language-markup"&gt;outputs.zerobus&lt;/code&gt;)
    &lt;ul&gt;
      &lt;li&gt;Streams metrics to a Unity Catalog Delta table using the &lt;a href="https://docs.databricks.com/aws/en/ingestion/zerobus-ingest/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;Databricks Zerobus Ingest&lt;/a&gt; service over gRPC to directly commit into Delta&lt;/li&gt;
      &lt;li&gt;Contributed by &lt;a href="https://github.com/zlata-stefanovic-db/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;zlata-stefanovic-db&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fritzbox Smarthome input&lt;/strong&gt;  (&lt;code class="language-markup"&gt;inputs.fritzbox_smarthome&lt;/code&gt;)
    &lt;ul&gt;
      &lt;li&gt;Reads metrics such as lamp state, energy consumption, etc., from Smarthome-capable &lt;a href="https://fritz.com/"&gt;FRITZ!&lt;/a&gt; routers using the &lt;a href="https://fritz.com/en/pages/interfaces"&gt;AVM Home Automation&lt;/a&gt; interface.&lt;/li&gt;
      &lt;li&gt;Contributed by &lt;a href="https://portal.influxdata.com/downloads/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;hdecarne&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="important-changes"&gt;Important changes&lt;/h2&gt;

&lt;p&gt;Here are some changes to highlight:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Cisco Model-Driven-Telemetry fixes&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Several fixes changed the metric format&lt;/li&gt;
      &lt;li&gt;Configured aliases are now respected for microburst and RIB messages, which might result in changes to the metric name&lt;/li&gt;
      &lt;li&gt;Spurious, invalid metrics with wrong tag-sets were removed or corrected&lt;/li&gt;
      &lt;li&gt;Row numbers are now tagged correctly for table messages&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Smart input fix for power-on-hours&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Power-on-hours are now reported correctly for raw values in &lt;code class="language-markup"&gt;182h+39m+24.879s&lt;/code&gt; format&lt;/li&gt;
      &lt;li&gt;Previously, those values resulted in the field containing the time in seconds&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Removal of deprecated plugins&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.aerospike&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.sflow&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;outputs.amon&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Removal of deprecated plugin options&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.kafka_consumer&lt;/code&gt; removal of &lt;code class="language-markup"&gt;connection_strategy&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.openstack&lt;/code&gt; removal of &lt;code class="language-markup"&gt;server_diagnostics&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.procstat&lt;/code&gt; removal of &lt;code class="language-markup"&gt;cmdline_tag&lt;/code&gt;, &lt;code class="language-markup"&gt;pid_tag&lt;/code&gt;, and &lt;code class="language-markup"&gt;supervisor_unit&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class="language-markup"&gt;inputs.tail&lt;/code&gt; removal of &lt;code class="language-markup"&gt;from_beginning&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Use the &lt;code class="language-markup"&gt;telegraf config migrate&lt;/code&gt; command to migrate your configuration&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Elasticsearch query input plugin migration to official library&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Migration to official and maintained libraries&lt;/li&gt;
      &lt;li&gt;Additional support for Elasticsearch v7, v8 and v9&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="downloads"&gt;Downloads&lt;/h2&gt;

&lt;p&gt;Head to our &lt;a href="https://portal.influxdata.com/downloads/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;Downloads page&lt;/a&gt; to get the latest Telegraf release. If you have issues or questions, please join our &lt;a href="https://influxdata.com/slack/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;InfluxDB Community Slack&lt;/a&gt; or post them in our InfluxDB GitHub Repo or &lt;a href="https://community.influxdata.com/c/influxdb2/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;Community Site&lt;/a&gt;, and we will look into them.&lt;/p&gt;

&lt;h2 id="influxdb-university"&gt;InfluxDB University&lt;/h2&gt;

&lt;p&gt;Learn more about collecting data with Telegraf by taking the free InfluxDB University &lt;a href="https://university.influxdata.com/courses/data-collection-with-telegraf-tutorial/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_1_40_release_notes_influxdb&amp;amp;utm_content=blog"&gt;Data Collection with Telegraf course&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;

&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What's new in Telegraf 1.40?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Telegraf 1.40 adds two new plugins: a Databricks Zerobus output plugin for streaming metrics directly into Unity Catalog Delta tables over gRPC, and a Fritzbox Smarthome input plugin for reading device metrics from AVM's FRITZ! routers. The release also fixes several Cisco Model-Driven-Telemetry issues, corrects a SMART input bug for power-on-hours reporting, and migrates the Elasticsearch query input plugin to an official, maintained library with added support for Elasticsearch v7, v8, and v9.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Which Telegraf plugins were removed in 1.40?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Three deprecated input/output plugins were removed: inputs.aerospike, inputs.sflow, and outputs.amon. If you're running configurations that use any of these, you'll need to migrate to an alternative plugin before upgrading.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What deprecated plugin options were removed in Telegraf 1.40?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Several plugin-specific options were dropped: &lt;code class="language-markup"&gt;connection_strategy&lt;/code&gt; from &lt;code class="language-markup"&gt;inputs.kafka_consumer&lt;/code&gt;, &lt;code class="language-markup"&gt;server_diagnostics from &amp;lt;/code class="language-markup"&amp;gt;inputs.openstack&lt;/code&gt;, and &lt;code class="language-markup"&gt;cmdline_tag&lt;/code&gt;, &lt;code class="language-markup"&gt;pid_tag&lt;/code&gt;, and &lt;code class="language-markup"&gt;supervisor_unit&lt;/code&gt; from &lt;code class="language-markup"&gt;inputs.procstat&lt;/code&gt;. The &lt;code class="language-markup"&gt;from_beginning&lt;/code&gt; option was also removed from &lt;code class="language-markup"&gt;inputs.tail&lt;/code&gt;. InfluxData recommends running &lt;code class="language-markup"&gt;telegraf config migrate&lt;/code&gt; to automatically update existing configuration files affected by these removals.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-4"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How do I download and get support for Telegraf 1.40?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-4" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Binaries are available on InfluxData's Downloads page. For questions or issues, join the InfluxDB Community Slack or post on the InfluxDB Community Site. InfluxData also offers a free "Data Collection with Telegraf" course through InfluxDB University for those new to the tool.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;
</description>
      <pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/telegraf-1-40-release-notes-influxdb/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/telegraf-1-40-release-notes-influxdb/</guid>
      <category>Product</category>
      <category>Developer</category>
      <author>Sven Rebhan (InfluxData)</author>
    </item>
    <item>
      <title> Building a Practical Time Series Data Layer for Automotive Manufacturing</title>
      <description>&lt;p&gt;Automotive manufacturing generates data at several operating cadences. Sensors and controllers emit measurements continuously. Equipment and line states change as events. Production and quality systems add context at the part, batch, shift, and plant level. Together, these records describe how a manufacturing process behaves over time.&lt;/p&gt;

&lt;p&gt;That data can support plant visibility, investigations, scrap reduction, condition monitoring, and longer-term comparison, but only if the storage and query layer can keep up with the workload. InfluxDB is a practical fit for these types of workloads because it is optimized for continuous, time-stamped data and can process, retain, and query it without requiring every system around it to be redesigned.&lt;/p&gt;

&lt;h2 id="why-automotive-data-is-a-time-series-workload"&gt;Why automotive data is a time series workload&lt;/h2&gt;

&lt;p&gt;A time series workload is, at its core, any data where you primarily store, query, and analyze it by looking at when it happened and how it progressed from one state to another.&lt;/p&gt;

&lt;p&gt;A temperature measurement in a time series workload never exists in isolation. When a device overheats, you ask how long it took to heat up, whether it had opportunities to cool down, and what that looked like over the span of five, ten, or fifty minutes. The same principles can apply to vibration, torque, current, pressure, flow, cycle state, machine availability, inspection results, alarms, and many  other signals generated during vehicle and component production.&lt;/p&gt;

&lt;p&gt;Queries are written to understand what changed during a particular interval, whether a condition persisted, how one shift compared with another, or how several signals behaved around a production or quality event. Those queries tend to start with a filter on timestamps to the relevant window in which questions are being asked, narrow the data to the relevant equipment or process, and then aggregate, compare, or retrieve the latest values.&lt;/p&gt;

&lt;p&gt;The write pattern of time series data is often distinctive, too. Sensors create readings at a regular cadence, so new data points arrive continuously, almost exclusively as appends to the database, with few or no updates or edits to historical rows.&lt;/p&gt;

&lt;p&gt;The same data also has value at very different time scales. Recent, high-resolution telemetry can support detailed investigations. Longer-range summaries can support plant management, capacity planning, process comparison, forecasting, and cross-plant visibility. Keeping every full-resolution observation may be difficult to justify, but reducing everything to hourly averages at ingestion time throws away information before anyone knows whether it will matter.&lt;/p&gt;

&lt;p&gt;This combination of continuous writes, time-bounded reads, multiple resolutions, and changing retention value is what makes automotive telemetry a time series workload.&lt;/p&gt;

&lt;p&gt;Automotive systems also contain plenty of data that doesn’t fit this description. Bills of material, work orders, user accounts, inventory records, and transactional production data may belong in an MES, ERP system, or relational database. InfluxDB is designed to operationalize telemetry, and it’s part of a well-orchestrated tech stack.&lt;/p&gt;

&lt;h2 id="why-use-a-time-series-database"&gt;Why use a time series database?&lt;/h2&gt;

&lt;p&gt;Any relational database (or even Excel) can store time-stamped measurements. The question is: how efficiently, effectively, and easily can they handle a time series workload, and can they scale? We already have &lt;a href="https://www.influxdata.com/blog/why-time-series-database-influxdb/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;a blog on this topic&lt;/a&gt; that explains the advantages of time series databases, and it’s a good place to start if you want to understand their key advantages.&lt;/p&gt;

&lt;p&gt;To summarize for automotive telemetry: InfluxDB most efficiently handles high-volume ingestion and time-oriented querying, while &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/reference/internals/data-retention/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;database- and table-level retention&lt;/a&gt; lets you control how long each data type remains available. A high-volume raw table can have a shorter retention period than a cleaned or downsampled table in the same database, and because InfluxDB 3 is designed and built for low-cost object storage, data costs stay low. Even better, all tables are very efficiently compressed, minimizing the footprint in object storage and lowering costs.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/admin/last-value-cache/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;Last Value Cache&lt;/a&gt; addresses another common query pattern: retrieving the newest value, or last few values, for each series. That is useful for current-state views where repeatedly scanning recent history would be unnecessary work.&lt;/p&gt;

&lt;p&gt;These features map directly to the shape of automotive manufacturing data workloads, which ingest continuously, query by time, keep recent detail, summarize older data, and serve current state efficiently. This makes a time-series database like InfluxDB the only cost-effective, performant, scalable choice for high-volume, time series workloads.&lt;/p&gt;

&lt;h2 id="a-reference-architecture-for-automotive-manufacturing"&gt;A reference architecture for automotive manufacturing&lt;/h2&gt;

&lt;p&gt;Rather than talking about it at a high level, it may be easier to show you how the architecture works in action.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/influxdata/influxdb3-ref-auto-manufacturing?utm_source=chatgpt.com/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;InfluxDB 3 automotive manufacturing reference architecture&lt;/a&gt; is a runnable example of an automotive time series pipeline. It uses Docker Compose, InfluxDB 3 Enterprise, Processing Engine plugins, and a small web UI:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;git clone https://github.com/influxdata/influxdb3-ref-auto-manufacturing.git
cd influxdb3-ref-auto-manufacturing
make up&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On first boot, the setup asks for an email address and sends an InfluxDB 3 Enterprise license-validation link. After validation, this stack begins generating and processing the simulated plant data, and you can see it by opening the UI at:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;http://localhost:8080&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The repository installs InfluxDB-approved plugins, creates tables and triggers, runs the processing chain, and exposes raw, processed, downsampled, and forecast data through the UI. It also demonstrates isolated station pipelines and a Last Value Cache for current-state views.&lt;/p&gt;

&lt;p&gt;Make sure you read the production notes before using it as a deployment template. The demo is single-node, uses a file-backed object store, shares an admin token between services, and does not enable TLS inside the Compose network. Production requires scoped service tokens, TLS, durable object storage, monitoring, backup planning, and an appropriate multi-node topology. One easy-to-miss detail: create source tables before registering WAL triggers, or the triggers may never fire.&lt;/p&gt;

&lt;p&gt;The repository’s most useful pattern is its separation of concerns. You can replace the signal generator with an OPC UA, historian, or gateway feed and keep the downstream stages. You can change the filter without rewriting the dashboard, or you can change retention without touching the PLC.&lt;/p&gt;

&lt;h2 id="building-on-the-reference-architecture"&gt;Building on the reference architecture&lt;/h2&gt;

&lt;p&gt;Obviously, this is a reference architecture, and it’s meant to be the first step towards building the architecture for your deployment. The most obvious first change to make is replacing the signal generator with an actual data source.&lt;/p&gt;

&lt;p&gt;The repository is deliberately structured so that this happens at the input boundary. &lt;a href="https://docs.influxdata.com/telegraf/v1/input-plugins/opcua/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;Telegraf’s OPC UA input&lt;/a&gt; can collect data from an OPC UA server and write it to InfluxDB. An industrial integration platform such as Litmus or HighByte can write line protocol through the InfluxDB write API. A historian or existing plant application can also become the source if it already exposes the required data. Downstream processing stages don’t need to know which route the points took to get there.&lt;/p&gt;

&lt;p&gt;From there, the work becomes signal-specific.&lt;/p&gt;

&lt;p&gt;A regular time grid may be necessary when several sensors need to be aligned or when an algorithm expects a stable sample rate. It may add little value for an event stream. Gap filling should be bounded and explicit, and ideally preserve enough metadata to distinguish observed values from generated ones. Some gaps should remain gaps because the absence of data is useful information, as it can indicate that certain systems were paused or sensors stopped generating readings.&lt;/p&gt;

&lt;p&gt;Filtering requires similar care. A filter that cleans up a slowly changing booth-temperature signal may be inappropriate for current, vibration, pressure, or torque. Before filtering a production stream, test the filter against the events engineers actually need to preserve. Keep the raw signal long enough to compare the processed output against it.&lt;/p&gt;

&lt;p&gt;Downsampling is another place to be deliberate. Whether you use an average, minimum, maximum, or median, downsampling necessarily entails losing some information. Fortunately, the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/library/official/downsampler/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;Downsampler plugin&lt;/a&gt; can generate several calculations for the same source measurement. Useful output might include an average for long-term trends, a maximum for threshold analysis, and a count to show whether the interval contained the expected number of observations.&lt;/p&gt;

&lt;p&gt;Retention follows the same logic. Raw telemetry can remain available for the period in which detailed investigations are likely. Intermediate processing tables may only need to exist long enough to support the next stage. Cleaned or downsampled data can remain available for longer-range analysis. InfluxDB’s table-level retention overrides make it possible to apply those policies within the same database, so that data can flow from maximal-resolution fresh tables into lower-resolution longer-term storage. This ensures all data is available when needed, without paying to store immense volumes of data that will only be used for sporadic historical analysis.&lt;/p&gt;

&lt;p&gt;Finally, the local deployment needs ordinary production hardening. The repository’s architecture notes cover moving from local files to S3, Google Cloud Storage, or Azure Blob Storage; separating ingest, query, compaction, and processing roles; using scoped tokens for individual services; and enabling TLS across trust boundaries. Plugins should also have version control, automated tests, resource limits, and a repeatable deployment process. Running Python inside the database removes some infrastructure, but it’s still production code.&lt;/p&gt;

&lt;h2 id="augmenting-an-existing-automotive-data-stack"&gt;Augmenting an existing automotive data stack&lt;/h2&gt;

&lt;p&gt;Many automotive environments already have control systems, historians, manufacturing applications, quality systems, and enterprise analytics infrastructure. There’s no need to displace them before evaluating InfluxDB. Similarly, the justification for adding InfluxDB doesn’t depend on proving that the historian, warehouse, or relational database is bad. It depends on whether a specialized time series database can serve a particular workload more efficiently.&lt;/p&gt;

&lt;p&gt;A useful entry point is to start bounded, picking a specific task or domain where InfluxDB may be worth leveraging. Mirror data from that domain into InfluxDB, but allow your existing systems to remain authoritative. Because InfluxDB 3 uses SQL, you can likely run the same queries across your existing system and InfluxDB at the same time, and just like that, you’re testing which system is more efficient. How fresh is the data? What’s the query latency, especially when you’re leveraging the Last Value Cache?&lt;/p&gt;

&lt;p&gt;Once it’s running, you can see what else InfluxDB unlocks for you. Use the Processing Engine with ML plugins to detect anomalies and generate forecasts that help with predictive maintenance. Hook into the InfluxDB MCP server to power your AI agents with your data. The database is the backbone, and what you build on top of it is where you truly begin to derive value.&lt;/p&gt;

&lt;p&gt;Measure sustained ingestion, storage consumption at each resolution, query latency for real investigation patterns, and operational overhead. Ask yourself how useful the resulting analysis, forecasts, and downstream tools you built are. A bounded evaluation with representative data gives you evidence from your own workload, which is what truly matters for your use case.&lt;/p&gt;

&lt;h2 id="bringing-it-all-together"&gt;Bringing it all together&lt;/h2&gt;

&lt;p&gt;Automotive telemetry has the defining characteristics of a time series workload: continuous writes, time-bounded queries, value at multiple resolutions, and a useful lifetime that changes as the data ages.&lt;/p&gt;

&lt;p&gt;InfluxDB provides a specialized solution for that workload. The automotive reference architecture makes the design concrete by showing how ingestion, resampling, gap filling, filtering, downsampling, forecasting, caching, and retention can fit together without becoming one inseparable pipeline. Once you’ve got the data flowing, you’ll unlock new capabilities to maximize the value you get from your data, driving efficiency and better data-driven results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.influxdata.com/products/influxdb3-enterprise/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=time_series_for_auto_manufacturing&amp;amp;utm_content=blog"&gt;Get started with a free trial of InfluxDB 3 Enterprise, and try it out today.
&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Wed, 09 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/time-series-for-auto-manufacturing/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/time-series-for-auto-manufacturing/</guid>
      <category>Developer</category>
      <author>Cole Bowden (InfluxData)</author>
    </item>
    <item>
      <title>Getting Started with InfluxDB 3 and Grafana Tutorial </title>
      <description>&lt;p&gt;InfluxDB and Grafana are the most common pairing in time series monitoring, and division of labor between them is simple. InfluxDB stores and queries the data: high-volume, time-stamped readings from sensors, servers, meters, vehicles, and anything else that emits time series. Grafana visualizes the data: dashboards, time series panels, stat tiles, state timelines, and alerts. 
Neither tool replaces the other, and many production monitoring stacks run both.&lt;/p&gt;

&lt;p&gt;Connecting them has gotten considerably simpler with InfluxDB 3. Grafana ships with a built-in InfluxDB datasource that speaks InfluxDB 3 SQL over Flight SQL, so there is no third-party plugin to install and no custom query language to learn.&lt;/p&gt;

&lt;p&gt;This guide walks through that integration end-to-end using a realistic dataset you generate yourself. You will learn the following in this tutorial:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Getting data in&lt;/strong&gt; - Writing line protocol and what tags, fields, and timestamps mean for how you will query later.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Transforming data as it arrives&lt;/strong&gt; - Using an InfluxDB 3 Python Processing Engine plugin to turn raw readings into a derived table of decisions. This pattern keeps dashboard queries simple and your source data untouched.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Connecting Grafana securely&lt;/strong&gt; - Read-only tokens and the datasource settings that actually matter.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Building a real dashboard&lt;/strong&gt; - Template variables, chained variables, &lt;code class="language-markup"&gt;$__timeFilter&lt;/code&gt;, &lt;code class="language-markup"&gt;date_bin()&lt;/code&gt; for downsampling, and eight panels across stats, time series, state timelines, bar gauges, and tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="what-you-will-build-with-influxdb-3-and-grafana"&gt;What you will build with InfluxDB 3 and Grafana&lt;/h2&gt;

&lt;p&gt;To make all of that concrete, you will generate a week of smart-building data. The generator is deterministic, so your numbers will match the expected output at every step, and you can verify each stage rather than guessing.&lt;/p&gt;

&lt;p&gt;The raw table, &lt;code class="language-markup"&gt;building_telemetry&lt;/code&gt;, preserves the original readings. The derived table, &lt;code class="language-markup"&gt;iaq_assessment&lt;/code&gt;, adds an indoor-air-quality risk score, a severity label, a recommended action, and a conservative estimate of energy used above an idle baseline. The finished dashboard answers four questions an operator actually asks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Which occupied rooms experienced unhealthy air?&lt;/li&gt;
  &lt;li&gt;Where did CO₂ and PM2.5 spike?&lt;/li&gt;
  &lt;li&gt;How much energy was consumed while rooms were empty?&lt;/li&gt;
  &lt;li&gt;Which building zones should someone investigate first?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dataset has four incidents deliberately built into it. A stuffy Tuesday conference room, a lab drawing power on an empty Wednesday evening, a Thursday ventilation problem in the design studio, and a building-wide Friday particulate event, so every panel has something real to show.&lt;/p&gt;

&lt;p&gt;Buildings are just an example. The architecture in this tutorial applies unchanged to industrial equipment, fleet telemetry, energy metering, network devices, or application metrics. If you are evaluating InfluxDB and Grafana for any of those, this is a working template.&lt;/p&gt;

&lt;h4 id="prerequisites"&gt;Prerequisites&lt;/h4&gt;

&lt;p&gt;This walk-through assumes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A Debian- or Ubuntu-based Linux environment; Grafana also publishes installation instructions for &lt;a href="https://grafana.com/docs/grafana/latest/setup-grafana/installation/redhat-rhel-fedora/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;RHEL, Fedora&lt;/a&gt;, and other supported systems.&lt;/li&gt;
  &lt;li&gt;Python 3.9 (or newer).&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;sudo&lt;/code&gt; access for installing Grafana.&lt;/li&gt;
  &lt;li&gt;An InfluxDB 3 Enterprise trial, at-home, or commercial license.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="why-use-grafanas-built-in-influxdb-datasource"&gt;Why use Grafana’s built-in InfluxDB datasource?&lt;/h2&gt;

&lt;p&gt;Grafana includes an InfluxDB datasource that supports InfluxDB 3 SQL out of the box. There’s no need to install anything else, which makes getting started easy.&lt;/p&gt;

&lt;p&gt;Other choices still have valid uses:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;InfluxQL is supported by the plugin as well, and is useful when migrating existing InfluxDB 1.x queries or working through an HTTP/1.1-only proxy.&lt;/li&gt;
  &lt;li&gt;A separately hosted Grafana is useful for a shared observability platform, but its backend needs private, HTTP/2-capable access to the InfluxDB query node.&lt;/li&gt;
  &lt;li&gt;A custom API can add domain-specific authorization or reshape data, but it adds another service that a beginner tutorial does not need.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="create-your-project-directory"&gt;Create Your Project Directory&lt;/h4&gt;

&lt;p&gt;Create a folder anywhere convenient and open it in your text editor:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;mkdir smart-building-demo
cd smart-building-demo
mkdir plugins influxdb-data&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The tutorial uses only these local paths:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;smart-building-demo/
├── generate.py
├── smart-building.lp
├── influxdb-data/
└── plugins/
    └── iaq_assessment.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Keep the terminal in &lt;code class="language-markup"&gt;smart-building-demo&lt;/code&gt; for the remaining commands.&lt;/p&gt;

&lt;h4 id="install-and-start-influxdb-3-enterprise"&gt;Install and Start InfluxDB 3 Enterprise&lt;/h4&gt;

&lt;p&gt;InfluxData provides a quick installer for Linux and macOS. Download the installer first so you can inspect it before running:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;curl -o /tmp/install_influxdb3.sh \
  https://www.influxdata.com/d/install_influxdb3.sh

# Optional: inspect the downloaded script before running it.
less /tmp/install_influxdb3.sh

sh /tmp/install_influxdb3.sh enterprise&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The installer tracks the latest InfluxDB 3 Enterprise release. For production, InfluxData recommends its DEB/RPM packages or a carefully secured container deployment; see the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/install/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;official installation guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Verify that the binary is available:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 --version&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the command is not found, open a new terminal or follow the installer’s instructions for adding the binary to your &lt;code class="language-markup"&gt;PATH&lt;/code&gt;, then return to the demo directory.&lt;/p&gt;

&lt;h4 id="start-the-database-with-the-processing-engine-enabled"&gt;Start the Database with the Processing Engine Enabled&lt;/h4&gt;

&lt;p&gt;Run InfluxDB in the first terminal and leave it running:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 serve \
  --node-id smart-building-node \
  --cluster-id smart-building-cluster \
  --object-store file \
  --data-dir "$PWD/influxdb-data" \
  --plugin-dir "$PWD/plugins"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code class="language-markup"&gt;--plugin-dir&lt;/code&gt; flag activates the Processing Engine and tells InfluxDB where to find the Python file you will create. The data directory persists the database between restarts. These options are documented in the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;Processing Engine guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the first start, InfluxDB asks you to choose and activate a license. A trial provides full Enterprise features for 30 days; the at-home license is intended for eligible hobbyist use. Follow the prompts and email-verification step described in the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/admin/license/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;license documentation&lt;/a&gt;. Do not include license files, license JWTs, or account details in screenshots or source code.&lt;/p&gt;

&lt;h4 id="create-and-store-the-administrator-token"&gt;Create and Store the Administrator Token&lt;/h4&gt;

&lt;p&gt;Open a second terminal, return to &lt;code class="language-markup"&gt;smart-building-demo&lt;/code&gt;, and create the initial administrator token:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 create token --admin&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;InfluxDB displays this token only once. Store it in a password manager. Then load it into the current terminal without placing it in shell history:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;read -rsp "Paste the InfluxDB administrator token: " INFLUXDB_TOKEN
echo
export INFLUXDB3_AUTH_TOKEN="$INFLUXDB_TOKEN"
export INFLUXDB3_HOST_URL="http://127.0.0.1:8181"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Do not put the token in &lt;code class="language-markup"&gt;generate.py&lt;/code&gt;, the plugin, Grafana queries, screenshots, or a committed shell script. InfluxDB’s &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/get-started/setup/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;authorization setup guide&lt;/a&gt; recommends the &lt;code class="language-markup"&gt;INFLUXDB3_AUTH_TOKEN&lt;/code&gt; environment variable for CLI authentication.&lt;/p&gt;

&lt;p&gt;Create a database for the demo:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;export INFLUXDB_DATABASE="smart_building"
influxdb3 create database "$INFLUXDB_DATABASE"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Expected result: the command completes without an error. You can confirm the database exists with:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 show databases&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="generate-a-demo-telemetry-data"&gt;Generate a Demo Telemetry Data&lt;/h4&gt;

&lt;p&gt;A good tutorial dataset should be small, understandable, and repeatable. This generator creates six zones, sampled every five minutes for seven days.&lt;/p&gt;

&lt;p&gt;In your editor, create &lt;code class="language-markup"&gt;generate.py&lt;/code&gt; in &lt;code class="language-markup"&gt;smart-building-demo&lt;/code&gt; and paste the following code:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-python"&gt;#!/usr/bin/env python3
"""Generate deterministic smart-building telemetry as InfluxDB line protocol."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
import math
from pathlib import Path
import random

START = datetime(2026, 8, 10, tzinfo=timezone.utc)
INTERVAL = timedelta(minutes=5)
POINTS_PER_ZONE = 7 * 24 * 12

ZONES = [
    {"building": "hq", "floor": "1", "zone": "open_office", "sensor": "hq-1-open", "capacity": 40, "offset": 0.0},
    {"building": "hq", "floor": "1", "zone": "conference_a", "sensor": "hq-1-conf-a", "capacity": 12, "offset": 0.3},
    {"building": "hq", "floor": "1", "zone": "cafe", "sensor": "hq-1-cafe", "capacity": 50, "offset": 0.7},
    {"building": "hq", "floor": "2", "zone": "lab", "sensor": "hq-2-lab", "capacity": 20, "offset": 0.5},
    {"building": "annex", "floor": "1", "zone": "design_studio", "sensor": "annex-1-design", "capacity": 24, "offset": -0.2},
    {"building": "annex", "floor": "2", "zone": "meeting_room", "sensor": "annex-2-meeting", "capacity": 10, "offset": 0.1},
]

def occupancy_for(zone: dict[str, object], ts: datetime, rng: random.Random) -&amp;gt; int:
    hour = ts.hour + ts.minute / 60
    weekday = ts.weekday() " 5
    capacity = int(zone["capacity"])
    name = str(zone["zone"])

    if not weekday:
        base = 0
    elif 8 "= hour " 18:
        profile = {
            "open_office": 0.64,
            "conference_a": 0.34,
            "cafe": 0.18,
            "lab": 0.48,
            "design_studio": 0.70,
            "meeting_room": 0.28,
        }[name]
        curve = 0.82 + 0.18 * math.sin((hour - 8) * math.pi / 10)
        base = round(capacity * profile * curve)
    else:
        base = 0

    if weekday and name == "cafe" and 11.5 "= hour " 13.5:
        base = round(capacity * 0.88)
    if ts.weekday() == 1 and name == "conference_a" and 14 "= hour " 16:
        base = capacity
    if ts.weekday() == 3 and name == "design_studio" and 9 "= hour " 12:
        base = round(capacity * 0.92)

    return max(0, min(capacity, base + rng.randint(-1, 1)))

def values_for(zone: dict[str, object], ts: datetime, rng: random.Random):
    occupancy = occupancy_for(zone, ts, rng)
    capacity = int(zone["capacity"])
    hour = ts.hour + ts.minute / 60
    zone_name = str(zone["zone"])
    diurnal = math.sin((hour - 7) * math.pi / 12)
    load = occupancy / capacity if capacity else 0

    temperature = 21.3 + float(zone["offset"]) + 1.3 * diurnal + 1.1 * load + rng.uniform(-0.18, 0.18)
    humidity = 43.0 - 4.0 * diurnal + 3.0 * load + rng.uniform(-0.8, 0.8)
    co2 = round(430 + 530 * load + rng.uniform(-18, 18))
    pm25 = 5.2 + 2.8 * load + rng.uniform(-0.7, 0.7)
    power = 0.65 + 0.10 * occupancy + max(0.0, temperature - 23.0) * 0.42 + rng.uniform(-0.12, 0.12)

    # Deterministic incidents used by the dashboard.
    if ts.weekday() == 1 and zone_name == "conference_a" and 14 "= hour " 16:
        co2 += round(720 + 80 * math.sin((hour - 14) * math.pi / 2))
    if ts.weekday() == 3 and zone_name == "design_studio" and 9 "= hour " 12:
        co2 += round(560 + 70 * math.sin((hour - 9) * math.pi / 3))
    if ts.weekday() == 4 and 9 "= hour " 14:
        pm25 += 20 + 4 * math.sin((hour - 9) * math.pi / 5)
    if ts.weekday() == 2 and zone_name == "lab" and 19 "= hour " 22:
        power += 5.5

    return (
        occupancy,
        round(temperature, 2),
        round(humidity, 2),
        int(co2),
        round(pm25, 2),
        round(max(power, 0.1), 2),
    )

def main() -&amp;gt; None:
    output = Path("smart-building.lp")
    rng = random.Random(20260821)
    rows = 0

    with output.open("w", encoding="utf-8") as handle:
        for tick in range(POINTS_PER_ZONE):
            ts = START + tick * INTERVAL
            epoch_s = int(ts.timestamp())

        for zone in ZONES:
                occupancy, temperature, humidity, co2, pm25, power = values_for(zone, ts, rng)
                tags = (
                    f"building={zone['building']},floor={zone['floor']},"
                    f"zone={zone['zone']},sensor_id={zone['sensor']}"
                )
                fields = (
                    f"temperature_c={temperature:.2f},humidity_pct={humidity:.2f},"
                    f"co2_ppm={co2}i,pm25_ug_m3={pm25:.2f},power_kw={power:.2f},"
                    f"occupancy={occupancy}i,capacity={zone['capacity']}i"
                )
                handle.write(f"building_telemetry,{tags} {fields} {epoch_s}\n")
                rows += 1

    print(f"wrote {rows} deterministic rows to {output}")

if __name__ == "__main__":
    main()&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Run the generator:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;python3 generate.py
wc -l smart-building.lp&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;wrote 12096 deterministic rows to smart-building.lp
12096 smart-building.lp&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can open &lt;code class="language-markup"&gt;smart-building.lp&lt;/code&gt; in your editor to inspect the line protocol. Each line contains:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Tags&lt;/strong&gt;: &lt;code class="language-markup"&gt;building&lt;/code&gt;, &lt;code class="language-markup"&gt;floor&lt;/code&gt;, &lt;code class="language-markup"&gt;zone&lt;/code&gt;, and &lt;code class="language-markup"&gt;sensor_id&lt;/code&gt; are low-cardinality dimensions used for filtering and grouping.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fields&lt;/strong&gt;: temperature, humidity, CO₂, PM2.5, power, occupancy, and capacity are values that change over time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Timestamp&lt;/strong&gt;: epoch seconds—the ingestion command will specify &lt;code class="language-markup"&gt;--precision s&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="add-an-influxdb-python-processing-engine-plugin"&gt;Add an InfluxDB Python Processing Engine Plugin&lt;/h4&gt;

&lt;p&gt;Raw sensor values are valuable, but operators usually want a smaller set of decisions: Is the room healthy? How urgent is the problem? What should someone check?&lt;/p&gt;

&lt;p&gt;Create &lt;code class="language-markup"&gt;plugins/iaq_assessment.py&lt;/code&gt; in your editor and paste this code:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-python"&gt;"""Create dashboard-ready indoor-air-quality assessments from raw telemetry.

InfluxDB 3 injects LineBuilder and influxdb3_local at runtime.
"""

def _classify(row):
    co2 = int(row["co2_ppm"])
    pm25 = float(row["pm25_ug_m3"])
    temperature = float(row["temperature_c"])
    humidity = float(row["humidity_pct"])
    occupancy = int(row["occupancy"])
    power_kw = float(row["power_kw"])

    co2_component = min(70, max(0, round((co2 - 800) / 8)))
    pm_component = min(30, max(0, round((pm25 - 8) * 2)))
    comfort_component = (
        10
        if temperature " 20
        or temperature &amp;gt; 25
        or humidity " 30
        or humidity &amp;gt; 60
        else 0
    )
    risk_score = min(100, co2_component + pm_component + comfort_component)

    if risk_score &amp;gt;= 70:
        severity = "critical"
    elif risk_score &amp;gt;= 45:
        severity = "high"
    elif risk_score &amp;gt;= 20:
        severity = "moderate"
    else:
        severity = "normal"

    if co2 &amp;gt; 1000 and occupancy &amp;gt; 0:
        recommendation = "increase_ventilation"
    elif pm25 &amp;gt; 25:
        recommendation = "check_filtration"
    elif comfort_component:
        recommendation = "balance_hvac"
    else:
        recommendation = "monitor"

    energy_waste_kw = max(0.0, power_kw - 1.5) if occupancy == 0 else 0.0
    return risk_score, severity, recommendation, energy_waste_kw

def process_writes(influxdb3_local, table_batches, args=None):
    processed = 0

    for table_batch in table_batches:
        if table_batch["table_name"] != "building_telemetry":
            continue

        for row in table_batch["rows"]:
            risk_score, severity, recommendation, energy_waste_kw = _classify(row)
            line = (
                LineBuilder("iaq_assessment")
                .tag("building", row["building"])
                .tag("floor", row["floor"])
                .tag("zone", row["zone"])
                .tag("severity", severity)
                .int64_field("risk_score", risk_score)
                .int64_field(
                    "co2_excess_ppm", max(0, int(row["co2_ppm"]) - 800)
                )
                .string_field("recommendation", recommendation)
                .float64_field("energy_waste_kw", energy_waste_kw)
                .bool_field("occupied", int(row["occupancy"]) &amp;gt; 0)
                .time_ns(int(row["time"]))
            )
            influxdb3_local.write(line)
            processed += 1

    influxdb3_local.info(
        f"iaq_assessment processed {processed} building_telemetry rows"
    )&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The plugin deliberately uses only the API InfluxDB injects and the Python standard library, so you don’t have to install any packages.&lt;/p&gt;

&lt;h4 id="understanding-the-risk-calculation"&gt;Understanding the Risk Calculation&lt;/h4&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;CO₂ component     = clamp(round((co2_ppm - 800) / 8), 0, 70)
PM2.5 component   = clamp(round((pm25_ug_m3 - 8) × 2), 0, 30)
comfort component = 10 outside 20–25 °C or 30–60% humidity
risk score        = min(100, all three components added together)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The score becomes one of four states:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Score&lt;/th&gt;
    &lt;th&gt;Severity&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;0–19&lt;/td&gt;
    &lt;td&gt;normal&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;20–44&lt;/td&gt;
    &lt;td&gt;moderate&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;45–69&lt;/td&gt;
    &lt;td&gt;high&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;70–100&lt;/td&gt;
    &lt;td&gt;critical&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;The rules are intentionally simple and transparent. They are tutorial logic, not a health, safety, or regulatory standard.&lt;/p&gt;

&lt;p&gt;The plugin also calculates &lt;code class="language-markup"&gt;energy_waste_kw&lt;/code&gt;. When occupancy is zero, it counts only power above a conservative 1.5 kW idle baseline. This is different from counting all energy used while a room is empty, which is shown in the dashboards.&lt;/p&gt;

&lt;h4 id="raw-and-derived-table-schemas"&gt;Raw and derived table schemas&lt;/h4&gt;

&lt;p&gt;The first write creates the raw &lt;code class="language-markup"&gt;building_telemetry&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;The plugin creates &lt;code class="language-markup"&gt;iaq_assessment&lt;/code&gt;:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Columns&lt;/th&gt;
    &lt;th&gt;Stored as&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;building, floor, zone, sensor_id&lt;/td&gt;
    &lt;td&gt;Tags&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;temperature_c, humidity_pct, pm25_ug_m3, power_kw&lt;/td&gt;
    &lt;td&gt;Float fields&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;co2_ppm, occupancy, capacity&lt;/td&gt;
    &lt;td&gt;Integer fields&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;time&lt;/td&gt;
    &lt;td&gt;Timestamp&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Keeping the raw and derived data separate makes the model easy to audit. You can inspect the original readings without reverse-engineering a transformed table, while Grafana can query the already classified states.&lt;/p&gt;

&lt;h4 id="test-the-plugin-before-connecting-it-to-writes"&gt;Test the Plugin Before Connecting it to Writes&lt;/h4&gt;

&lt;p&gt;InfluxDB can run a WAL plugin against a sample row without committing its output. This is the quickest way to catch syntax errors or confirm the scoring logic. The &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/get-started/process/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;Processing Engine testing guide&lt;/a&gt; describes this behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run&lt;/strong&gt;:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 test wal_plugin \
  --database "$INFLUXDB_DATABASE" \
  --lp "building_telemetry,building=test,floor=1,zone=test,sensor_id=test temperature_c=22.0,humidity_pct=45.0,co2_ppm=1200i,pm25_ug_m3=10.0,power_kw=2.0,occupancy=4i,capacity=10i 1786406400000000000" \
  iaq_assessment.py&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Expected&lt;/strong&gt;: one derived &lt;code class="language-markup"&gt;iaq_assessment&lt;/code&gt; line, no errors, risk score 54, severity &lt;code class="language-markup"&gt;high&lt;/code&gt;, and recommendation &lt;code class="language-markup"&gt;increase_ventilation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The risk is 54 because CO₂ contributes 50 points, PM2.5 contributes 4, and temperature and humidity add zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake to avoid&lt;/strong&gt;: The final argument is relative to the server’s &lt;code class="language-markup"&gt;--plugin-dir&lt;/code&gt;, not your terminal’s current directory. Because the server started with plugins as its plugin directory, the correct value is &lt;code class="language-markup"&gt;iaq_assessment.py&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id="create-the-trigger-and-ingest-sample-data"&gt;Create the Trigger and Ingest Sample Data&lt;/h4&gt;

&lt;p&gt;Create a WAL trigger that watches only the raw table:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;export INFLUXDB_TRIGGER="smart_building_iaq_assessment"
influxdb3 create trigger \
  --database "$INFLUXDB_DATABASE" \
  --path iaq_assessment.py \
  --trigger-spec "table:building_telemetry" \
  --error-behavior disable \
  "$INFLUXDB_TRIGGER"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The table scope is important. The plugin writes to &lt;code class="language-markup"&gt;iaq_assessment&lt;/code&gt;; because the trigger watches only &lt;code class="language-markup"&gt;building_telemetry&lt;/code&gt;, those derived writes cannot invoke the plugin recursively. The code also ignores batches from other tables as a second guard.&lt;/p&gt;

&lt;p&gt;Write all 12,096 source rows:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 write \
  --database "$INFLUXDB_DATABASE" \
  --precision s \
  --file smart-building.lp&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;InfluxDB acknowledges the source write separately from completing asynchronous Processing Engine work. Poll the derived count for up to 60 seconds:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;derived_rows=0
for attempt in $(seq 1 60); do
  derived_rows=$(
    influxdb3 query \
      --database "$INFLUXDB_DATABASE" \
      --format csv \
      "SELECT COUNT(*) AS derived_rows FROM iaq_assessment" |
      tail -n 1
  )

  if [[ "$derived_rows" == "12096" ]]; then
    break
  fi

  sleep 1
done

printf 'Derived rows: %s\n' "$derived_rows"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Expected:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;Derived rows: 12096&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the count does not reach 12,096, do not continue to Grafana yet. Review the troubleshooting section and inspect the InfluxDB terminal for Processing Engine errors.&lt;/p&gt;

&lt;p&gt;Disable the trigger after enrichment finishes:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 disable trigger \
  --database "$INFLUXDB_DATABASE" \
  "$INFLUXDB_TRIGGER"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In a real streaming application, you would normally leave a healthy trigger enabled so future writes are enriched.&lt;/p&gt;

&lt;h4 id="query-the-results-with-sql"&gt;Query the Results with SQL&lt;/h4&gt;

&lt;p&gt;Start by validating the raw table:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 query \
  --database "$INFLUXDB_DATABASE" \
  "SELECT
     COUNT(*) AS source_rows,
     COUNT(DISTINCT building) AS buildings,
     COUNT(DISTINCT zone) AS zones,
     MIN(time) AS first_time,
     MAX(time) AS last_time
   FROM building_telemetry"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Expected values:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;source_rows: 12096
buildings: 2
zones: 6
first_time: 2026-08-10T00:00:00
last_time: 2026-08-16T23:55:00&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now summarize the derived table:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 query \
  --database "$INFLUXDB_DATABASE" \
  "SELECT
     COUNT(*) AS derived_rows,
     MIN(risk_score) AS min_risk,
     MAX(risk_score) AS max_risk,
     SUM(
       CASE
         WHEN severity IN ('high', 'critical') AND occupied THEN 1
         ELSE 0
       END
     ) AS unhealthy_occupied_intervals,
     ROUND(SUM(energy_waste_kw) * 5.0 / 60.0, 2) AS avoidable_kwh
   FROM iaq_assessment"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Expected values:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;derived_rows: 12096
min_risk: 0
max_risk: 71
unhealthy_occupied_intervals: 62
avoidable_kwh: 9.29&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, find the CO₂ hotspots and empty-zone consumption:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 query \
  --database "$INFLUXDB_DATABASE" \
  "SELECT
     building,
     zone,
     MAX(co2_ppm) AS peak_co2_ppm,
     ROUND(AVG(pm25_ug_m3), 2) AS avg_pm25_ug_m3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first two rows should be &lt;code class="language-markup"&gt;hq/conference_a&lt;/code&gt; at 1,770 ppm and &lt;code class="language-markup"&gt;annex/design_studio&lt;/code&gt; at 1,572 ppm. The &lt;code class="language-markup"&gt;hq/lab&lt;/code&gt; row should have the largest raw empty-zone consumption at 61.22 kWh.&lt;/p&gt;

&lt;h2 id="installing-grafana"&gt;Installing Grafana&lt;/h2&gt;

&lt;p&gt;Install Grafana OSS from Grafana Labs’ signed APT repository:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;sudo apt-get install -y apt-transport-https wget gnupg

sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc \
  https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc

echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" |
  sudo tee /etc/apt/sources.list.d/grafana.list

sudo apt-get update
sudo apt-get install -y grafana&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start Grafana and enable it at boot:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;sudo systemctl daemon-reload
sudo systemctl enable --now grafana-server.service
sudo systemctl status --no-pager grafana-server.service&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; in your browser. On a new installation, the default username and password are both admin; Grafana immediately prompts you to choose a new password.&lt;/p&gt;

&lt;p&gt;Use a unique local password supplied as &lt;code class="language-markup"&gt;$GRAFANA_ADMIN_PASSWORD&lt;/code&gt;. Never reuse the InfluxDB token as a Grafana password.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/23hMx8HAxiYLKCbq8davHr/ec7c0d5bd384ab4c367bde047d86da68/0dc52148-672c-40bf-90ec-e6b6df97cabc.png" alt="InfluxDB 3 and Grafana 1" /&gt;&lt;/p&gt;

&lt;h4 id="create-a-read-only-token-for-grafana"&gt;Create a Read-Only Token for Grafana&lt;/h4&gt;

&lt;p&gt;Grafana only needs to query this one database. It should not receive the administrator token used for setup.&lt;/p&gt;

&lt;p&gt;In the terminal where &lt;code class="language-markup"&gt;INFLUXDB3_AUTH_TOKEN&lt;/code&gt; is still set, run:&lt;/p&gt;

&lt;p&gt;influxdb3 create token \
  –permission “db:smart_building:read” \
  –name “Grafana read-only access to smart_building”&lt;/p&gt;

&lt;p&gt;Store the returned value in your password manager as &lt;code class="language-markup"&gt;$INFLUXDB_GRAFANA_TOKEN&lt;/code&gt;. InfluxDB’s &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/admin/tokens/resource/create/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;resource-token documentation&lt;/a&gt; explains the &lt;code class="language-markup"&gt;db:"database":read&lt;/code&gt; permission format.&lt;/p&gt;

&lt;p&gt;Do not paste this token into a text file or include it in a screenshot. Enter it once in Grafana’s secure token field.&lt;/p&gt;

&lt;h4 id="connect-grafana-to-influxdb-3"&gt;Connect Grafana to InfluxDB 3&lt;/h4&gt;

&lt;p&gt;In Grafana:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open &lt;strong&gt;Connections → datasources&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Add new datasource&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;InfluxDB&lt;/strong&gt;. This is the built-in datasource; do not install another plugin.&lt;/li&gt;
  &lt;li&gt;Enter &lt;code class="language-markup"&gt;InfluxDB 3 Smart Building&lt;/code&gt; as the name.&lt;/li&gt;
  &lt;li&gt;Configure the connection:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Setting&lt;/th&gt;
    &lt;th&gt;Value&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;URL&lt;/td&gt;
    &lt;td&gt;http://127.0.0.1:8181&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Query language&lt;/td&gt;
    &lt;td&gt;SQL&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Database&lt;/td&gt;
    &lt;td&gt;smart_building&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Token&lt;/td&gt;
    &lt;td&gt;The securely stored $INFLUXDB_GRAFANA_TOKEN value&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Insecure Connection&lt;/td&gt;
    &lt;td&gt;Enabled&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Click &lt;strong&gt;Save &amp;amp; test&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SQL uses Flight SQL over gRPC. Because both programs are on the same machine and the URL uses local HTTP without TLS, &lt;strong&gt;Insecure Connection&lt;/strong&gt; is required. This setting disables gRPC TLS; it is not a recommendation for traffic crossing an untrusted network.&lt;/p&gt;

&lt;p&gt;The URL is interpreted by the Grafana server, not by your browser. &lt;code class="language-markup"&gt;127.0.0.1&lt;/code&gt; works here because Grafana and InfluxDB run on the same machine. If you later move either service into a container or another machine, update the URL to an address reachable from the Grafana backend while keeping the connection private.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/1my3gCZyflm5Y4KeiqZ6CI/767f7fd0bc9a6186cd4a9e0cc685c135/2fbe33f9-0c51-4b9b-bb21-e50f656579fa.png" alt="InfluxDB 3 and Grafana 2" /&gt;&lt;/p&gt;

&lt;h4 id="create-the-grafana-dashboard-and-variables"&gt;Create the Grafana Dashboard and Variables&lt;/h4&gt;

&lt;p&gt;Create the dashboard first:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open &lt;strong&gt;Dashboards&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;New → New Dashboard&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Save it as &lt;strong&gt;Smart Building Operations&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Set the dashboard timezone to &lt;strong&gt;UTC&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Set the absolute time range from &lt;code class="language-markup"&gt;2026-08-10 00:00:00&lt;/code&gt; through &lt;code class="language-markup"&gt;2026-08-17 00:00:00&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The fixed range matters because this is historical data generated in a specific time range. A relative range such as “Last 7 days” will not include it.&lt;/p&gt;

&lt;h4 id="add-the-building-variable"&gt;Add the Building Variable&lt;/h4&gt;

&lt;p&gt;While editing the dashboard, click &lt;strong&gt;Add → Variable&lt;/strong&gt; and configure:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Option&lt;/th&gt;
    &lt;th&gt;Value&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;Variable type&lt;/td&gt;
    &lt;td&gt;Query&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Name&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;building&lt;/code&gt;&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Label&lt;/td&gt;
    &lt;td&gt;Building&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;datasource&lt;/td&gt;
    &lt;td&gt;InfluxDB 3 Smart Building&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Multi-value&lt;/td&gt;
    &lt;td&gt;Enabled&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Include All&lt;/td&gt;
    &lt;td&gt;Enabled&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Use this SQL query:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT DISTINCT
  building AS __text,
  building AS __value
FROM building_telemetry
ORDER BY building&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The preview should show &lt;code class="language-markup"&gt;annex&lt;/code&gt; and &lt;code class="language-markup"&gt;hq&lt;/code&gt;. Save the variable.&lt;/p&gt;

&lt;h4 id="add-the-zone-variable"&gt;Add the Zone Variable&lt;/h4&gt;

&lt;p&gt;Add another query variable:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Option&lt;/th&gt;
    &lt;th&gt;Value&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;Name&lt;/td&gt;
    &lt;td&gt;&lt;code&gt;zone&lt;/code&gt;&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Label&lt;/td&gt;
    &lt;td&gt;Zone&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;datasource&lt;/td&gt;
    &lt;td&gt;InfluxDB 3 Smart Building&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Multi-value&lt;/td&gt;
    &lt;td&gt;Enabled&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Include All&lt;/td&gt;
    &lt;td&gt;Enabled&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Use:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT DISTINCT
  zone AS __text,
  zone AS __value
FROM building_telemetry
WHERE building IN (${building:sqlstring})
ORDER BY zone&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a chained variable: changing Building refreshes the relevant Zone choices. For multi-value SQL variables, use &lt;code class="language-markup"&gt;IN&lt;/code&gt; and &lt;code class="language-markup"&gt;${variable:sqlstring}&lt;/code&gt;. Grafana supplies correctly quoted SQL strings; do not add another pair of quotes around the variable. See &lt;a href="https://grafana.com/docs/grafana/latest/datasources/influxdb/template-variables/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;InfluxDB template variables&lt;/a&gt; for more examples.&lt;/p&gt;

&lt;p&gt;Set both variables to &lt;strong&gt;All&lt;/strong&gt; before creating the panels.&lt;/p&gt;

&lt;h2 id="building-the-grafana-dashboard-panels"&gt;Building the Grafana dashboard panels&lt;/h2&gt;

&lt;p&gt;For each panel, click &lt;strong&gt;Add → Visualization,&lt;/strong&gt; select &lt;strong&gt;InfluxDB 3 Smart Building&lt;/strong&gt;, switch the query 
editor to SQL code mode if necessary, paste the query, choose the indicated format and visualization, then apply the panel.&lt;/p&gt;

&lt;p&gt;Every query uses two Grafana features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;$__timeFilter(time)&lt;/code&gt; expands to the dashboard’s active time range.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;${building:sqlstring}&lt;/code&gt; and &lt;code class="language-markup"&gt;${zone:sqlstring}&lt;/code&gt; expand the multi-select variables into quoted SQL values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grafana documents the available SQL macros in its &lt;a href="https://grafana.com/docs/grafana/latest/datasources/influxdb/query-editor/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;InfluxDB query editor reference&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id="peak-co-dashboard"&gt;Peak CO₂ Dashboard&lt;/h4&gt;

&lt;p&gt;This stat answers, “What was the highest CO₂ reading in the selected scope?”&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT MAX(co2_ppm) AS "Peak CO₂ ppm"
FROM building_telemetry
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Stat&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Unit: &lt;strong&gt;ppm&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Peak CO₂&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all buildings and zones selected, expect 1,770 ppm.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/5Jlq6geUXEzBO7Iaj0Xl7r/6fdb2591da2147c97b38e9f90322a637/c639889b-2964-473d-b275-d62bc14ec884.png" alt="InfluxDB 3 and Grafana 3" /&gt;&lt;/p&gt;

&lt;p&gt;Keep the color neutral unless you have documented, context-appropriate CO₂ thresholds. A peak value alone does not describe duration, sensor accuracy, or local health policy.&lt;/p&gt;

&lt;h4 id="unhealthy-occupied-intervals-dashboard"&gt;Unhealthy Occupied Intervals Dashboard&lt;/h4&gt;

&lt;p&gt;This panel counts five-minute zone intervals that the plugin classified as high or critical while someone was present:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT COUNT(*) AS "Unhealthy occupied intervals"
FROM iaq_assessment
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
  AND severity IN ('high', 'critical')
  AND occupied = true&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Stat&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Unit: short&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Unhealthy occupied intervals&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expected result: 62. This means 62 five-minute zone records, not 62 separate incidents.&lt;/p&gt;

&lt;h4 id="energy-while-empty-dashboard"&gt;Energy While Empty Dashboard&lt;/h4&gt;

&lt;p&gt;The source cadence is five minutes, so kWh for one row is &lt;code class="language-markup"&gt;power_kw × 5/60&lt;/code&gt;. This query integrates every interval with zero occupancy:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT ROUND(
  SUM(
    CASE
      WHEN occupancy = 0 THEN power_kw * 5.0 / 60.0
      ELSE 0
    END
  ),
  2
) AS "Empty-zone kWh"
FROM building_telemetry
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Stat&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Unit: &lt;strong&gt;kWh&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Energy while empty&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="co-by-zone-dashboard"&gt;CO₂ by Zone Dashboard&lt;/h4&gt;

&lt;p&gt;A raw five-minute series is readable, but a 15-minute average makes the incident shape easier to compare across zones:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  date_bin(
    INTERVAL '15 minutes',
    time,
    TIMESTAMP '1970-01-01T00:00:00Z'
  ) AS time,
  zone,
  AVG(co2_ppm) AS "CO₂ ppm"
FROM building_telemetry
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
GROUP BY 1, 2
ORDER BY 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Time series&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Time series&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Unit: ppm&lt;/li&gt;
  &lt;li&gt;Legend: table, with maximum displayed&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;CO₂ by zone&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result contains 672 series rows: 112 quarter-hour bins × six zones. A time series query must return a timestamp column; naming it time keeps the Grafana mapping straightforward.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/26p6fhSvn9DqLvFPSMbmqN/ef63172e1dc7bbdc1ca706344ae46a82/63d5c6b0-60b9-4b89-8972-e155d9fdc855.png" alt="InfluxDB 3 and Grafana 4" /&gt;&lt;/p&gt;

&lt;h4 id="indoor-air-quality-state-dashboard"&gt;Indoor Air Quality State Dashboard&lt;/h4&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT time, zone, severity
FROM iaq_assessment
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
ORDER BY time&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;State timeline&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Time series&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Indoor-air-quality state&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add value mappings:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Value&lt;/th&gt;
    &lt;th&gt;Label&lt;/th&gt;
    &lt;th&gt;Suggested color&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;normal&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;Normal&lt;/td&gt;
    &lt;td&gt;Green&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;moderate&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;Moderate&lt;/td&gt;
    &lt;td&gt;Yellow&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;high&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;High&lt;/td&gt;
    &lt;td&gt;Orange&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;critical&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;Critical&lt;/td&gt;
    &lt;td&gt;Red&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;h4 id="wasted-energy-dashboard"&gt;Wasted Energy Dashboard&lt;/h4&gt;

&lt;p&gt;The plugin’s energy signal shows how much power exceeded the 1.5 kW baseline while the zone was empty.&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  date_bin(
    INTERVAL '15 minutes',
    time,
    TIMESTAMP '1970-01-01T00:00:00Z'
  ) AS time,
  zone,
  AVG(energy_waste_kw) AS "Excess kW wasted"
FROM iaq_assessment
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
GROUP BY 1, 2
ORDER BY 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Time series&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Time series&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Draw style: bars&lt;/li&gt;
  &lt;li&gt;Stacking: normal&lt;/li&gt;
  &lt;li&gt;Unit: &lt;code class="language-markup"&gt;kW&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Energy wasted&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The derived weekly total is 9.29 kWh, concentrated in the lab’s Wednesday evening event. Panel 3’s 316.04 kWh and this 9.29 kWh are intentionally different—the second number is more conservative.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/5FJh5uuI4xD5lNIsypoI1L/7f57e792c902d8637c01c7f2206c8ff4/957a8adc-6ea9-4ec8-89f2-be452b3d0a0f.png" alt="InfluxDB 3 and Grafana 5" /&gt;&lt;/p&gt;

&lt;h4 id="risk-by-zone-dashboard"&gt;Risk by Zone Dashboard&lt;/h4&gt;

&lt;p&gt;This query compares each zone’s normal operating level with its worst interval:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  zone,
  ROUND(AVG(risk_score), 1) AS "Average risk",
  MAX(risk_score) AS "Peak risk"
FROM iaq_assessment
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
GROUP BY zone
ORDER BY "Peak risk" DESC&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Bar gauge&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Minimum: &lt;strong&gt;0&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Maximum: &lt;strong&gt;100&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Unit: &lt;strong&gt;none&lt;/strong&gt;—the score is bounded from 0 to 100 but is not a percentage&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Risk by zone&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/2yof0RI2DgWBhP4fw5K118/54fa0fdf1874038d7f36f2bd4f0a3fb3/46780643-dcb0-46ad-aa99-6345b4442361.png" alt="InfluxDB 3 and Grafana 6" /&gt;&lt;/p&gt;

&lt;h4 id="operational-hotspots-dashboard"&gt;Operational Hotspots Dashboard&lt;/h4&gt;

&lt;p&gt;Finish with a table that puts ventilation and energy context together:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  building,
  zone,
  MAX(co2_ppm) AS peak_co2_ppm,
  ROUND(AVG(pm25_ug_m3), 1) AS avg_pm25,
  ROUND(
    SUM(CASE WHEN occupancy = 0 THEN power_kw * 5.0 / 60.0 ELSE 0 END),
    2
  ) AS empty_zone_kwh
FROM building_telemetry
WHERE $__timeFilter(time)
  AND building IN (${building:sqlstring})
  AND zone IN (${zone:sqlstring})
GROUP BY building, zone
ORDER BY peak_co2_ppm DESC&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Configure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Visualization: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;strong&gt;Table&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Sort: &lt;code class="language-markup"&gt;peak_co2_ppm&lt;/code&gt;, descending&lt;/li&gt;
  &lt;li&gt;Title: &lt;strong&gt;Operational hotspots&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The table should contain six rows. conference_a ranks first for CO₂, while the lab leads in raw empty-zone energy, a useful reminder that air quality and energy efficiency reveal different operational problems. Save the dashboard after adding all eight panels.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/6YJqNgPGe7DkBc6PTWEr0Q/f4b74aeb992426952ac223247b8c0e7d/0ea255c9-2f4e-44e6-9ed3-bf34a6592cd1.png" alt="InfluxDB 3 and Grafana 7" /&gt;&lt;/p&gt;

&lt;h2 id="where-to-take-the-project-next"&gt;Where to take the project next?&lt;/h2&gt;

&lt;p&gt;You now have a complete InfluxDB 3 and Grafana stack running locally. Telemetry is written as line protocol, with a Python plugin enriching every row as it lands, a derived table storing decisions rather than measurements, and a Grafana dashboard querying both.
The pattern is worth more than just this project. The plugin is roughly fifty lines of standard-library Python. The trigger is table-scoped so it cannot recurse. The raw table is untouched, so if you decide next month that 800 ppm was the wrong CO₂ threshold, you can rewrite the scoring and recompute from source. And because the enrichment happens once at write time, every dashboard panel stays a short, readable query.&lt;/p&gt;

&lt;h4 id="grafana-features-to-try"&gt;Grafana Features to Try&lt;/h4&gt;

&lt;p&gt;You built eight panels in this tutorial, but that’s only a fraction of what Grafana can do:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Transformations&lt;/strong&gt; - Join &lt;code class="language-markup"&gt;building_telemetry&lt;/code&gt; and &lt;code class="language-markup"&gt;iaq_assessment&lt;/code&gt; on time and zone, then use Organize fields and Add field from calculation to build a combined table without writing a more complex SQL query.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Data links and drill-downs&lt;/strong&gt; - Turn each row of the Operational hotspots table into a link that opens a zone-scoped view with the zone variable pre-set. It’s a two-minute change that makes the dashboard feel like an application.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ad hoc filters&lt;/strong&gt; - An ad hoc filter variable lets viewers add their own building = hq style constraints at view time without editing any query.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Canvas and Geomap&lt;/strong&gt; - Canvas can overlay live severity colors on a floor plan image; Geomap does the same on a real map if your zones carry coordinates. Both are far more legible to non-technical stakeholders than a time series.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Explore mode&lt;/strong&gt; - Prototype SQL against the datasource without creating a panel first—the fastest way to iterate on a query.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dashboards as code&lt;/strong&gt; - Export the dashboard JSON and check it into version control, or use Grafana provisioning to deploy the datasource and dashboard together on a fresh machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="influxdb-3-features-to-try"&gt;InfluxDB 3 Features to Try&lt;/h4&gt;

&lt;p&gt;The Processing Engine plugin you wrote is one trigger type among several, and InfluxDB has caching and lifecycle features that pair directly with what you just built:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Scheduled triggers&lt;/strong&gt; - WAL triggers run inline on the write path, so they should stay cheap. A scheduled trigger runs on an interval or cron expression instead. These are great for hourly and daily rollups, anomaly detection over a window, or anything that needs to look across many rows rather than one.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rollup tables&lt;/strong&gt; - Have a scheduled trigger write a &lt;code class="language-markup"&gt;zone_hourly&lt;/code&gt; summary. Dashboards over long ranges then read a small pre-aggregated table instead of scanning weeks of five-minute data.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;HTTP request triggers&lt;/strong&gt; - Expose a plugin at an endpoint, and you have a small API—a current-status lookup for a room display, or a webhook that returns the top three zones to investigate.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Last Value Cache&lt;/strong&gt; - Keeps the most recent value per series in memory for very fast lookups, which suits “current state” stat panels and real-time displays far better than a &lt;code class="language-markup"&gt;MAX(time)&lt;/code&gt; subquery.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Distinct Value Cache&lt;/strong&gt; - Speeds up exactly the kind of query your Building and Zone template variables run, like &lt;code class="language-markup"&gt;SELECT DISTINCT&lt;/code&gt; over a tag column. On a large deployment, this is the difference between dashboards that load instantly and dashboards that stall on variable refresh.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Retention periods&lt;/strong&gt; - Set a retention period on the database so raw five-minute data expires while your rollup table keeps the long history. This is the standard way to control storage cost in time series systems.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;InfluxDB 3 Explorer&lt;/strong&gt; - A browser UI for browsing databases and prototyping queries, handy when you want to check a table’s shape without dropping into the CLI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="get-started-with-influxdb-3-and-grafana"&gt;Get started with InfluxDB 3 and Grafana&lt;/h2&gt;

&lt;p&gt;If you have not set up InfluxDB 3 yet, start a free InfluxDB 3 Enterprise trial or download InfluxDB 3 Core from the InfluxData downloads page, then follow the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/install/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;installation guide&lt;/a&gt;. The full Processing Engine reference is in the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;InfluxDB 3 documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Built something with this pattern? A different plugin, a better scoring model, a dashboard for a domain we didn’t think of? Share it in the &lt;a href="https://community.influxdata.com/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=getting_started_influxdb_3_grafana&amp;amp;utm_content=blog"&gt;InfluxData Community forums&lt;/a&gt;. Questions are welcome there too.&lt;/p&gt;
</description>
      <pubDate>Wed, 02 Sep 2026 07:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/getting-started-influxdb-3-grafana/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/getting-started-influxdb-3-grafana/</guid>
      <category>Getting Started</category>
      <category>Developer</category>
      <author>Charles Mahler (InfluxData)</author>
    </item>
    <item>
      <title>Telegraf Controller 1.1: Make Fleet-Wide Config Changes with a Single Edit</title>
      <description>&lt;p&gt;Telegraf is easy to stand up. You just need a config file with a few plugins and data flows. That ease is why Telegraf fleets grow fast, and why managing the agents that collect data becomes more difficult than collecting it. A change that takes minutes on ten agents takes days across a few hundred. Telegraf Controller is a centralized control plane for managing, configuring, and monitoring your fleet of Telegraf agents.&lt;/p&gt;

&lt;p&gt;Telegraf Controller 1.1 is available today and addresses that management problem from two sides: keeping the Controller layer itself reliable, and making fleet-wide changes without touching every config. The release adds high availability, configuration versioning, global constants, configuration groups, and aliases.&lt;/p&gt;

&lt;h2 id="high-availability-telegraf-enterprise"&gt;High availability (Telegraf Enterprise)&lt;/h2&gt;

&lt;p&gt;High availability lets you run multiple Controller instances from a shared database with automatic failover. If an instance goes down, agents keep pulling configuration and reporting health through the remaining instances.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/39kyCk4mF4jPhZihpQEirg/2c54ea056b699631c5380005a5686dcd/da520a75-dae5-4f92-85ec-f23ebd5579c7.png" alt="Telegraf Controller 1.1 graphic 1" /&gt;&lt;/p&gt;

&lt;p&gt;For production monitoring, compliance systems, and other mission-critical pipelines, the configuration layer needs to meet the same reliability requirements as the rest of the stack. High availability also simplifies planned maintenance: one instance can go offline for patching and the config path stays open. In strict environments that require redundancy, it also removes Controller as a single point of failure.&lt;/p&gt;

&lt;h2 id="configuration-versioning"&gt;Configuration versioning&lt;/h2&gt;

&lt;p&gt;Configuration versioning saves every configuration change as a new version, including who made it. If an edit causes errors, compare the current version against the previous one in a side-by-side, color-coded diff and revert.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/Jky3U3WlEzmdvEWtySoM1/3845319032a3fc7472af30c1200ca2ca/b67ee017-6cdb-499b-bba3-bf6d676bd2d9.png" alt="Telegraf Controller 1.1 graphic 2" /&gt;&lt;/p&gt;

&lt;p&gt;Configuration history also provides an  audit trail. Teams can trace when individual settings changed and test tuning changes without losing the last working configuration.&lt;/p&gt;

&lt;h2 id="global-constants"&gt;Global constants&lt;/h2&gt;

&lt;p&gt;Global constants let you define a value once (an output endpoint, a collection interval, a region name) and reference it from any configuration; updating the constant updates every configuration that references it.&lt;/p&gt;

&lt;p&gt;Reference a constant anywhere in a configuration with the &lt;code class="language-markup"&gt;::{constant_name}&lt;/code&gt; syntax. When Controller serves a configuration to an agent, it resolves each reference to the constant’s current value:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;[agent]
  interval = "::{collection_interval}"

[[outputs.influxdb_v3]]
  urls = ["::{influxdb_url}"]
  token = "${INFLUX_TOKEN}"
  database = "::{metrics_database}"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The immediate benefit is consistency: shared values live in one place instead of being duplicated across dozens of configurations and drifting out of sync. Constants also simplify migrations: moving your fleet to a new output endpoint or standardizing collection intervals across teams becomes one edit instead of a config-by-config sweep.&lt;/p&gt;

&lt;h2 id="configuration-groups"&gt;Configuration groups&lt;/h2&gt;

&lt;p&gt;Configuration groups bundle multiple configurations into an ordered unit that agents retrieve in a single request. Instead of assigning each agent its own stack of individual configs, you assign the group.&lt;/p&gt;

&lt;p&gt;Groups compose by reference, not by copy. A config can belong to multiple groups, and editing a config propagates the change to every group that includes it.&lt;/p&gt;

&lt;p&gt;That’s what makes groups work as role profiles. Build one base config with the system inputs every host runs, then compose it with the configs specific to a web server or a database host. Tune the base config once and every role updates without maintaining separate copies that can drift. Groups also serve as templates. A group proven in production becomes the starting point for the next site or deployment, instead of another copy-paste job.&lt;/p&gt;

&lt;h2 id="configuration-aliases"&gt;Configuration aliases&lt;/h2&gt;

&lt;p&gt;Configuration aliases let you use custom, human-readable identifiers for configs in both the UI and the API. Agents request &lt;code class="language-markup"&gt;web-baseline&lt;/code&gt; instead of a UUID, so when it’s time to move agents to a different config, simply move the alias and the fleet cuts over on the next pull.&lt;/p&gt;

&lt;p&gt;If you are running multiple Controllers use a single alias to reference the same config on each instance rather than config UUIDs, which are unique to each Controller.&lt;/p&gt;

&lt;h2 id="one-change-fleet-wide"&gt;One change, fleet-wide&lt;/h2&gt;

&lt;p&gt;Constants, groups, and aliases are each useful on their own, but they’re designed to work together. If every site’s agents request the same group by alias, and that group’s configurations pull shared values from global constants, a fleet-wide change—a new endpoint or interval—is a single edit. Every site picks it up on its next config pull.&lt;/p&gt;

&lt;h2 id="whats-free-and-what-requires-telegraf-enterprise"&gt;What’s free and what requires Telegraf Enterprise&lt;/h2&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Feature&lt;/th&gt;
    &lt;th&gt;Free&lt;/th&gt;
    &lt;th&gt;Enterprise&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;High availability&lt;/td&gt;
    &lt;td style="color:#e02b2b; font-weight:bold;"&gt;No&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Configuration versioning&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Configuration aliases&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Global constants&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Configuration groups&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
    &lt;td style="color:#6a9a4d; font-weight:bold;"&gt;Yes&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;h2 id="get-started"&gt;Get started&lt;/h2&gt;

&lt;p&gt;The value of Telegraf Controller 1.1 is a config path that stays up and is far less work to keep the fleet current. High availability maintains uptime through instance failures, so the layer serving your configs meets the same reliability as the rest of your stack. Shared values are defined once instead of copied across every config, and config changes reach the whole fleet on the next pull with a record of what moved and an easy revert if something went wrong.&lt;/p&gt;

&lt;p&gt;Telegraf Controller 1.1 is available today. &lt;a href="https://docs.influxdata.com/telegraf/controller/install/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_controller_1_1&amp;amp;utm_content=blog"&gt;Download and install Telegraf Controller&lt;/a&gt; for free or &lt;a href="https://www.influxdata.com/contact-sales-telegraf-enterprise/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=telegraf_controller_1_1&amp;amp;utm_content=blog"&gt;contact us about Telegraf Enterprise&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;

&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What's new in Telegraf Controller 1.1?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Telegraf Controller 1.1 adds high availability (Enterprise only), configuration versioning, global constants, configuration groups, and configuration aliases. Together, these allow a single edit, like a new output endpoint or collection interval, propagate across an entire fleet on the next agent config pull.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How does high availability work in Telegraf Controller?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                High availability lets you run multiple Telegraf Controller instances against a shared database with automatic failover. If one instance goes down, agents keep pulling their configuration and reporting health through the remaining instances.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How do global constants work in Telegraf Controller?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Global constants let you define a value once (i.e. an output endpoint, collection interval, or region name) and reference it from any configuration using ::{constant_name} syntax. When Controller serves a config to an agent, it resolves the reference to the constant's current value, so updating the constant updates every configuration that uses it.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;
</description>
      <pubDate>Tue, 25 Aug 2026 00:25:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/telegraf-controller-1-1/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/telegraf-controller-1-1/</guid>
      <category>Product</category>
      <category>Developer</category>
      <author>Scott Anderson (InfluxData)</author>
    </item>
    <item>
      <title>A Guide to Downsampling Time Series Data with InfluxDB 3</title>
      <description>&lt;p&gt;This tutorial demonstrates both approaches using the InfluxDB 3 Processing Engine’s built-in bird tracking simulator plugin. You will generate telemetry, aggregate it into 10-second windows, and validate the result with SQL. The same pattern works for infrastructure metrics, industrial sensors, application telemetry, and other time series workloads.&lt;/p&gt;

&lt;h2 id="why-downsample-time-series-data"&gt;Why downsample time series data?&lt;/h2&gt;

&lt;p&gt;High-resolution data is valuable while diagnosing a recent event, but its value often changes as it ages. A temperature reading collected every second may be useful for an active incident, while a daily report may only need 10-minute or hourly averages.&lt;/p&gt;

&lt;p&gt;Downsampling helps you:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;scan fewer rows in long-range queries&lt;/li&gt;
  &lt;li&gt;make dashboards over weeks or months more responsive&lt;/li&gt;
  &lt;li&gt;retain useful historical trends at a lower resolution, reducing storage costs&lt;/li&gt;
  &lt;li&gt;calculate common summaries once instead of repeating the work&lt;/li&gt;
  &lt;li&gt;keep raw data only for as long as its full resolution is useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downsampling is not the same as deleting raw data; it creates a summarized view or table at a lower granularity. Retention is a separate decision and process where data is fully deleted after a set period of time.&lt;/p&gt;

&lt;h4 id="choose-query-time-or-persisted-downsampling"&gt;Choose Query-Time or Persisted Downsampling&lt;/h4&gt;

&lt;p&gt;InfluxDB 3 gives you two practical patterns for downsampling:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Approach&lt;/th&gt;
    &lt;th&gt;How it works&lt;/th&gt;
    &lt;th&gt;Best for&lt;/th&gt;
    &lt;th&gt;Main tradeoff&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;Query-time SQL&lt;/td&gt;
    &lt;td&gt;Uses &lt;code&gt;DATE_BIN&lt;/code&gt; and aggregate functions in a &lt;code&gt;SELECT&lt;/code&gt; query&lt;/td&gt;
    &lt;td&gt;Exploration, flexible dashboards, and changing aggregation requirements&lt;/td&gt;
    &lt;td&gt;Recomputes the result each time and does not reduce stored raw data&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Python Processing Engine&lt;/td&gt;
    &lt;td&gt;Runs the official downsampler plugin on a schedule and writes aggregate rows to a target table&lt;/td&gt;
    &lt;td&gt;Repeated long-range queries, predictable rollups, and tiered retention&lt;/td&gt;
    &lt;td&gt;Requires you to choose aggregates and scheduling behavior in advance&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;A useful starting point is to develop and validate an aggregate in SQL. If the same query becomes a frequent or expensive workload, then use the downsampler plugin.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;InfluxDB 3 Core or Enterprise with the Processing Engine enabled&lt;/li&gt;
  &lt;li&gt;InfluxDB 3 CLI installed and connected to the server&lt;/li&gt;
  &lt;li&gt;An authorization token with permission to create a database and triggers&lt;/li&gt;
  &lt;li&gt;Outbound access to GitHub so InfluxDB can load the official plugins referenced by gh: paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The examples assume a database named &lt;code class="language-markup"&gt;bird_demo&lt;/code&gt;:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 create database bird_demo&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you are using the local Docker environment that accompanies this article, InfluxDB is available at &lt;code class="language-markup"&gt;http://localhost:8181&lt;/code&gt;. InfluxDB 3 Explorer is available separately at &lt;code class="language-markup"&gt;http://localhost:8888&lt;/code&gt; for running the SQL and capturing visual results.&lt;/p&gt;

&lt;h4 id="generate-sample-telemetry-with-the-official-bird-simulator"&gt;Generate Sample Telemetry with the Official Bird Simulator&lt;/h4&gt;

&lt;p&gt;InfluxData’s &lt;a href="https://github.com/influxdata/influxdb3_plugins/tree/main/influxdata/bird_data_simulator/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=downsampling_guide_influxdb_3&amp;amp;utm_content=blog"&gt;bird data simulator&lt;/a&gt; is a convenient fit for this tutorial. It creates repeatable time series behavior without requiring a separate data generator or hardware sensor.&lt;/p&gt;

&lt;p&gt;The plugin writes to the &lt;code class="language-markup"&gt;bird_tracking&lt;/code&gt; table. Its tags include species and name, and its fields include &lt;code class="language-markup"&gt;body_temp&lt;/code&gt;, &lt;code class="language-markup"&gt;longitude&lt;/code&gt;, &lt;code class="language-markup"&gt;latitude&lt;/code&gt;, &lt;code class="language-markup"&gt;speed&lt;/code&gt;, and &lt;code class="language-markup"&gt;heading&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, install the plugin’s &lt;code class="language-markup"&gt;Faker&lt;/code&gt; dependency:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 install package Faker&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 create trigger \
  --database bird_demo \
  --path gh:influxdata/bird_data_simulator/bird_data_simulator.py \
  --trigger-spec every:1s \
  --trigger-arguments bird_count=10 \
  bird_tracking_demo&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After the trigger has run for several seconds, confirm that data is arriving:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT *
FROM bird_tracking
ORDER BY time DESC
LIMIT 5;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At one write per bird per second, the simulator produces approximately 600 rows per minute for 10 birds.&lt;/p&gt;

&lt;h4 id="downsample-at-query-time-with-sql"&gt;Downsample at Query Time with SQL&lt;/h4&gt;

&lt;p&gt;InfluxDB 3 SQL supports time bucketing with &lt;code class="language-markup"&gt;DATE_BIN()&lt;/code&gt;. The following query groups the raw bird readings into 10-second intervals and calculates speed and body-temperature statistics for each bird:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  DATE_BIN(INTERVAL '10 seconds', time) AS time,
  species,
  name,
  AVG(speed) AS avg_speed,
  MIN(speed) AS min_speed,
  MAX(speed) AS max_speed,
  AVG(body_temp) AS avg_body_temp,
  COUNT(*) AS record_count
FROM bird_tracking
WHERE time &amp;gt;= now() - INTERVAL '2 minutes'
GROUP BY 1, species, name
ORDER BY 1 DESC, species, name;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class="language-markup"&gt;DATE_BIN&lt;/code&gt; aligns each timestamp to a 10-second boundary. &lt;code class="language-markup"&gt;GROUP BY 1&lt;/code&gt; refers to the first expression in the &lt;code class="language-markup"&gt;SELECT&lt;/code&gt; list while &lt;code class="language-markup"&gt;species&lt;/code&gt; and &lt;code class="language-markup"&gt;name&lt;/code&gt; preserve one series per bird.&lt;/p&gt;

&lt;p&gt;This is true query-time downsampling: the server returns fewer, summarized rows, but it does not write them to another table. Change the interval or aggregates whenever the question changes. For example, a long-range dashboard might replace &lt;code class="language-markup"&gt;INTERVAL '10 seconds'&lt;/code&gt; with &lt;code class="language-markup"&gt;INTERVAL '1 hour'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Always include a bounded time predicate. It reduces the data scanned and makes the query’s intended resolution explicit.&lt;/p&gt;

&lt;h4 id="persist-downsampled-data-with-the-python-processing-engine"&gt;Persist Downsampled Data with the Python Processing Engine&lt;/h4&gt;

&lt;p&gt;For an aggregate that many users or dashboards repeatedly request, calculate it on a schedule with InfluxData’s official &lt;a href="https://docs.influxdata.com/influxdb3/core/plugins/library/official/downsampler/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=downsampling_guide_influxdb_3&amp;amp;utm_content=blog"&gt;downsampler plugin&lt;/a&gt;. The plugin queries the source table, computes aggregates, and writes the results to a target table.&lt;/p&gt;

&lt;p&gt;This trigger creates one average speed and body-temperature row per bird for every 10-second interval:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3 create trigger \
  --database bird_demo \
  --path gh:influxdata/downsampler/downsampler.py \
  --trigger-spec every:10s \
  --trigger-arguments \
  'source_measurement=bird_tracking,target_measurement=bird_tracking_10s,interval=10s,window=2min,offset=10s,calculations=speed:avg.body_temp:avg,specific_fields=speed.body_temp' \
  bird_tracking_downsample&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The trigger arguments control the rollup:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;source_measurement&lt;/code&gt; is the raw source table.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;target_measurement&lt;/code&gt; is the table that receives aggregates&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;interval=10s&lt;/code&gt; defines the time-bin width.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;calculations&lt;/code&gt; assigns an aggregate function to each field&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;specific_fields&lt;/code&gt; limits processing to the fields needed by this rollup.&lt;/li&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;offset=10s&lt;/code&gt; delays the queried window so its newest bin has time to close.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id="why-the-offset-matters"&gt;Why the Offset Matters&lt;/h4&gt;

&lt;p&gt;The offset is not cosmetic. Without it, the simulator and downsampler could execute on the same 10-second scheduler boundary. Without an offset, the downsampler may query the newest bin while writes are still arriving, producing partial aggregates.&lt;/p&gt;

&lt;p&gt;With &lt;code class="language-markup"&gt;offset=10s&lt;/code&gt;, the preceding completed bin and every aggregate in the validation window should have the expected 10 source points. In production, set the offset to at least the maximum delay you expect between an event occurring and becoming queryable. Workloads with late or out-of-order data may need a larger offset and lookback window.&lt;/p&gt;

&lt;h4 id="inspect-the-persisted-result"&gt;Inspect the Persisted Result&lt;/h4&gt;

&lt;p&gt;The downsampler names calculated fields by appending the aggregate function, so &lt;code class="language-markup"&gt;speed&lt;/code&gt; becomes &lt;code class="language-markup"&gt;speed_avg&lt;/code&gt; and &lt;code class="language-markup"&gt;body_temp&lt;/code&gt; becomes &lt;code class="language-markup"&gt;body_temp_avg&lt;/code&gt;. It also writes &lt;code class="language-markup"&gt;record_count&lt;/code&gt;, &lt;code class="language-markup"&gt;time_from&lt;/code&gt;, and &lt;code class="language-markup"&gt;time_to&lt;/code&gt; metadata.&lt;/p&gt;

&lt;p&gt;The plugin stores the data with nanosecond precision and converts it with &lt;code class="language-markup"&gt;TO_TIMESTAMP_NANOS()&lt;/code&gt; when you want readable timestamps:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  time,
  species,
  name,
  ROUND(speed_avg, 2) AS avg_speed_mph,
  ROUND(body_temp_avg, 2) AS avg_body_temp_c,
  record_count,
  TO_TIMESTAMP_NANOS(time_from) AS source_start,
  TO_TIMESTAMP_NANOS(time_to) AS source_end
FROM bird_tracking_10s
ORDER BY time DESC, species, name
LIMIT 20;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="sample-output"&gt;Sample Output&lt;/h4&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Time (UTC)&lt;/th&gt;
    &lt;th&gt;Species&lt;/th&gt;
    &lt;th&gt;Bird&lt;/th&gt;
    &lt;th&gt;Avg. speed&lt;/th&gt;
    &lt;th&gt;Avg. body temp.&lt;/th&gt;
    &lt;th&gt;Source points&lt;/th&gt;
    &lt;th&gt;Source start&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;2026-08-14 21:21:00&lt;/td&gt;
    &lt;td&gt;American Robin&lt;/td&gt;
    &lt;td&gt;Tracy&lt;/td&gt;
    &lt;td&gt;22.47&lt;/td&gt;
    &lt;td&gt;41.94&lt;/td&gt;
    &lt;td&gt;10&lt;/td&gt;
    &lt;td&gt;21:21:00&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;2026-08-14 21:21:00&lt;/td&gt;
    &lt;td&gt;Blue Jay&lt;/td&gt;
    &lt;td&gt;Sara&lt;/td&gt;
    &lt;td&gt;21.59&lt;/td&gt;
    &lt;td&gt;41.67&lt;/td&gt;
    &lt;td&gt;10&lt;/td&gt;
    &lt;td&gt;21:21:00&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;2026-08-14 21:21:00&lt;/td&gt;
    &lt;td&gt;Cactus Wren&lt;/td&gt;
    &lt;td&gt;Brandon&lt;/td&gt;
    &lt;td&gt;18.78&lt;/td&gt;
    &lt;td&gt;41.74&lt;/td&gt;
    &lt;td&gt;10&lt;/td&gt;
    &lt;td&gt;21:21:00&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;2026-08-14 21:21:00&lt;/td&gt;
    &lt;td&gt;Great Blue Heron&lt;/td&gt;
    &lt;td&gt;Brenda&lt;/td&gt;
    &lt;td&gt;6.38&lt;/td&gt;
    &lt;td&gt;41.10&lt;/td&gt;
    &lt;td&gt;10&lt;/td&gt;
    &lt;td&gt;21:21:00&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;2026-08-14 21:21:00&lt;/td&gt;
    &lt;td&gt;Great Blue Heron&lt;/td&gt;
    &lt;td&gt;Pamela&lt;/td&gt;
    &lt;td&gt;24.97&lt;/td&gt;
    &lt;td&gt;41.35&lt;/td&gt;
    &lt;td&gt;10&lt;/td&gt;
    &lt;td&gt;21:21:00&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;h4 id="validate-the-reduction-with-sql"&gt;Validate the Reduction with SQL&lt;/h4&gt;

&lt;p&gt;Do not judge a downsampling job only by whether the target table contains rows. Compare a fixed, completed interval so active writes cannot change the counts during validation.&lt;/p&gt;

&lt;p&gt;The following query compares two completed minutes of source data with the corresponding persisted rollups. &lt;strong&gt;Replace the timestamps with a completed interval from your own run&lt;/strong&gt;:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;WITH raw AS (
  SELECT COUNT(*) AS raw_rows, COUNT(DISTINCT name) AS birds
  FROM bird_tracking
  WHERE time &amp;gt;= TIMESTAMP '2026-08-14T21:20:00Z'
    AND time " TIMESTAMP '2026-08-14T21:22:00Z'
),
downsampled AS (
  SELECT
    COUNT(*) AS downsampled_rows,
    SUM(record_count) AS represented_raw_rows,
    AVG(record_count) AS avg_points_per_row
  FROM bird_tracking_10s
  WHERE time &amp;gt;= TIMESTAMP '2026-08-14T21:20:00Z'
    AND time " TIMESTAMP '2026-08-14T21:22:00Z'
)
SELECT
  raw_rows,
  birds,
  downsampled_rows,
  represented_raw_rows,
  avg_points_per_row,
  ROUND(
    100.0 * (
      1.0 - CAST(downsampled_rows AS DOUBLE) / CAST(raw_rows AS DOUBLE)
    ),
    1
  ) AS row_reduction_percent
FROM raw CROSS JOIN downsampled;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="example-output"&gt;Example Output&lt;/h4&gt;

&lt;p&gt;The equality between &lt;code class="language-markup"&gt;raw_rows&lt;/code&gt; and &lt;code class="language-markup"&gt;represented_raw_rows&lt;/code&gt; is an important completeness check. The 90% figure is a reduction in rows for this query shape, not a promise of an identical reduction in bytes on disk. Actual storage depends on schema, tags, fields, compression, and retention settings.&lt;/p&gt;

&lt;p&gt;You should also compare aggregates against the query-time SQL version before applying any raw-data retention policy.&lt;/p&gt;

&lt;h2 id="downsampling-best-practices-for-production"&gt;Downsampling best practices for production&lt;/h2&gt;

&lt;p&gt;The tutorial uses short intervals so you can see results quickly. A production workload might keep second-level raw data briefly, create 5-minute summaries for operational dashboards, and return hourly summaries for long-term reporting.&lt;/p&gt;

&lt;p&gt;When designing that pipeline:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class="language-markup"&gt;window=2min&lt;/code&gt; gives each execution a lookback window large enough to reprocess recent data.&lt;/li&gt;
  &lt;li&gt;Start with the questions users need to answer, then choose the interval. A bin that is too wide can hide short-lived behavior.&lt;/li&gt;
  &lt;li&gt;Preserve the tags you need for filtering and grouping. Removing a dimension during aggregation cannot be reversed later.&lt;/li&gt;
  &lt;li&gt;Select aggregates that match the signal. Gauges often need average, minimum, and maximum; counters may need sums or rates.&lt;/li&gt;
  &lt;li&gt;Account for ingestion delay with &lt;code class="language-markup"&gt;offset&lt;/code&gt;, and use a lookback window that can catch expected late data.&lt;/li&gt;
  &lt;li&gt;Validate counts and values over multiple completed intervals before shortening raw-data retention.&lt;/li&gt;
  &lt;li&gt;Monitor the Processing Engine logs and query latency after deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="troubleshoot-a-scheduled-downsampler"&gt;Troubleshoot a scheduled downsampler&lt;/h2&gt;

&lt;p&gt;If the target table remains empty, inspect the Processing Engine logs:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-sql"&gt;SELECT
  trigger_name,
  log_level,
  log_text,
  event_time
FROM system.processing_engine_logs
ORDER BY event_time DESC
LIMIT 20;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then check the most common causes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The source table or a field name does not match the trigger arguments.&lt;/li&gt;
  &lt;li&gt;The source does not contain data within the plugin’s window and offset.&lt;/li&gt;
  &lt;li&gt;A Python dependency required by a plugin has not been installed.&lt;/li&gt;
  &lt;li&gt;The server cannot retrieve a remote &lt;code class="language-markup"&gt;gh:&lt;/code&gt; plugin.&lt;/li&gt;
  &lt;li&gt;The selected aggregate is not valid for a field’s data type.&lt;/li&gt;
  &lt;li&gt;The newest interval is incomplete because the offset is too small.&lt;/li&gt;
  &lt;li&gt;Getting started with downsampling with InfluxDB 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;InfluxDB 3 makes it possible to use the same SQL aggregation logic at two stages of a workload: interactively at query time and operationally as a persisted rollup. Start with &lt;code class="language-markup"&gt;DATE_BIN&lt;/code&gt; to confirm the right interval and dimensions. When the query becomes a stable, repeated access pattern, schedule the official Python downsampler and include an offset so it works on completed data.&lt;/p&gt;

&lt;p&gt;Try the tutorial with &lt;a href="https://www.influxdata.com/influxdb-signup/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=downsampling_guide_influxdb_3&amp;amp;utm_content=blog"&gt;InfluxDB 3&lt;/a&gt; and the &lt;a href="https://github.com/influxdata/influxdb3_plugins/tree/main/influxdata/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=downsampling_guide_influxdb_3&amp;amp;utm_content=blog"&gt;official InfluxDB 3 plugin library&lt;/a&gt;. Once your results match the raw source over a completed interval, adapt the interval, aggregates, offset, and retention strategy to your production workload.&lt;/p&gt;

&lt;h2 id="faqs"&gt;FAQs&lt;/h2&gt;

&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What is downsampling?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Downsampling is the process of grouping time series points into wider time intervals and calculating summaries such as averages, minimums, maximums, sums, or counts. InfluxDB 3 can calculate those summaries at query time with SQL or persist them with a Python Processing Engine.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How do I downsample data with InfluxDB 3 SQL?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
              Use &lt;code class="language-markup"&gt;DATE_BIN&lt;/code&gt; to place timestamps into fixed intervals, apply aggregate functions such as &lt;code class="language-markup"&gt;AVG&lt;/code&gt; or &lt;code class="language-markup"&gt;MAX&lt;/code&gt;, and group by the binned time plus any tags you want to preserve. SQL downsampling returns aggregate rows but does not automatically write them to another table.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;When should I use the Python Processing Engine instead of SQL alone?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Use the Processing Engine when you want to precompute and store a rollup on a schedule. It is a good fit for aggregates used repeatedly by dashboards or long-range reports. Use query-time SQL when you need flexible intervals, are still exploring the data, or do not want another stored representation.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-4"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How do I prevent partial downsampling windows?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-4" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Set the downsampler plugin’s `offset` so it queries intervals that have finished receiving data. The offset should cover normal ingestion latency and expected late arrivals. Validate completeness by comparing the sum of `record_count` in the aggregate table with the raw row count over the same closed interval.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-5"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Does downsampling delete the original data?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-5" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                No. Both a SQL aggregate query and the downsampler plugin leave the source data intact. The plugin writes additional rows to a target table. Raw-data deletion or expiration is controlled separately through retention settings.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-6"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What does record_count mean in the downsampled table?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-6" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                `record_count` is the number of source records represented by an aggregate row. It is useful for detecting incomplete bins and for comparing the target table with the source over the same period.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-7"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Can I calculate more than one aggregate per interval?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-7" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Yes. A SQL query can return multiple functions such as AVG(speed), MIN(speed), and MAX(speed) in the same group. The official downsampler accepts calculation mappings for multiple fields. Choose only the summaries your queries need to avoid unnecessary write and storage overhead.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-8"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Does this approach work with InfluxDB 3 Cloud Serverless or Cloud Dedicated?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-8" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                The embedded Python Processing Engine workflow in this tutorial applies to InfluxDB 3 Core and Enterprise. For InfluxDB Cloud Serverless, follow the documented client-library downsampling pattern: query aggregates with SQL, write the results back, and schedule the client externally. For Cloud Dedicated, you can use the same general query-and-write pattern with an InfluxDB 3 client library and an external scheduler. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-9"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How much storage will downsampling save?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-9" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                There is no universal percentage. The tutorial reduced the number of rows returned for its fixed test window by 90%, but byte-level storage depends on the schema, data types, tag cardinality, compression, the aggregates stored, and how long you retain each resolution.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;
</description>
      <pubDate>Wed, 19 Aug 2026 08:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/downsampling-guide-influxdb-3/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/downsampling-guide-influxdb-3/</guid>
      <category>Developer</category>
      <author>Charles Mahler (InfluxData)</author>
    </item>
    <item>
      <title>A Rust Client for InfluxDB 3</title>
      <description>&lt;p&gt;Time series data shows up wherever the physical world meets software. A satellite constellation streams altitude, power, and thermal telemetry from every spacecraft on every pass. A factory floor running on Industry 4.0 principles instruments every line, every motor, every batch. And underneath all of it sits a humbler problem that anyone who has worked in operational technology knows well: getting telemetry out of the PLCs and edge controllers that actually run the machines, off the bus, and into a database that can enable real-time asset intelligence.&lt;/p&gt;

&lt;p&gt;InfluxDB 3 is built for this class of workload. It is the latest generation of the InfluxDB time series engine, built on an open source stack: &lt;strong&gt;Apache Arrow&lt;/strong&gt; for in-memory columnar data and &lt;strong&gt;Apache DataFusion&lt;/strong&gt; as the query engine. In practice, that means InfluxDB 3 is a columnar, vectorized engine that speaks SQL, exchanges data over Arrow Flight, and interoperates with the broader Arrow ecosystem, rather than a closed world with its own bespoke query path. For high-cardinality telemetry (thousands of spacecraft channels, tens of thousands of sensor tags on a plant floor), that columnar foundation is what keeps both ingest and analytical queries fast.&lt;/p&gt;

&lt;p&gt;What has been missing for Influx users is a first-class &lt;strong&gt;Rust&lt;/strong&gt; client. We just built one: &lt;a href="https://crates.io/crates/influxdb3-client/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;influxdb3-client&lt;/a&gt;, an async Rust client for InfluxDB 3 Core and Enterprise that mirrors the feature set of the official &lt;a href="https://github.com/InfluxCommunity/influxdb3-go/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;Go&lt;/a&gt; and &lt;a href="https://github.com/InfluxCommunity/influxdb3-python/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;Python&lt;/a&gt; clients with an idiomatic Rust API.&lt;/p&gt;

&lt;h2 id="why-rust-when-go-and-python-clients-already-exist"&gt;Why Rust, when Go and Python clients already exist?&lt;/h2&gt;

&lt;p&gt;The honest answer is that for a lot of jobs, you shouldn’t switch. If you’re exploring data in a notebook, the Python client is the right tool. If you’re writing a typical backend service, the Go client is mature and perfectly fast. Rust earns its place in the parts of a telemetry pipeline where the other two start to fight you. Here are some scenarios where it makes sense to switch:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The edge box next to the PLC&lt;/strong&gt;. The machine that bridges OPC UA or Modbus to your historian is often an ARM gateway with a few hundred MB of RAM, no package manager you control, and a change window measured in months. Rust cross-compiles to a single static binary, with no Python interpreter to install and patch on the box and no runtime to ship. &lt;code class="language-markup"&gt;scp&lt;/code&gt; it over, run it for a year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingest where tail latency is the spec&lt;/strong&gt;. During a ten-minute satellite pass, the ground segment has to drain every frame the downlink produces; there is no catching up later. A garbage collector that pauses at the wrong moment turns into dropped telemetry. Rust’s lack of a GC doesn’t make your code faster on average; it makes the worst case boring, which is what you actually care about when the data source won’t wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backpressure you can reason about&lt;/strong&gt;. High-rate ingest lives or dies on flow control: how many batches are in flight, how much memory they pin, what happens when the database slows down. With tokio, that’s an explicit semaphore and bounded buffers checked by the type system, rather than a goroutine count you tune by load testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The telemetry source is already Rust&lt;/strong&gt;. Increasingly, the code producing the telemetry is Rust: a ROS 2 node on an autonomous mobile robot, drone flight software, a soft-PLC runtime, or a protocol bridge that speaks OPC UA or MQTT-Sparkplug. When the producer is a Rust process, the historian client should be a library you embed in it: same binary, same async runtime, no sidecar process to deploy and monitor on every robot in the fleet. An &lt;code class="language-markup"&gt;Arc"Client"&lt;/code&gt; shared across your tokio tasks is the whole integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compile once, use anywhere&lt;/strong&gt;. The same binary often has to write to whatever InfluxDB the customer runs. As of 0.2, writes default to the V2 &lt;code class="language-markup"&gt;/api/v2/write&lt;/code&gt; endpoint, so one client works unchanged against InfluxDB 3 Core and Enterprise as well as InfluxDB Clustered and Cloud Dedicated/Serverless. A config flag, not a code change, opts into the V3-only extras like &lt;code class="language-markup"&gt;no_sync&lt;/code&gt; when you control the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You’re already in the Arrow ecosystem&lt;/strong&gt;. InfluxDB 3 itself is written in Rust on Arrow and DataFusion. With this client, query results come back as native &lt;code class="language-markup"&gt;Arrow RecordBatch&lt;/code&gt;es—the same types you’d hand to DataFusion, Polars, or your own analytics code, with no serialization boundary in between. The client and the server are speaking the same in-memory format end-to-end.&lt;/p&gt;

&lt;p&gt;If none of these scenarios describe your situation, the Go and Python clients remain great choices. If one of them does, you should get familiar with the Rust client.&lt;/p&gt;

&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;

&lt;p&gt;The client is on &lt;a href="http://crates.io"&gt;crates.io&lt;/a&gt; and requires Rust 1.89 or later:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;cargo add influxdb3-client&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or add it to your &lt;code class="language-markup"&gt;Cargo.toml&lt;/code&gt; alongside an async runtime:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;[dependencies]
influxdb3-client = "0.2"
tokio = { version = "1", features = ["full"] }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is an optional &lt;code class="language-markup"&gt;polars&lt;/code&gt; feature for DataFrame-based workflows, which we’ll come back to later.&lt;/p&gt;

&lt;h4 id="let-claude-write-the-boilerplate"&gt;Let Claude Write the Boilerplate&lt;/h4&gt;

&lt;p&gt;If you use &lt;a href="https://claude.com/claude-code"&gt;Claude Code&lt;/a&gt;, there’s an &lt;strong&gt;influxdb3 skill&lt;/strong&gt; that teaches it the InfluxDB 3 API surface:  line protocol, the v3 SQL and InfluxQL query paths, token and database administration, and the client libraries—this one included. With the &lt;code class="language-markup"&gt;claude-influxdb3&lt;/code&gt; plugin installed, you can skip the docs-spelunking and ask things like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;*"Write me a Rust snippet that writes a batch of sensor readings to my InfluxDB 3 cluster and reads the last hour back with SQL."*
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll get working code against this client’s actual API. It also knows the troubleshooting terrain: 401s from token scoping, line-protocol parse errors, and queries that silently return no rows. That’s most of what a first hour with any database consists of.&lt;/p&gt;

&lt;h2 id="configuring-a-client"&gt;Configuring a client&lt;/h2&gt;

&lt;p&gt;uuA client needs a host, a database, and (usually) an API token. This is the most explicit way is to build the configuration yourself:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use influxdb3_client::{Client, ClientConfig};

#[tokio::main]
async fn main() -&amp;gt; influxdb3_client::Result"()" {
    let client = Client::new(
        ClientConfig::builder()
            .host("http://localhost:8181")
            .token("my-api-token")
            .database("sensors")
            .build()?,
    )
    .await?;
    Ok(())
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For deployments where configuration comes from the environment, such as a container or a systemd unit on an edge box, read &lt;code class="language-markup"&gt;INFLUX_HOST&lt;/code&gt;, &lt;code class="language-markup"&gt;INFLUX_TOKEN&lt;/code&gt;, and &lt;code class="language-markup"&gt;INFLUX_DATABASE&lt;/code&gt; directly:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;let client = influxdb3_client::Client::from_env().await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Optional variables (&lt;code class="language-markup"&gt;INFLUX_AUTH_SCHEME&lt;/code&gt;, &lt;code class="language-markup"&gt;INFLUX_ORG&lt;/code&gt;, &lt;code class="language-markup"&gt;INFLUX_PRECISION&lt;/code&gt;, &lt;code class="language-markup"&gt;INFLUX_GZIP_THRESHOLD&lt;/code&gt;, and the &lt;code class="language-markup"&gt;INFLUX_WRITE_*&lt;/code&gt; family) configure the same write defaults the builder exposes, so a deployed agent can be retuned without a rebuild.&lt;/p&gt;

&lt;p&gt;Or, parse a single connection string, which is handy when configuration arrives as a single opaque value:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;"let client = influxdb3_client::Client::from_connection_string(
    "https://cluster.example.io/?token=TOKEN&amp;amp;database=mydb",
).await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Arrow Flight channel used for queries is opened lazily on the first query, so constructing a client never blocks on query connectivity, and a write-only ingest agent never pays for a query connection it won’t use.&lt;/p&gt;

&lt;h2 id="writing-data"&gt;Writing data&lt;/h2&gt;

&lt;p&gt;&lt;code class="language-markup"&gt;client.write(data)&lt;/code&gt; returns a builder; chain the options you want, then &lt;code class="language-markup"&gt;.await&lt;/code&gt; it. The data argument is flexible—it can be a line-protocol string, a &lt;code class="language-markup"&gt;Vec"Point"&lt;/code&gt;, or (with the &lt;code class="language-markup"&gt;polars&lt;/code&gt; feature) a DataFrame.&lt;/p&gt;

&lt;h4 id="points"&gt;Points&lt;/h4&gt;

&lt;p&gt;The &lt;code class="language-markup"&gt;Point&lt;/code&gt; builder is the most ergonomic way to construct measurements in code:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use influxdb3_client::{Point, Precision};

let points = vec![
    Point::new("temperature")
        .tag("location", "office")
        .tag("floor", "2")
        .field("celsius", 22.5_f64)
        .field("humidity", 48_i64)
        .field("occupied", true),
];

client.write(points).precision(Precision::Millisecond).await?;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="raw-line-protocol"&gt;Raw Line Protocol&lt;/h4&gt;

&lt;p&gt;If you already have line protocol, say forwarded straight off a device, you can write it as is:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;client
    .write("cpu,host=server01 usage_user=42.3,usage_system=1.2")
    .await?;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="write-options"&gt;Write Options&lt;/h4&gt;

&lt;p&gt;The builder exposes the knobs that matter for real ingest pipelines:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;client.write(points)
    .precision(Precision::Nanosecond)
    .batch_size(10_000)          // points per HTTP request
    .max_inflight(8)             // concurrent in-flight requests
    .default_tag("region", "us-east")
    .tag_order(["region", "host"])
    .await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Large inputs are split into batches and sent as multiple pipelined requests, with one batch buffer held in memory at a time, so memory stays bounded even on very large writes.&lt;/p&gt;

&lt;p&gt;&lt;code class="language-markup"&gt;tag_order&lt;/code&gt; matters more than it appears to. The &lt;strong&gt;first write defines the physical tag column order for a table, and that order&lt;/strong&gt; affects query performance; tags you filter on most should sort first. &lt;code class="language-markup"&gt;.tag_order(...)&lt;/code&gt; serializes the listed tags first, then appends any remaining tags in deterministic lexicographic order, so the machine that happens to boot first doesn’t accidentally pick a bad layout for everyone. (Background: &lt;a href="https://docs.influxdata.com/influxdb3/core/write-data/best-practices/optimize-writes/#sort-tags-by-query-priority"&gt;sort tags by query priority&lt;/a&gt;.)&lt;/p&gt;

&lt;h4 id="which-write-endpoint"&gt;Which Write Endpoint?&lt;/h4&gt;

&lt;p&gt;As of 0.2, writes go to the V2 &lt;code class="language-markup"&gt;/api/v2/write endpoint&lt;/code&gt; &lt;strong&gt;by default&lt;/strong&gt;, which means the same client also works against InfluxDB Clustered and InfluxDB Cloud Dedicated/Serverless without changes. Opting into the V3 endpoint (&lt;code class="language-markup"&gt;ClientConfig::builder().write_use_v2_api(false)&lt;/code&gt;, or &lt;code class="language-markup"&gt;INFLUX_WRITE_USE_V2_API=false&lt;/code&gt; in the environment) unlocks two V3-only behaviours: &lt;code class="language-markup"&gt;no_sync()&lt;/code&gt; (acknowledge before the WAL is synced) and partial-write reporting, both covered below.&lt;/p&gt;

&lt;h4 id="high-throughput-ingest"&gt;High-Throughput Ingest&lt;/h4&gt;

&lt;p&gt;For sustained, high-volume writes, such as a satellite pass or a full plant floor, the throughput levers are &lt;code class="language-markup"&gt;batch_size&lt;/code&gt; (points per request) and &lt;code class="language-markup"&gt;max_inflight&lt;/code&gt; (concurrent requests per call). On the V3 endpoint (&lt;code class="language-markup"&gt;write_use_v2_api(false)&lt;/code&gt;), &lt;code class="language-markup"&gt;no_sync()&lt;/code&gt; adds a third: acknowledge before the WAL is synced, trading a little durability for speed.&lt;/p&gt;

&lt;p&gt;A single &lt;code class="language-markup"&gt;write&lt;/code&gt; call serializes its batches on one task. To use more CPU cores and connections, run several &lt;code class="language-markup"&gt;write&lt;/code&gt; calls concurrently. A &lt;code class="language-markup"&gt;Client&lt;/code&gt; is cheap to share, and its HTTP connection pool is reused, so the idiomatic pattern is to wrap it in an &lt;code class="language-markup"&gt;Arc&lt;/code&gt;, spread chunks across tasks, and cap concurrency with a semaphore to keep in-flight buffers bounded:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use std::sync::Arc;
use tokio::sync::Semaphore;

let client = Arc::new(client);
// cap concurrent writes
let gate = Arc::new(Semaphore::new(8)); 

// each chunk is a Vec"Point"
for chunk in chunks {                    
    let permit = gate.clone().acquire_owned().await.unwrap();
    let client = Arc::clone(&amp;amp;client);
    tokio::spawn(async move {
        // released when the write completes
        let _permit = permit;            
        client
            .write(chunk)
            .batch_size(10_000)
            .max_inflight(8)
            .no_sync()

            .await
    });
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To spread load across multiple ingest nodes, put a load balancer in front of the cluster, or construct one &lt;code class="language-markup"&gt;Client&lt;/code&gt; per node and distribute chunks across them.&lt;/p&gt;

&lt;h2 id="querying-data"&gt;Querying data&lt;/h2&gt;
&lt;p&gt;InfluxDB 3 supports both &lt;strong&gt;SQL&lt;/strong&gt; and &lt;strong&gt;InfluxQL&lt;/strong&gt;, and the client exposes both through the same query-builder pattern: &lt;code class="language-markup"&gt;client.sql(q)&lt;/code&gt; or &lt;code class="language-markup"&gt;client.influxql(q)&lt;/code&gt;.&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;let result = client
    .sql("SELECT * FROM temperature ORDER BY time DESC LIMIT 10")
    .await?;

for row in result {
    let row = row?;
    let loc = row["location"].as_str().unwrap_or("");
    let c = row["celsius"].as_f64().unwrap_or(0.0);
    println!("{loc}: {c}");
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;InfluxQL is called in exactly the same way, which makes it easy to bring existing InfluxQL queries forward:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;let result = client
    .influxql("SELECT MEAN(celsius) FROM temperature WHERE time &amp;gt; now() - 1h")
    .await?;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="parameterized-queries"&gt;Parameterized Queries&lt;/h4&gt;

&lt;p&gt;Bind parameters with &lt;code class="language-markup"&gt;.param()&lt;/code&gt; rather than interpolating into the query string:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;let rows = client
    .sql("SELECT COUNT(*) AS n FROM cpu WHERE host = $host")
    .param("host", "server01")
    .await?
    .rows()?;

if let Some(r) = rows.first() {
    println!("count: {}", r["n"]);
}&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="working-with-rows"&gt;Working with Rows&lt;/h4&gt;

&lt;p&gt;A &lt;code class="language-markup"&gt;QueryResult&lt;/code&gt; can be iterated row by row, collected all at once with &lt;code class="language-markup"&gt;.rows()&lt;/code&gt;, or accessed as raw Arrow &lt;code class="language-markup"&gt;RecordBatches&lt;/code&gt; with &lt;code class="language-markup"&gt;.record_batches()&lt;/code&gt; if you want to hand the columnar data straight to another Arrow-aware library. A &lt;code class="language-markup"&gt;Row&lt;/code&gt; is indexed by column name (&lt;code class="language-markup"&gt;row["col"]&lt;/code&gt;) or position (&lt;code class="language-markup"&gt;row[0]&lt;/code&gt;), and yields a Value with typed accessors: &lt;code class="language-markup"&gt;as_f64&lt;/code&gt;, &lt;code class="language-markup"&gt;as_i64&lt;/code&gt;, &lt;code class="language-markup"&gt;as_str&lt;/code&gt;, &lt;code class="language-markup"&gt;as_bool&lt;/code&gt;, &lt;code class="language-markup"&gt;is_null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One deliberate design choice: if a query returns an Arrow type the row API doesn’t support, you get an explicit &lt;code class="language-markup"&gt;Error::UnsupportedArrowType&lt;/code&gt;, rather than a silent null. Telemetry pipelines fail quietly often enough without the client library helping.&lt;/p&gt;

&lt;h4 id="streaming-large-results"&gt;Streaming Large Results&lt;/h4&gt;

&lt;p&gt;For analytical queries whose results are too large to hold in memory, like a scan over a month of high-rate telemetry, stream the Arrow batches instead of collecting them:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use futures_util::TryStreamExt;

let mut stream = client.sql("SELECT * FROM temperature").stream().await?;
while let Some(batch) = stream.try_next().await? {
    println!("got {} rows", batch.num_rows());
}&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="reliability"&gt;Reliability&lt;/h4&gt;

&lt;p&gt;Telemetry pipelines run unattended, so the client retries transient failures automatically with exponential backoff and full jitter. Connection errors, timeouts, &lt;code class="language-markup"&gt;429&lt;/code&gt;, and &lt;code class="language-markup"&gt;5xx&lt;/code&gt; responses are retried, and &lt;code class="language-markup"&gt;Retry-After&lt;/code&gt; is honored when present. Deterministic failures (other &lt;code class="language-markup"&gt;4xx&lt;/code&gt; responses and partial writes) are never retried. Retrying writes is safe because line-protocol writes are idempotent at the (&lt;code class="language-markup"&gt;series&lt;/code&gt;, &lt;code class="language-markup"&gt;timestamp&lt;/code&gt;, &lt;code class="language-markup"&gt;field&lt;/code&gt;) level, i.e., re-sending the same point simply overwrites it.&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use influxdb3_client::RetryConfig;
use std::time::Duration;

// Per-request override.
client.write(points)
    .retry(RetryConfig { max_retries: 5, base_delay: Duration::from_millis(100), ..RetryConfig::default() })
    .await?;

// Disable retries for a single call.
client.write(points).no_retry().await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On the V3 endpoint, when a batch contains invalid lines, the server accepts the valid ones and reports the rest, which surfaces as &lt;code class="language-markup"&gt;Error::PartialWrite&lt;/code&gt; with the rejected lines attached:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use influxdb3_client::Error;

if let Err(Error::PartialWrite(e)) = client.write(line_protocol).await {
    for line_error in &amp;amp;e.line_errors {
        eprintln!("line {}: {}", line_error.line, line_error.message);
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If all-or-nothing semantics fit your pipeline better, &lt;code class="language-markup"&gt;.accept_partial(false)&lt;/code&gt; rejects the entire batch when any line fails.&lt;/p&gt;

&lt;h2 id="polars-integration"&gt;Polars integration&lt;/h2&gt;

&lt;p&gt;For data-engineering and analysis workflows, the optional &lt;code class="language-markup"&gt;polars&lt;/code&gt; feature lets you write a DataFrame directly and read query results back as one:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;influxdb3-client = { version = "0.2", features = ["polars"] }

use influxdb3_client::write_dataframe::DataFrameWrite;
use polars::prelude::*;&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;let df = df![
    "host"    =&amp;gt; ["srv1", "srv2"],
    "region"  =&amp;gt; ["us-east", "us-west"],
    "cpu_pct" =&amp;gt; [42.5_f64, 71.0_f64],
    "time"    =&amp;gt; [1_700_000_000_000_000_000_i64, 1_700_000_001_000_000_000_i64],
]?;

client
    .write(
        DataFrameWrite::new(&amp;amp;df, "server_metrics")
            .tags(&amp;amp;["host", "region"])
            .timestamp_column("time"),
    )
    .await?;

let df_back = client
    .sql("SELECT * FROM server_metrics")
    .await?
    .to_polars()?;&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id="backfilling-from-parquet-files"&gt;Backfilling from Parquet Files&lt;/h4&gt;

&lt;p&gt;Let’s look at a common migration task. Say you have historical telemetry sitting in Parquet files, exported from another system or batch dumped from a data lake, and you want it in InfluxDB where it can be queried alongside live data. File IO deliberately lives in your code rather than the client; read the file with Polars, then hand the frame to &lt;code class="language-markup"&gt;DataFrameWrite&lt;/code&gt;. Enable the Parquet reader on Polars in your own &lt;code class="language-markup"&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;polars = { version = "0.53", features = ["Parquet"] }&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use std::fs::File;
use polars::prelude::*;
use influxdb3_client::write_dataframe::DataFrameWrite;

let df = ParquetReader::new(File::open("sensors.Parquet")?).finish()?;

client
    .write(
        DataFrameWrite::new(&amp;amp;df, "sensor_data")
            .tags(&amp;amp;["site", "line", "machine_id"])
            .timestamp_column("time"),
    )
    .await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Parquet files carry their schema, so dtypes arrive correct—floats stay floats, timestamps stay timestamps. Columns you name in &lt;code class="language-markup"&gt;.tags(&amp;amp;[...])&lt;/code&gt; become tags, the timestamp column sets each point’s time, and every remaining column becomes a field. The whole pipeline, from Parquet reader through DataFrame and client to server, is Arrow-native, so the data never leaves columnar form until the final encode.&lt;/p&gt;

&lt;p&gt;For a multi-gigabyte backfill, read and write in file-sized chunks rather than one giant frame, and reuse the high-throughput pattern from earlier (&lt;code class="language-markup"&gt;batch_size&lt;/code&gt;, &lt;code class="language-markup"&gt;max_inflight&lt;/code&gt;, one task per file) to keep the pipe full.&lt;/p&gt;

&lt;h4 id="loading-csv-exports"&gt;Loading CSV Exports&lt;/h4&gt;

&lt;p&gt;Most SCADA packages and historians will export CSV, so it’s often the format you’re handed. The same programming pattern works, with one caveat: CSV carries no schema, so Polars infers column types, and anything ambiguous infers as a &lt;strong&gt;string&lt;/strong&gt;. A string column becomes a string field in InfluxDB, and you can’t &lt;code class="language-markup"&gt;MEAN()&lt;/code&gt; a string. Supply the dtypes explicitly and parse the timestamp at read time:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;polars = { version = "0.53", features = ["csv"] }&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=""&gt;&lt;code class="language-rust"&gt;use polars::prelude::*;
use influxdb3_client::write_dataframe::DataFrameWrite;

let schema = Schema::from_iter([
    Field::new("machine_id".into(), DataType::String),         
    Field::new("rpm".into(), DataType::Float64),                       Field::new("spindle_load_pct".into(), DataType::Float64),  
    Field::new("alarm_active".into(), DataType::Boolean),     
    Field::new("time".into(), DataType::Datetime(TimeUnit::Nanoseconds, None)),
]);

let df = CsvReadOptions::default()
    .with_schema(Some(Arc::new(schema)))
    .try_into_reader_with_file_path(Some("plc_export.csv".into()))?
    .finish()?;

client
    .write(
        DataFrameWrite::new(&amp;amp;df, "machine_telemetry")
            .tags(&amp;amp;["machine_id"])
            .timestamp_column("time"),
    )
    .await?;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you’d rather let inference do the first pass, that works too. Just &lt;code class="language-markup"&gt;cast()&lt;/code&gt; the numeric and boolean columns before writing, or they’ll land as string fields and you’ll be wondering why your aggregation queries return nothing.&lt;/p&gt;

&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;

&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
              &lt;h3&gt;What is &lt;code class="language-markup"&gt;influxdb3-client?&lt;/code&gt;&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
              The Rust &lt;code class="language-markup"&gt;influxdb3-client&lt;/code&gt; is a Rust native client library for programmatically interacting with Influxdb. It simplifies writing Line Protocol (Influxdb’s native format) as well writing queries in SQL and Influxql.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What Rust version does it require?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                The crate requires Rust version 1.89 or later.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Does it work with InfluxDB Cloud Dedicated, Clustered, or Serverless, or only Core and Enterprise?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Yes! The client library has a full backwards compatible api and integrates with any version of Influxdb that supports the v2 write API. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-4"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Does the client support InfluxDB 1.x or 2.x?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-4" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Yes. Later versions of InfluxDB have forward compatibility APIs, and all versions of Influxdb support the v2 write API. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-5"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Why would I use this instead of the Go or Python client?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-5" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                 Rust excels in embedded programing environments, or integrations with other Rust codebases. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-6"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Is the Polars integration required?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-6" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                No. Polars enables reading and writing to CSV and Parquet files, or if you want to leverage Polars Dataframes, but isn’t required. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-7"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Does it retry failed writes automatically?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-7" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Yes, the client retries transient failures automatically with exponential backoff and full jitter.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-8"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Where do I report issues or request features?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-8" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                https://github.com/InfluxCommunity/influxdb3-rust
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;

&lt;h2 id="try-it"&gt;Try it&lt;/h2&gt;

&lt;p&gt;The repository ships runnable examples in &lt;a href="https://github.com/InfluxCommunity/influxdb3-rust/tree/main/examples/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;examples/:&lt;/a&gt; a &lt;code class="language-markup"&gt;quickstart&lt;/code&gt; that does an end-to-end write and query, a Cloud Dedicated connection example, and a Polars DataFrame round-trip. Point them at a running InfluxDB 3 instance and go:&lt;/p&gt;

&lt;pre class=""&gt;&lt;code class="language-bash"&gt;INFLUX_HOST=http://localhost:8181 INFLUX_TOKEN=token INFLUX_DATABASE=mydb \
    cargo run --example quickstart&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The client is &lt;a href="https://crates.io/crates/influxdb3-client/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;influxdb3-client on crates.io&lt;/a&gt;, the source lives on &lt;a href="https://github.com/InfluxCommunity/influxdb3-rust/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=rust_client_influxdb_3&amp;amp;utm_content=blog"&gt;GitHub&lt;/a&gt;, and the API docs are on &lt;a href="https://docs.rs/influxdb3-client"&gt;docs.rs&lt;/a&gt;. It’s early, and feedback, issues, and pull requests are all welcome.&lt;/p&gt;
</description>
      <pubDate>Thu, 13 Aug 2026 08:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/rust-client-influxdb-3/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/rust-client-influxdb-3/</guid>
      <category>Developer</category>
      <author>Ian Clark (InfluxData)</author>
    </item>
    <item>
      <title>Where Historians Fall Short for Physical AI</title>
      <description>&lt;p&gt;Physical AI enables machines and industrial systems to perceive conditions, reason about them, and act in the real world. In industrial settings, as part of an industrial AI strategy, physical AI models can help organizations identify risks earlier, optimize operations, and respond to changing conditions in real-time.&lt;/p&gt;

&lt;p&gt;Delivering these outcomes starts with training AI models on detailed historical operational data. Then, when deployed, these models need access to real-time telemetry to interpret current conditions, make decisions, and automate actions.&lt;/p&gt;

&lt;p&gt;For organizations built around traditional historians, supporting model training and real-time operations creates new challenges and new opportunities.&lt;/p&gt;

&lt;h2 id="where-historians-fall-short-for-physical-ai"&gt;Where historians fall short for Physical AI&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.influxdata.com/glossary/data-historian/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;Data historians&lt;/a&gt; remain essential systems for industrial operations. They create a durable record of process and equipment behavior, supporting engineering analysis, troubleshooting, reporting, auditability, and, where required, regulatory compliance.&lt;/p&gt;

&lt;p&gt;But the issue is, simply put, data historians were built for, well, historical data. The data requirements of Physical AI are different.&lt;/p&gt;

&lt;p&gt;At inference time, models need immediate access to current, sufficiently detailed operational data. During development and training, they need consistent, well-labeled history drawn from many operating conditions, assets, and sites. Historian-centered architectures can make both jobs difficult.&lt;/p&gt;

&lt;h4 id="retaining-operational-history-versus-a-live-model-feed"&gt;Retaining Operational History Versus a Live Model Feed&lt;/h4&gt;

&lt;p&gt;Data historians are effective at recording operations and helping engineers investigate what happened. But many historian deployments were not designed to continuously serve high-frequency OT data to AI models and applications operating across edge, IT, and cloud environments.
The data may be collected in real-time, yet still be difficult to use in real time. Historian data commonly remains inside the OT environment, where access is constrained by network segmentation, security requirements, proprietary interfaces, and site-specific infrastructure. Making it available to an AI application may require gateways, scheduled queries, replication, exports, or additional integration pipelines.&lt;/p&gt;

&lt;p&gt;Every additional step introduces operational complexity, and more importantly, delays. At inference time, latency matters. Physical AI systems must evaluate current conditions while there is still an opportunity to respond. If telemetry reaches the model after the equipment state or process condition has changed, the data may still support investigation, but it can no longer support timely intervention.&lt;/p&gt;

&lt;h4 id="compression-removes-relevant-signals-for-model-training"&gt;Compression Removes Relevant Signals for Model Training&lt;/h4&gt;

&lt;p&gt;Legacy data historians often use techniques such as (deadbands, exception processing, compression, aggregation, or downsampling, to reduce data volume while preserving operational trends. This approach is appropriate for traditional data historian workloads, e.g., reporting, troubleshooting, and compliance.&lt;/p&gt;

&lt;p&gt;By comparison, Physical AI models need to be trained on high-resolution telemetry that preserves the patterns required to recognize normal and abnormal operating states, predict outcomes, and determine the appropriate action. When those details are removed before training, models may not be able to learn the subtle behaviors that distinguish one condition from another.&lt;/p&gt;

&lt;h4 id="the-cost-of-fragmented-data"&gt;The Cost of Fragmented Data&lt;/h4&gt;

&lt;p&gt;Legacy data historian architectures were designed in an era when operational technology (OT) and information technology (IT) environments were largely separate. That separation creates challenges when training Physical AI models because much of the context needed to interpret telemetry, such as maintenance records, production schedules, quality results, operator actions, business processes, and asset relationships, resides in IT applications such as ERP, MES, and CMMS systems.&lt;/p&gt;

&lt;p&gt;In addition, historian architectures also tend to be site-centric. Historians are typically deployed and managed at the plant, facility, or asset level, creating separate stores of operational history across the organization. Physical AI initiatives may need data from multiple—or even all—sites to build an enterprise-level history that captures a broader range of assets, operating conditions, failures, and outcomes.&lt;/p&gt;

&lt;p&gt;Lastly, training datasets for Physical AI models are increasingly multimodal, combining telemetry with inputs such as images, video, audio, and LiDAR. When these sensory inputs are connected and aligned in time, models can learn how observations—across sight, sound, spatial awareness, and machine state—relate to operating conditions, how events unfold, and which outcomes or actions follow. This multimodal training prepares Physical AI models to interpret a wider range of sensory inputs at inference time, reason about current conditions, and ultimately act within the physical world.&lt;/p&gt;

&lt;p&gt;The challenge is that legacy historian architectures preserve time series telemetry as an operational record, but they do not inherently connect it with enterprise context, history from other sites, or data held in specialized multimodal systems.&lt;/p&gt;

&lt;p&gt;Bridging these &lt;a href="https://www.influxdata.com/blog/breaking-data-silos-influxdb-3/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;data silos&lt;/a&gt; requires building &lt;a href="https://www.influxdata.com/glossary/etl/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;ETL pipelines&lt;/a&gt;, custom integrations, and manual workflows to align timestamps, asset identities, operating conditions, events, and outcomes. Engineers spend significant time collecting, reconciling, and preparing data before it can be used to train Physical AI models.&lt;/p&gt;

&lt;table style="border-collapse: collapse; width: 100%; font-family: Arial, Helvetica, sans-serif; font-size: 16px; color: #000;"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="background-color: #1A203F; color: #fff; text-align: left; padding: 16px 20px; border: 2px solid #000;"&gt;The Problem&lt;/th&gt;
      &lt;th style="background-color: #1A203F; color: #fff; text-align: left; padding: 16px 20px; border: 2px solid #000;"&gt;Historian Limitation&lt;/th&gt;
      &lt;th style="background-color: #1A203F; color: #fff; text-align: left; padding: 16px 20px; border: 2px solid #000;"&gt;InfluxDB 3 Solution&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Inference needs live data&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;OT data is trapped behind gateways and exports&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Ingests at the edge with low-latency queries&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Training needs full signal details&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Compression, downsampling, and aggregation strip patterns out&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Retain and serve full resolution data at scale&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;IT/OT connection&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Context lives outside the historian&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Connects telemetry with applications (ERP, MES, CMMS)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;AI needs data from every site&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Stores data on a per-site basis&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Consolidates multi-site data&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Models need to learn from more than sensor data&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Historians don't line up video, images, or audio with telemetry&lt;/td&gt;
      &lt;td style="padding: 16px 20px; border: 2px solid #000;"&gt;Syncs telemetry with other data types by time&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id="a-modern-time-series-architecture-connects-edge-and-cloud"&gt;A modern time series architecture connects edge and cloud&lt;/h2&gt;

&lt;p&gt;Fortunately, teams don’t need to rip out their historians and toss them aside. With InfluxDB 3, organizations can build on existing historian investments while creating a more modern operational data architecture for Physical AI. Historians can continue to preserve the operational record, while InfluxDB provides a distributed time series data layer for ingesting, processing, consolidating, and distributing telemetry across edge, cloud, and enterprise environments.&lt;/p&gt;

&lt;p&gt;At the industrial edge, time series services can sit close to the source, where teams can process and query live telemetry for low-latency inference and action. Some or all of that data can also be sent to InfluxDB Cloud, creating a common, time-aligned data layer that brings together operational history from multiple sources. This gives organizations the detailed, connected historical datasets needed for model training, evaluation, and enterprise analysis.&lt;/p&gt;

&lt;p&gt;The cloud consolidation point also serves as a distribution layer. Telemetry can be made available without building a separate DataOps pipeline or custom integration between each source and every consumer. The same architecture can therefore support local decision-making at the edge and enterprise-wide learning in the cloud.&lt;/p&gt;

&lt;p&gt;Even better, this shared data layer can connect telemetry with asset metadata, data from enterprise systems such as ERP, MES, and CMMS, and multimodal data such as images, video, and audio. Time-aligned telemetry provides the operating context for these other data types, showing machine state, load, temperature, vibration, control settings, and process conditions at the moment a multimodal observation was captured or an operator action was taken. The result is a richer training foundation that helps Physical AI models learn not just what happened, but the conditions in which it happened, context that supports more accurate inference, stronger root-cause analysis, and better operational decisions.&lt;/p&gt;

&lt;h4 id="building-a-context-rich-training-dataset-for-physical-ai"&gt;Building a Context-Rich Training Dataset for Physical AI&lt;/h4&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/2uxMiXlnGroOl9O9rSk0VV/c60ef6fb5df45ea0e31b26f4d05eb187/8993186e-317e-4dbc-9557-69b60e31e46b.png" alt="Where Historians Fall Short for Physical AI diagram" /&gt;&lt;/p&gt;

&lt;h4 id="delivering-live-telemetry-to-ai-at-inference"&gt;Delivering Live Telemetry to AI at Inference&lt;/h4&gt;

&lt;p&gt;Built on &lt;a href="https://www.influxdata.com/glossary/apache-arrow/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;Apache Arrow &lt;/a&gt;and DataFusion with SQL support, InfluxDB 3 enables fast time series queries across large volumes of time-stamped data. It works alongside legacy historians as a real-time time series layer, or hub. Teams can ingest high-frequency telemetry, query recent data as it arrives, and use that data in dashboards, and to trigger alerts and automation workflows. With the &lt;a href="https://www.influxdata.com/blog/new-python-processing-engine-influxdb3/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;Python Processing Engine&lt;/a&gt;, teams can process signals, extract features, detect anomalies, and act on data as it arrives. This brings analysis closer to ingestion, helping deployed physical AI models run inference on current operating conditions and respond as they change.&lt;/p&gt;

&lt;h2 id="built-for-the-physical-world"&gt;Built for the physical world&lt;/h2&gt;

&lt;p&gt;Legacy data historians will continue to play an important role in preserving operational history and supporting long-term analysis. But as AI-driven operations become more common, organizations need additional capabilities to make detailed operational data continuously available for training, inference, and action.&lt;/p&gt;

&lt;p&gt;By extending historian investments with a modern, distributed time-series architecture, teams can support real-time action at the edge while consolidating and distributing operational data across the enterprise.&lt;/p&gt;

&lt;p&gt;Ready to build a stronger foundation for Physical AI? 
Explore InfluxDB 3 open source with &lt;a href="https://www.influxdata.com/products/influxdb/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;InfluxDB 3 Core&lt;/a&gt; or a free trial of &lt;a href="https://www.influxdata.com/products/influxdb-enterprise/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;InfluxDB 3 Enterprise&lt;/a&gt;. For more on this topic, watch the webinar, &lt;a href="https://www.influxdata.com/resources/physical-ai-for-industrial-iot-edge-impulse-influxdb/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=where_historians_fall_short&amp;amp;utm_content=blog"&gt;Physical AI for Industrial IoT: Edge Impulse + InfluxDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="faq"&gt;FAQ&lt;/h2&gt;
&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What is Physical AI, and how does it differ from other forms of AI?
&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Physical AI refers to AI systems that interact with the material world, interpreting telemetry through the lens of physical laws rather than just processing numbers or generating content. In industrial settings, it relies on continuous operational data such as sensor telemetry, machine states, and process conditions to interpret its environment and act in real-time.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What's the difference between a data historian and a time series database?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                A data historian is built to record industrial process and equipment data for engineering analysis, troubleshooting, and compliance reporting. A time-series database, like InfluxDB 3, is purpose-built to ingest, query, and act on time series data in real-time across edge, cloud, and enterprise environment. Historians excel at retaining a historical record; time series databases are built to also serve that data live to applications and AI models.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Why can't traditional historians support real-time AI inference?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Many historian deployments store data inside OT environments where network segmentation, proprietary interfaces, and site-specific infrastructure limit real-time access. Getting that data to an AI model typically requires gateways, scheduled queries, or export pipelines, each of which adds latency. 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-4"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How does InfluxDB 3 help unify data across multiple industrial sites?
&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-4" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                Historians are typically deployed at the plant or facility level, creating separate stores of operational history. InfluxDB 3 can consolidate time series data from multiple sites into a centralized or cloud-based layer, giving Physical AI models an enterprise-wide dataset spanning more assets, operating conditions, and failure modes than any single site can provide alone.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;
</description>
      <pubDate>Tue, 04 Aug 2026 08:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/where-historians-fall-short/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/where-historians-fall-short/</guid>
      <category>Developer</category>
      <author>Conrad Chuang (InfluxData)</author>
    </item>
    <item>
      <title>What’s New in InfluxDB 3.11: A Significant Performance Upgrade for Complex Time Series Workloads </title>
      <description>&lt;p&gt;Time series workloads rarely stay predictable for long. A system might begin with a few devices reporting a small set of measurements. As the fleet grows, new sensors come online, tags multiply, and query patterns change. A table that looked simple at the start becomes wide and sparse. A workload built around recent data expands into long-range analysis. As complexity grows, maintaining fast query performance and a predictable resource profile becomes much harder.&lt;/p&gt;

&lt;p&gt;InfluxDB 3.11 was built for that reality.&lt;/p&gt;

&lt;p&gt;Our last two releases gave users an early look at a set of performance improvements designed for heavier, more complex time series workloads. Today’s release of InfluxDB 3.11 makes those performance improvements GA in InfluxDB 3 Enterprise, bringing significantly faster queries on live data, greater flexibility for wide and ultra-sparse schemas, and more predictable performance under heavy load. The release also adds new Enterprise capabilities for backup and recovery, bulk data import, row-level deletes, and cluster operations.&lt;/p&gt;

&lt;h2 id="performance-and-flexibility-for-heavier-more-complex-workloads"&gt;Performance and flexibility for heavier, more complex workloads&lt;/h2&gt;

&lt;h4 id="faster-reads-for-single-series-workloads"&gt;Faster Reads for Single-Series Workloads&lt;/h4&gt;

&lt;p&gt;Time series applications rely on fast access to recent data, whether it’s the latest reading from a device, the current state of an asset, a narrow time range from a sensor, or a live view that tracks ingest. &lt;strong&gt;For highly selective queries, like those for a single time series, this new release of InfluxDB 3 Enterprise is up to 4x faster than previous versions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Actual performance will vary by workload, schema, hardware, and query shape. But for industrial monitoring, fleet management, observability, energy systems, and other applications built around live operational data, these gains mean faster access to the current state of the system.&lt;/p&gt;

&lt;h4 id="more-room-for-wide-sparse-and-changing-schemas"&gt;More Room for Wide, Sparse, and Changing Schemas&lt;/h4&gt;

&lt;p&gt;Time series data rarely arrives in one clean shape. As devices, sensors, tags, and operating environments change, schemas can become wide, sparse, and highly variable. Rigid schema limits can force teams to split data across more tables, drop useful context, or design around the database instead of the application.&lt;/p&gt;

&lt;p&gt;InfluxDB 3.11 expands schema flexibility with support for thousands of tables and millions of columns, while efficiently handling ultra-sparse datasets where only a small fraction of fields may be populated at any given time. Teams can model complex, changing telemetry around the data itself, with predictable query performance even as schemas grow wider and sparser.&lt;/p&gt;

&lt;p&gt;For customers moving from InfluxDB 1.x or 2.x to InfluxDB 3, this brings forward the schema flexibility they know from InfluxDB, now with the full SQL query engine, object-store-based durability, and unlimited cardinality.&lt;/p&gt;

&lt;h4 id="more-predictable-performance-under-load"&gt;More Predictable Performance Under Load&lt;/h4&gt;

&lt;p&gt;InfluxDB 3.11 reduces the resource spikes that can occur during heavy ingest and compaction. Memory usage is more predictable, and compaction runs with a more consistent resource profile, making it easier to understand what a workload actually requires.&lt;/p&gt;

&lt;p&gt;Teams can size infrastructure with more confidence instead of holding extra capacity for occasional spikes. Query responsiveness stays steadier under load, while memory and infrastructure costs are easier to plan for as workloads grow.&lt;/p&gt;

&lt;h2 id="new-data-management-capabilities-for-enterprise"&gt;New data management capabilities for Enterprise&lt;/h2&gt;

&lt;p&gt;InfluxDB 3.11 also adds new Enterprise capabilities for backing up, importing, and deleting data:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;End-to-End Backup &amp;amp; Restore&lt;/strong&gt;: You can now run full or incremental backups to capture compacted data. Restores run asynchronously, providing disaster recovery and allowing you to roll a live cluster back to an earlier point in time.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Bulk Import from Parquet&lt;/strong&gt;: Bringing existing or external data into InfluxDB 3 is now much simpler. A new bulk-import feature lets you upload an entire directory of Parquet files to ingest the data directly.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Row-Level Deletes&lt;/strong&gt;: Deleting individual rows is inherently more complex in columnar storage where data is optimized to be written and read in larger blocks. InfluxDB 3.11 now lets you target specific data by time range or tag predicate, without having to drop an entire table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="influxdb-3-explorer-ui-now-built-in"&gt;InfluxDB 3 Explorer UI, now built in&lt;/h2&gt;

&lt;p&gt;InfluxDB 3.11 brings the Explorer UI directly into InfluxDB 3, giving users a visual interface for querying, exploring, and managing their data out of the box.&lt;/p&gt;

&lt;p&gt;Explorer goes well beyond basic data visualization. You can query data using SQL, InfluxQL, or natural language, convert existing Flux queries to SQL with AI-assisted explanations, and manage Processing Engine plugins from the built-in plugin manager. Explorer also includes schema browsing, sample data generation, visualization, and live instance monitoring in one interface.&lt;/p&gt;

&lt;h2 id="get-started-with-influxdb-311"&gt;Get started with InfluxDB 3.11&lt;/h2&gt;

&lt;p&gt;InfluxDB 3.11 brings the performance improvements introduced in our last two releases to GA, with faster single-series queries, support for wide and ultra-sparse schemas, and more predictable performance under load. New Enterprise data management capabilities and the built-in Explorer UI make these improvements easier to deploy and manage in production.&lt;/p&gt;

&lt;p&gt;As time series workloads grow, so do demands on the database. InfluxDB 3.11 is built to maintain performance as complexity increases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.influxdata.com/downloads/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=influxdb-3-11&amp;amp;utm_content=blog"&gt;Download the latest version&lt;/a&gt; of InfluxDB 3 Core or Enterprise, pull the newest Docker image, or check out the docs (&lt;a href="https://docs.influxdata.com/influxdb3/core/release-notes/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=influxdb-3-11&amp;amp;utm_content=blog"&gt;Core&lt;/a&gt;, &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/release-notes/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=influxdb-3-11&amp;amp;utm_content=blog"&gt;Enterprise&lt;/a&gt;) to get started.&lt;/p&gt;
</description>
      <pubDate>Thu, 30 Jul 2026 07:30:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/influxdb-3-11/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/influxdb-3-11/</guid>
      <category>Product</category>
      <category>Developer</category>
      <category>news</category>
      <author>Peter Barnett (InfluxData)</author>
    </item>
    <item>
      <title>AI-Powered Spacecraft Operations with InfluxDB 3</title>
      <description>&lt;p&gt;When a satellite is drifting toward a fault, operators don’t need another dashboard full of disconnected charts. They need to know what changed, what it means, and what to check before the next ground pass closes. That’s the idea behind our &lt;a href="https://www.influxdata.com/solutions/by-industries/satellite-telemetry-monitoring/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;satellite telemetry&lt;/a&gt; demo: a live mission-control experience built on &lt;a href="https://www.influxdata.com/products/influxdb/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The demo monitors a simulated fleet of 12 satellites, continuously ingesting telemetry such as onboard computer temperature, battery voltage, solar current, reaction wheel RPM, power draw, RF link margin, ground station, orbit pass, and attitude status. One satellite is actively degrading, while historical incidents provide useful context for comparison.&lt;/p&gt;

&lt;p&gt;You can check out the live demo &lt;a href="https://www.influxdata.com/demos/satellite-demo/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;here&lt;/a&gt; or watch the video to see it in action:&lt;/p&gt;

&lt;div class="youtube-container"&gt;
  &lt;iframe class="responsive-iframe" src="https://www.youtube.com/embed/G20XWaESNjk?si=C4bjYRbucMsyVttr" title="Real-Time Satellite Monitoring with InfluxDB 3 &amp;amp; Claude" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""&gt;
  &lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id="demo-overview"&gt;Demo overview&lt;/h2&gt;

&lt;h4 id="from-telemetry-stream-to-operator-signal"&gt;From Telemetry Stream to Operator Signal&lt;/h4&gt;

&lt;p&gt;The main dashboard provides a fleet overview showing which satellites are online, which are currently in contact, and where anomalies are occurring. Operators can then drill into individual spacecraft to get a more specific view and look at historical data.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/7Hnc2hAafjqsncgkQWS8eS/d84f83fd4ff1284ada15b36a79720489/c6c08772-315c-43b5-89b8-45e4ebf0aa86.png" alt="AI-Powered Spacecraft Operations with InfluxDB 3 #1" /&gt;&lt;/p&gt;

&lt;h4 id="detecting-anomalies-as-data-arrives"&gt;Detecting Anomalies as Data Arrives&lt;/h4&gt;

&lt;p&gt;Behind the scenes, the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3 Processing Engin&lt;/a&gt;e observes incoming data as it is written to the database and automatically creates alerts based on conditional thresholds or sensor states.&lt;/p&gt;

&lt;p&gt;That means anomaly detection happens without waiting for a separate pipeline to catch up. Another processing engine task incorporates NOAA data for environmental context alongside vehicle behavior.&lt;/p&gt;

&lt;p&gt;This pattern can be extended beyond satellites. Any connected system with high-volume and time-sensitive data, such as industrial equipment, energy infrastructure, production systems, or logistics networks, will benefit.&lt;/p&gt;

&lt;h4 id="accessing-operational-truth-using-ai"&gt;Accessing Operational Truth Using AI&lt;/h4&gt;

&lt;p&gt;This demo also includes an AI agent powered by the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/admin/mcp-server/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3 MCP server&lt;/a&gt;. Instead of generating answers based on generic knowledge, the agent can access data stored in InfluxDB. You can ask about fleet health, a specific satellite, or the root cause of a suspected issue. The agent can then query InfluxDB throughout a conversation and summarize what it found.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/2PBcQAMHMzcDTGf2NnK5yY/ae6cc4af4ce194aead3e22739d3cc6a2/34b36852-d14e-403a-99f9-4b3151bf1893.png" alt="AI-Powered Spacecraft Operations with InfluxDB 3 #2" /&gt;&lt;/p&gt;

&lt;h2 id="under-the-hood-from-raw-telemetry-to-operational-context"&gt;Under the hood: From raw telemetry to operational context&lt;/h2&gt;

&lt;p&gt;This demo goes beyond the dashboard; it illustrates how InfluxDB 3 can transform live data into enriched operational data without external tools, as well as how to integrate InfluxDB into LLM-powered workflows.&lt;/p&gt;

&lt;h4 id="alerting-on-real-time-data"&gt;Alerting on Real-Time Data&lt;/h4&gt;

&lt;p&gt;Each incoming batch of satellite telemetry data triggers a processing engine plugin that evaluates data, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Onboard computer temperature&lt;/li&gt;
  &lt;li&gt;Reaction wheel RPMs&lt;/li&gt;
  &lt;li&gt;Power draw&lt;/li&gt;
  &lt;li&gt;Attitude status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an anomaly is found, an alert is written to a separate InfluxDB table. The row contains all relevant information for managing an incident, both in the short term and for historical tracking. Instead of the AI agent burning tokens querying and searching raw telemetry, it works from a dedicated alert stream that preserves the signal behind the alert.&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/43el1jBplLhRWvsGw5f6Sa/3527873b6deeea2f88aef524f882a88a/1a54ebbd-6fa3-4427-9315-df8624cd8135.png" alt="AI-Powered Spacecraft Operations with InfluxDB 3 #3" /&gt;&lt;/p&gt;

&lt;h4 id="enriching-telemetry-with-third-party-data"&gt;Enriching Telemetry with Third Party Data&lt;/h4&gt;

&lt;p&gt;Another way the demo uses the Processing Engine collecting NOAA planetary Kp index data with an external API call. This data is then stored in InfluxDB where operators can analyze how satellites are being impacted by external conditions and surface any correlated relationship between them.&lt;/p&gt;

&lt;p&gt;Scheduled Processing Engine jobs help enrich operational data. Depending on your use case, this could include weather, energy prices, traffic, or financial market data. It can also calculate rollups, forecast, or downsample data at a scheduled interval.&lt;/p&gt;

&lt;h4 id="integrated-ai-agent-using-the-influxdb-3-mcp-server"&gt;Integrated AI Agent Using the InfluxDB 3 MCP Server&lt;/h4&gt;

&lt;p&gt;The AI agent is connected to InfluxDB via the MCP server. Rather than relying on a static prompt or blindly summarizing a dashboard, it can query recent telemetry and alerts, inspect the result, and explain its conclusion.&lt;/p&gt;

&lt;p&gt;The UI exposes that query/tool trace so operators can see what evidence informed the answer. The agent effectively becomes an interface to your data model.&lt;/p&gt;

&lt;h2 id="putting-the-processing-engine-to-work"&gt;Putting the processing engine to work&lt;/h2&gt;

&lt;p&gt;InfluxDB 3’s Processing Engine runs Python inside the database and supports triggers on data writes, schedules, and HTTP requests. That allows it to do far more than simple threshold alerts. You can check the &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;documentation&lt;/a&gt; for more detail on the three trigger types and how plugins can retain state between executions.&lt;/p&gt;

&lt;p&gt;Here are a few interesting ways you can take advantage of the Processing Engine:&lt;/p&gt;

&lt;div class="blog-html-table-wrapper"&gt;
 &lt;table class="blog-html-table"&gt;
  &lt;thead&gt;
   &lt;tr&gt;
    &lt;th&gt;Trigger type&lt;/th&gt;
    &lt;th&gt;Use case&lt;/th&gt;
    &lt;th&gt;Outcome&lt;/th&gt;
   &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
   &lt;tr&gt;
    &lt;td&gt;On write&lt;/td&gt;
    &lt;td&gt;Stateful anomaly detection&lt;/td&gt;
    &lt;td&gt;Detect drift against a rolling baseline, not just a static threshold.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;On write&lt;/td&gt;
    &lt;td&gt;Schema validation and enrichment&lt;/td&gt;
    &lt;td&gt;Reject or flag malformed data; add additional metadata.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;On write&lt;/td&gt;
    &lt;td&gt;Alert routing&lt;/td&gt;
    &lt;td&gt;Write an alert record and then kick off an incident management workflow with related context.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Scheduled&lt;/td&gt;
    &lt;td&gt;Deadman check&lt;/td&gt;
    &lt;td&gt;Identify data sources that have stopped writing data.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Scheduled&lt;/td&gt;
    &lt;td&gt;Forecasting and error evaluation&lt;/td&gt;
    &lt;td&gt;Generate expected readings, compare them to actual behavior, and detect changes before a hard limit is crossed.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;Scheduled&lt;/td&gt;
    &lt;td&gt;Data lifecycle&lt;/td&gt;
    &lt;td&gt;Downsample or export selected data.&lt;/td&gt;
   &lt;/tr&gt;
   &lt;tr&gt;
    &lt;td&gt;HTTP request&lt;/td&gt;
    &lt;td&gt;Operational runbooks&lt;/td&gt;
    &lt;td&gt;Expose an API endpoint that an app can use to retrieve data or start a workflow.&lt;/td&gt;
   &lt;/tr&gt;
  &lt;/tbody&gt;
 &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;If you don’t want to start from scratch building with the Processing Engine, there are a number of &lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/library/official/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;plugins built and supported by the InfluxDB team&lt;/a&gt;. Some plugin examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Anomaly detection&lt;/li&gt;
  &lt;li&gt;Schema validation&lt;/li&gt;
  &lt;li&gt;Notifications&lt;/li&gt;
  &lt;li&gt;Iceberg export&lt;/li&gt;
  &lt;li&gt;Integrations for Kafka, MQTT, and AMQP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger opportunity is to compose these pieces. You could ingest data, validate it on write, detect drift against a baseline, write a contextual alert, enrich it with scheduled external data, and let an MCP-enabled AI agent explain the event in plain language.&lt;/p&gt;

&lt;h2 id="check-out-the-demo-and-start-building"&gt;Check out the demo and start building&lt;/h2&gt;

&lt;p&gt;While this demo is built specifically for satellites, the architecture is designed for any use case where &lt;a href="https://www.influxdata.com/what-is-time-series-data/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;time series data&lt;/a&gt; is used to take action and direct important decisions.&lt;/p&gt;

&lt;p&gt;InfluxDB 3 stores the live signal. The Processing Engine detects, enriches, and routes meaningful events. The MCP server gives an AI agent a grounded way to investigate the data and show its work. Together, they turn telemetry from data teams merely collect into something they can understand and act on.&lt;/p&gt;

&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.influxdata.com/demos/satellite-demo/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;Satellite Telemetry demo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.influxdata.com/products/signup/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;Download InfluxDB 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://docs.influxdata.com/influxdb3/explorer/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3 Explorer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://docs.influxdata.com/influxdb3/enterprise/admin/mcp-server/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3 MCP Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://docs.influxdata.com/influxdb3/enterprise/plugins/library/official/?utm_source=website&amp;amp;utm_medium=direct&amp;amp;utm_campaign=ai-powered-spacecraft-ops&amp;amp;utm_content=blog"&gt;InfluxDB 3 official processing engine plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="faqs"&gt;FAQs&lt;/h2&gt;

&lt;div id="accordion_second"&gt;
    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-1"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How does the demo detect anomalies in real-time?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-1" class="message-body is-collapsible is-active" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
                The InfluxDB 3 processing Engine watches incoming data as it is written and automatically generates alerts based on conditional thresholds or sensor states. This removes the need for a separate processing pipeline.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-2"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What is the InfluxDB 3 Processing Engine and what can it do beyond alerting? &lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-2" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
               The Processing Engine allows InfluxDB 3 to run custom Python code inside your database. This code can be activated by three different trigger types: data writes, schedules, or via HTTP request. Beyond threshold alerts, it can be used for schema validation, deadman checks, downsampling, data transformation, and many other use cases.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-3"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;How can InfluxDB be used with an AI agent?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-3" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
               This demo uses the InfluxDB MCP server to connect to an AI agent, giving the LLM context directly from InfluxDB to answer questions about the current state of the satellite fleet. InfluxDB also has a CLI and REST API that can be utilized by AI agents.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-4"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;Is this demo specific to satellites or can the architecture be reused elsewhere?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-4" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
              The architecture is designed for any use case built on time series data that is used to drive decisions. It could be used for monitoring industrial equipment, energy infrastructure, or logistics networks. The same combination of InfluxDB for storage, Processing Engine for anomaly detection and enrichment, and MCP for AI-powered investigation generalizes for all of these use cases.
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

    &lt;article class="message"&gt;
        &lt;a href="javascript:void(0)" data-action="collapse" data-target="collapsible-message-accordion-second-5"&gt;
            &lt;div class="message-header"&gt;
                &lt;h3&gt;What is InfluxDB 3?&lt;/h3&gt;
                &lt;span class="icon"&gt;
                    &lt;i class="fas fa-angle-down" aria-hidden="true"&gt;&lt;/i&gt;
                &lt;/span&gt;
            &lt;/div&gt;&lt;/a&gt;
        &lt;div id="collapsible-message-accordion-second-5" class="message-body is-collapsible" data-parent="accordion_second" data-allow-multiple="true"&gt;
            &lt;div class="message-body-content"&gt;
               InfluxDB 3 is a time series database used to store and analyze time series data for high performance workloads. It has a built-in Python VM for low latency data analysis and an MCP server for integration with AI agents.

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/article&gt;

&lt;/div&gt;
</description>
      <pubDate>Wed, 29 Jul 2026 08:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/ai-powered-spacecraft-ops/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/ai-powered-spacecraft-ops/</guid>
      <category>Developer</category>
      <author>Charles Mahler (InfluxData)</author>
    </item>
  </channel>
</rss>
