Skip to content
Permalink
Browse files

perf: fix 1ms per async CopyAPI (regression since 42.2.5) (#1314)

The problem is SSL sockets use buffers for input, and `stream#available()` might easily return 0 even though the data is available in the downstream socket. The only viable WA is to perform `read` calls, however it might impact performance (e.g. for async copy operations). The resolution is to throttle read calls for copy api, and keep 1ms reads for replication api.

fixes #1312
  • Loading branch information...
vlsi committed Oct 25, 2018
1 parent e44e4e8 commit e2623d63d4b6fad0b12fb9ace842475e4a9134dc
@@ -9,8 +9,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
### Added

### Fixed
- Fixed async copy performance regression [PR 1314](https://github.com/pgjdbc/pgjdbc/pull/1314)

## [42.2.5] (2018-08-27)
### Known issues
- 1ms per async copy call [issue 1312](https://github.com/pgjdbc/pgjdbc/issues/1312)

### Changed
- `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e)

@@ -7,6 +7,9 @@ version: 42.2.5
---
**Notable changes**

### Known issues
- 1ms per async copy call [issue 1312](https://github.com/pgjdbc/pgjdbc/issues/1312)

### Changed
- `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e)

@@ -44,6 +44,11 @@
private OutputStream pg_output;
private byte[] streamBuffer;

private long nextStreamAvailableCheckTime;
// This is a workaround for SSL sockets: sslInputStream.available() might return 0
// so we perform "1ms reads" once in a while
private int minStreamAvailableCheckDelay = 1000;

private Encoding encoding;
private Writer encodingWriter;

@@ -114,6 +119,12 @@ public boolean hasMessagePending() throws IOException {
return true;
}
// In certain cases, available returns 0, yet there are bytes
long now = System.currentTimeMillis();
if (now < nextStreamAvailableCheckTime && minStreamAvailableCheckDelay != 0) {
// Do not use ".peek" too often
return false;
}
nextStreamAvailableCheckTime = now + minStreamAvailableCheckDelay;
int soTimeout = getNetworkTimeout();
setNetworkTimeout(1);
try {
@@ -125,6 +136,10 @@ public boolean hasMessagePending() throws IOException {
}
}

public void setMinStreamAvailableCheckDelay(int delay) {
this.minStreamAvailableCheckDelay = delay;
}

/**
* Switch this stream to using a new socket. Any existing socket is <em>not</em> closed; it's
* assumed that we are changing to a new socket that delegates to the original socket (e.g. SSL).
@@ -28,11 +28,7 @@ public long endCopy() throws SQLException {
}

public byte[] readFromCopy() throws SQLException {
if (received.isEmpty()) {
queryExecutor.readFromCopy(this, true);
}

return received.poll();
return readFromCopy(true);
}

@Override
@@ -129,6 +129,8 @@ private void configureSocketTimeout(CommonOptions options) throws PSQLException
}

pgStream.getSocket().setSoTimeout(minimalTimeOut);
// Use blocking 1ms reads for `available()` checks
pgStream.setMinStreamAvailableCheckDelay(0);
} catch (IOException ioe) {
throw new PSQLException(GT.tr("The connection attempt failed."),
PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);

0 comments on commit e2623d6

Please sign in to comment.
You can’t perform that action at this time.