pgjdbc/pgjdbc GitHub issues and pull requests (mirror)  
help / color / mirror / Atom feed
From: davecramer (@davecramer) <[email protected]>
To: pgjdbc/pgjdbc <[email protected]>
Subject: [pgjdbc/pgjdbc] PR #4074: Use NIO
Date: Fri, 15 May 2026 20:28:18 +0000
Message-ID: <[email protected]> (raw)

docs: add full NIO refactor design document

Outlines the architecture for replacing PGStream's blocking I/O with SocketChannel + ByteBuffer + Selector. This enables true async notification detection and eliminates socket sharing races.

Key decisions:
- Channel always non-blocking, blocking via Selector.select()
- Selector.selectNow() for thread-safe async readability checks
- SSLEngine for NIO-compatible SSL
- Backward compatibility with existing PGStream API

wip: NIO full refactor - NIOInputStream/NIOOutputStream and PGStream SocketChannel scaffolding

Adds NIOInputStream (Selector-based blocking reads via ByteBuffer) and NIOOutputStream (buffered channel writes). PGStream creates a SocketChannel but still uses VisibleBufferedInputStream for reads — next step is to replace all reads with NIOInputStream.

The channel is created in non-blocking mode for Selector compatibility. The full refactor requires replacing pgInput usage throughout PGStream with NIOInputStream to eliminate the InputStream/blocking mode conflict.

wip: NIO full refactor - NIOInputStream/NIOOutputStream and PGStream SocketChannel scaffolding

Adds NIOInputStream (Selector-based blocking reads via ByteBuffer) and NIOOutputStream (buffered channel writes). PGStream creates a SocketChannel but still uses VisibleBufferedInputStream for reads — next step is to replace all reads with NIOInputStream.

The channel is created in non-blocking mode for Selector compatibility. The full refactor requires replacing pgInput usage throughout PGStream with NIOInputStream to eliminate the InputStream/blocking mode conflict.

wip: PGStream uses NIOInputStream/NIOOutputStream in non-SSL mode

changeSocket now creates NIOInputStream (Selector-based) and NIOOutputStream (channel-based) when a SocketChannel is available. Falls back to traditional InputStream/OutputStream for SSL connections.

pgInput is now nullable — next step is to update all read methods in PGStream to delegate to nioInput when in NIO mode.

feat: full NIO refactor - PGStream uses SocketChannel + Selector after setup

PGStream now creates a SocketChannel-backed socket and switches to NIO mode after connection setup (including SSL negotiation) completes:

- createSocket() uses SocketChannel.open() for default socket factory
- Channel stays in blocking mode during connection setup for SSL compat
- enableNIO() called from PgConnection constructor switches to non-blocking
- NIOInputStream uses Selector.select() for blocking reads via ByteBuffer
- NIOOutputStream writes to channel via ByteBuffer on flush
- VisibleBufferedInputStream wraps NIOInputStream (existing API unchanged)
- SSL connections fall back to traditional InputStream (no NIO)

Key benefit: NIOInputStream.isReadable() uses Selector.selectNow() which is thread-safe — a background NotificationPoller can check for incoming async data without reading from the socket or conflicting with the main thread's reads.

All 26 pipeline tests pass.

feat: add NotificationPoller for async LISTEN/NOTIFY detection

Background daemon thread that uses NIOInputStream.isReadable() (Selector.selectNow()) to detect incoming async data without reading from the socket. Thread-safe — no socket sharing conflicts.

When data is detected, sets a flag. getNotifications() checks the flag and processes any pending NotificationResponse or ParameterStatus messages before returning.

- NotificationPoller started in enableNIO() (after connection setup)
- Shut down on connection close/abort
- Only active for non-SSL connections (SSL falls back to polling in getNotifications via hasMessagePending)
- 100ms poll interval when idle, 10ms spin when waiting for main thread

All 26 pipeline tests pass.

fix: resolve checkstyle and checker-framework violations

- Remove unused SelectionKey/Selector imports from PGStream
- Fix PGNotification import ordering in QueryExecutorImpl
- Use local variable for nullable notificationPoller dereference
- Use local variable for nullable nioChannel in enableNIO()

chore: remove development artifacts and binary blobs from repo

Remove files that should not be in the PR:
- 30MB heap dump (.hprof)
- Development scripts and design documents
- Generated pom.xml
- IDE settings (.kiro/)

fix: address NIO review feedback

- NIOOutputStream: replace Thread.yield() spin-loop with Selector OP_WRITE wait when OS send buffer is full
- NotificationPoller: use Selector.select(timeout) for efficient blocking instead of Thread.sleep(100) polling loop
- processPolledNotifications: acquire main lock to prevent concurrent socket access with query execution
- PGStream.close: explicitly close NIOInputStream and its Selector
- Add NIOInputStream.waitForData(timeout) for blocking select

fix: replace Thread.sleep spin with Object.wait/notify in NotificationPoller

Use wait/notify for the dataAvailable flag instead of Thread.sleep(10) polling. The poller now parks efficiently until clearDataAvailable() is called by the main thread, eliminating unnecessary wakeups.



reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: github://pgjdbc/pgjdbc
  Cc: [email protected], [email protected]
  Subject: Re: [pgjdbc/pgjdbc] PR #4074: Use NIO
  In-Reply-To: <<[email protected]>>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox