Skip to content

EN Writes and Errors

Alan Zhang edited this page Oct 3, 2026 · 2 revisions

Home · GitHub

English | 简体中文

TsGate 2.1.0: This guide uses com.alandevise.tsgate.*. Upgrading from 2.0.0 requires updating imports, reflection names and package scanning, then recompiling. Central 2.0.0 retains com.alandevise.tsdb.*. See migration steps and release status.

Annotated POJOs

import com.alandevise.tsgate.annotation.TGField;
import com.alandevise.tsgate.annotation.TGMeasurement;
import com.alandevise.tsgate.annotation.TGTag;
import com.alandevise.tsgate.annotation.TGTime;

@TGMeasurement("ACCRUE")
public class AccrueRecord {

    @TGTime
    private Long timestamp;

    @TGTag("device_code")
    private String deviceCode;

    @TGTag("point_type")
    private String pointType;

    @TGTag("source")
    private String source;

    @TGField("value")
    private Double value;

    @TGField("status")
    private Long status;
}

Annotations:

  • @TGMeasurement: target measurement/table.
  • @TGTime: time field, restricted to non-null Long/long values representing Unix Epoch milliseconds, without implicit timezone conversion.
  • @TGTag: tag field, converted to a string on write.
  • @TGField: ordinary field; at least one non-null field value is required.

A write POJO must declare one @TGTime and at least one @TGField. Query result objects need not be write POJOs and may omit TsGate annotations.

Standard API

Inject com.alandevise.tsgate.core.TGTemplate. When a backend is active, its starter registers the shared template under the default bean name tgTemplate:

import com.alandevise.tsgate.core.TGTemplate;
import org.springframework.beans.factory.annotation.Autowired;

@Autowired
private TGTemplate tgTemplate;

Writes

Omitting the database uses the adapter's configured default database:

boolean success = tgTemplate.write(record);

Specify a database:

boolean success = tgTemplate.write("tsdb", record);

Batch write:

boolean success = tgTemplate.batchWrite("tsdb", records);

Use the detailed result when commit boundaries matter. On failure, TSDBBatchWriteException carries the same result object:

BatchWriteResult result = tgTemplate.batchWriteDetailed("tsdb", records);

commitState is SUCCESS, NOT_COMMITTED, PARTIALLY_COMMITTED or UNKNOWN. Network errors, HTTP 408 and 5xx responses cannot prove that the current physical batch was not committed. Its state is therefore UNKNOWN, with error code BATCH_COMMIT_UNKNOWN; previously confirmed record and batch counts are retained.

With accept_partial=false, InfluxDB 3 treats HTTP 400/401/403/404/405/413/415/422/429 as a definite rejection of the current batch, reporting zero or partial commitment according to earlier successful requests. InfluxDB 1.x only treats HTTP 401/403/404/405/413/415/429 as definite rejection. HTTP 400 may have committed valid points, so its state is UNKNOWN.

retryable is independent of commit certainty: callers may retry 429, 408 and temporary 5xx responses according to an idempotency strategy; 501/505 do not suggest retry. The adapter does not automatically replay these writes. See the official write documentation for rejection semantics with accept_partial=false.

A single write becomes a one-element collection and is sent immediately as a batch. The starters do not provide asynchronous batch accumulation, unbounded buffering or a backpressure queue.

For the OpenGemini adapter, the complete batch is checked before I/O: measurement names cannot contain comma, semicolon, slash or backslash, equal . or .., or contain non-printable characters. Letters, marks, numbers, punctuation, symbols and ASCII space follow the server's printable-name rule; = is preserved. Invalid names produce ARGUMENT_ERROR with NOT_COMMITTED and zero physical batches; names are never renamed. The inherited line-protocol checks also apply. Once a request is sent, an HTTP 400 partial-write response retains UNKNOWN, as in InfluxDB 1.x.

Exception error codes

Adapters consistently throw TSDBException or a subclass. Codes are six-digit 1003xx integers. Use exception.getErrorCode() for the enum or exception.getCode() for its integer value. These classify application errors and are distinct from HTTP status codes.

Code Enum Meaning
100300 CONFIGURATION_ERROR Missing required settings, invalid batch limits or pool configuration
100301 ARGUMENT_ERROR Invalid SQL, pagination, predicates, identifiers or write values
100302 METADATA_ERROR Invalid annotation model, physical column, Java type or result mapping
100303 ADAPTER_STATE_ERROR Adapter or underlying client is uninitialized or unavailable
100304 CONNECTION_ERROR Network failure, timeout, unavailable service or rate limit
100305 PERMISSION_ERROR Authentication failure or insufficient permissions
100306 RESOURCE_NOT_FOUND Missing database, table, column or other resource
100307 WRITE_ERROR Definite server rejection with a known commit boundary
100308 QUERY_ERROR Query syntax, execution or response parsing failure
100309 BATCH_COMMIT_UNKNOWN Connection loss after writing or insufficient response evidence to determine the current batch's commit state
100310 UNSUPPORTED_OPERATION Backend capability or call combination unsupported by the shared API
100399 INTERNAL_ERROR Unclassified internal error or compatibility fallback for legacy constructors
try{
        tgTemplate.write(record);
}catch(
TSDBException e){
int code = e.getCode();
String detail = e.getMessage();
Throwable rootCause = e.getCause();
}

The application's Web layer maps these codes to its response format and appropriate HTTP statuses. The starters do not register a global exception handler.

Synchronous write semantics

  • write, batchWrite and batchWriteDetailed are synchronous. Successful return means the underlying TSDB acknowledged the write, rather than merely accepting it into an adapter memory queue.
  • For the OpenGemini adapter, new measurements or tag series become query-visible after asynchronous index merging. When required, the application explicitly polls the expected timestamp, tags and values with a deadline; each adapter query sends one request without a transparent visibility retry.
  • Synchronous does not mean globally serialized. Applications may call concurrently; IoTDB SessionPool or InfluxDB HTTP pools reuse connections. Prefer batch APIs for large writes to reduce network round trips.
  • Slow databases or exhausted pools propagate latency, timeouts and exceptions to the caller. The adapter does not retain pending data, so there is no unflushed internal buffer to lose on application restart.
  • Applications needing asynchronous burst handling, durable retries or reliable delivery should use an application executor, message queue or dedicated data pipeline. These components must choose queue capacity, rejection behavior, persistence, retry and deduplication according to business semantics.

page() and strictCursorPage() only support detail queries. Aggregation requires offset pagination through page(pageNum, pageSize). Aggregate results are stably ordered by window_start + groupByTags, or by groupByTags without a time window. Offset pagination issues an additional total-count query: detail queries count matching source rows, and grouped/aggregate queries count aggregate result rows.

Connections and batch writes

  • IoTDB requires the official ITableSessionPool; tsdb.iotdb.pool.enabled=false fails application startup.
  • IoTDB requires a configured default database created in advance. It is set on TableSessionPoolBuilder; ordinary reads/writes avoid redundant USE statements, and the official client restores the default database when a cross-database Session closes.
  • The previously tested IoTDB SDK 2.0.10 does not reliably replenish a pool slot if restoring the default database fails on Session close. The default database must remain present and the application account must retain access throughout runtime. SDK 2.0.11 still restores the default database when a borrowed Session closes; upgrading the client does not remove the requirement to keep that database available.
  • IoTDB waits for a returned Session when the pool is full. Acquisition timeout is returned without replacing the physical pool; other connection exceptions during acquisition also do not trigger replacement. Query execution retains version-aware recovery for the known SDK empty-response defect and connection failures, retrying read-only execution at most once. This does not repair slot leaks during acquisition or add a semaphore across pool generations. When a fault causes pool replacement, draining the old pool and using the new pool can briefly overlap; max-size remains a per-physical-pool limit.
  • IoTDB assembles batch Tablets by measurement. tsdb.iotdb.table.tablet-max-row-size controls each Tablet's maximum row count.
  • IoTDB Tablet RPC compact encoding/compression is enabled by default through tsdb.iotdb.table.rpc-compression-enabled=true; omission keeps the default. Set it to false for older incompatible servers such as 2.0.2. This is not Thrift transport or disk compression. Verify at least 10 rows in one actual Tablet, rather than only the total application batch, and check the rows after writing. The same setting is used for initial and replacement pools; it does not introduce an automatic write retry or change commit-state reporting.
  • For null IoTDB field values, types are inferred from non-null values in the same batch. Always-null columns are omitted. A table batch with no inferable field is rejected before I/O.
  • IoTDB fields map precisely to INT32/INT64/FLOAT/DOUBLE/BOOLEAN/STRING/BLOB/DATE. Unsupported Java types are rejected before the first Tablet write.
  • IoTDB / InfluxDB application batches default to at most 10000 records, controlled by each backend's max-batch-records.
  • InfluxDB 3 validates and encodes the complete batch before splitting it into HTTP requests of at most 5000 lines or 1 MiB of UTF-8 payload. Writes use accept_partial=false.
  • InfluxDB 1.x also validates the full batch and chunks requests, but HTTP 400 can mean some valid points were written. Such a response yields UNKNOWN; confirmed counts include only earlier successful requests. Potentially partially committed chunks are not automatically retried.
  • Both InfluxDB adapters reject measurement names beginning with # to prevent silent data loss from line-protocol comment interpretation. Measurement = characters and backend-specific backslash rules are encoded appropriately.
  • InfluxDB integer fields accept Byte/Short/Integer/Long and BigInteger within signed-int64 bounds. BigInteger uses the integer protocol suffix to avoid float64 rounding. Ambiguous Number subclasses are rejected.
  • InfluxDB Float/Double values must be finite. InfluxDB 1.x accepts BigDecimal only when exactly representable as float64. InfluxDB 3 permits ordinary double rounding for BigDecimal but rejects overflow and nonzero underflow to zero.
  • InfluxDB 1.x additionally rejects _field, _measurement and time as tag or field keys, avoiding data loss or rejection caused by reserved protocol names.
  • A batch is not a database transaction across multiple Tablets or HTTP requests. Conversion or validation failure before I/O confirms zero writes for the whole batch. After I/O starts, inspect BatchWriteResult.commitState for successful, uncommitted, partially committed or unknown outcomes.
  • Query/pagination limit/pageSize must be in 1–10000. Offset arithmetic overflow fails explicitly.
  • Adapters write synchronously and maintain no asynchronous buffer. If incoming application traffic can exceed database or network write capacity, implement rate limiting, burst handling, retry and backpressure in the application or a dedicated data pipeline.

← Configuration and database selection · Fluent queries and aggregation →

OpenGemini in 2.1.0

The tested 1.4.1/1.5.2 Influx-compatible HTTP write path converts integer fields through float64 before storing them. An integer protocol suffix does not prevent this conversion: 9007199254740993i reads back as 9007199254740992. The OpenGemini adapter therefore checks the complete batch before I/O and accepts integer-typed fields only within signed-int64 bounds and when their exact value is representable by IEEE 754 float64. All integers from -2^53 to 2^53 are exact; some larger values, including 2^53+2 and Long.MIN_VALUE, are also exact. 2^53+1, Long.MAX_VALUE and Long.MIN_VALUE+1 are rejected with ARGUMENT_ERROR, NOT_COMMITTED and zero physical requests, without coercion or string encoding. Structured query/count also preflight exact integer filter literals, including every IN/BETWEEN value, because backend numeric comparisons can round them. Extreme integer-valued BigDecimal predicates are compared with signed-int64 bounds before integer expansion, so very large exponents cannot bypass validation or force unbounded allocation. Float/Double and fractional query literals retain ordinary floating-point semantics; existing BigDecimal field checks still apply. Epoch-millisecond timestamps use their separate time path. Native SQL and borrowed native writes bypass these checks and retain backend precision limitations; aggregate overflow and floating-point accumulation are not made exact by this validation. This restriction is specific to the HTTP adapter, not a claim about every OpenGemini engine or alternate ingestion protocol.

TsGate 2.1.0 includes the independent OpenGemini adapter and starter. See OpenGemini integration for configuration, default-engine limits and exact-version testing, and 2.1.0 release notes for migration requirements.

Clone this wiki locally