clickhouse-java icon indicating copy to clipboard operation
clickhouse-java copied to clipboard

InputStream `reset()` can fail on retries for BufferedInputStreams

Open JD557 opened this issue 3 weeks ago • 1 comments

Description

The ClickHouse Java Client provides an insert method that takes an InputStream.

On failure, this method calls data.reset() https://github.com/ClickHouse/clickhouse-java/blob/f9f588d9d1b8abd8e7e8ab0eafc84b070377a68d/client-v2/src/main/java/com/clickhouse/client/api/Client.java#L1387-L1390

However, as far as I can tell, the code never sets a mark.

This works well on ByteArrayInputStreams (as is used in the unit tests), where reset will move the stream back to the beginning, but not one something like BufferedInputStream. In that case, even though markSupported is true, the mark needs to be defined.

This will also obviously fail on InputStreams like GZIPInputStream where markSupported is false, but that's to be expected.

I'm not sure if the client should set a mark, but I think it should at least mention that in the documentation.

Steps to reproduce

Send an insert request that fails from a BufferedInputStream. Note that this can simply be a BufferedInputStream wrapping a ByteInputStream.

Error Log or Exception StackTrace

Failed to reset stream before next attempt
Caused by: Exception java.io.IOException: Resetting to invalid mark

Expected Behaviour

Code Example

jshell> var bais = new java.io.ByteArrayInputStream("1,2,3".getBytes())
bais ==> java.io.ByteArrayInputStream@3159c4b8

jshell> var buffered = new java.io.BufferedInputStream(bais)
buffered ==> java.io.BufferedInputStream@29ca901e

jshell> // both support marks

jshell> bais.markSupported()
$3 ==> true

jshell> buffered.markSupported()
$4 ==> true

jshell> // reset works on byte array input streams

jshell> bais.reset()

jshell> // but fails on the buffered version

jshell> buffered.reset()
|  Exception java.io.IOException: Resetting to invalid mark
|        at BufferedInputStream.implReset (BufferedInputStream.java:583)
|        at BufferedInputStream.reset (BufferedInputStream.java:569)
|        at (#6:1)

jshell> // it works as expected after setting the first mark

jshell> buffered.mark(1024)

jshell> buffered.reset()

Configuration

Client Configuration


Environment

  • Client version: v0.9.4
  • Language version: Java

JD557 avatar Dec 02 '25 15:12 JD557

Good day, @JD557!

It protects from sending request from a middle of input stream. Retries happen only in small number of cases - when data should not be sent but it may be read from input stream.

chernser avatar Dec 03 '25 06:12 chernser