Cheapest way to poll for notifications?
Hi
As Pg doesn't presently support client push for notifications arising
from LISTEN/NOTIFY, I'm wondering if anybody here has done any research
into the cheapest statement to issue to check for such notifications.
First: is it worth caring? Or is a `SELECT 1;' every few seconds from a
small (50-ish) number of clients unlikely to have a detectable effect on
load?
Does it have any significant costs (considering that each statement
starts its own transaction) ? Is it worth issuing:
BEGIN ISOLATION LEVEL READ COMMITTED, READ ONLY; SELECT 1; ROLLBACK;
rather than just `SELECT 1' ?
I'll be doing some testing on all of this, of course, but I thought I'd
ask in case others have looked into this already. I didn't see anything
much in the archives.
One thing I've noticed is that an invalid statement that fails to parse
still produces the notifications, though it avoids planning and query
execution. However, it'll also fill the error log with garbage, so it's
not exactly desirable. The error paths are probably more expensive than
just running the SELECT anyway.
I wonder if it'd be useful to have a utility statement (perhaps 'LISTEN'
without arguments?) that was essentially a no-op that _only_ checked
notifications. It'd make it easier to see why clients were issuing
apparently random statements, and could possibly be also excluded from
statement logging even when other things were logged. Would there be any
support for the idea of something like that? It might be project #2 for
me once I get around to posting the array_reverse function, if so.
Also: Is there any practical way Pg can ever support server-to-client
push for notifications? I assume the reasons why it doesn't at present
are to do with the protocol and with deadlock/concurrency issues at the
wire level ... is that so?
--
Craig Ringer
Craig Ringer <craig@postnewspapers.com.au> writes:
As Pg doesn't presently support client push for notifications arising
from LISTEN/NOTIFY, I'm wondering if anybody here has done any research
into the cheapest statement to issue to check for such notifications.
The backend certainly will push the notification. Maybe you just have
a client-side-software issue?
regards, tom lane
On Thu, Dec 10, 2009 at 8:23 AM, Craig Ringer
<craig@postnewspapers.com.au> wrote:
Hi
As Pg doesn't presently support client push for notifications arising from
LISTEN/NOTIFY, I'm wondering if anybody here has done any research into the
cheapest statement to issue to check for such notifications.
notifications are sent to the client as soon as they are generated and
the socket is not otherwise being used. psql just prints the
notifcation message following a query because that happens to be a
convenient place to do it. The only polling that has to be done (if
any) is 100% client side. There is an excellent example (in C) in the
documentation showing a method of waiting on the connection socket for
notification events when the connection is not in use by the client.
merlin
Tom Lane wrote:
Craig Ringer <craig@postnewspapers.com.au> writes:
As Pg doesn't presently support client push for notifications arising
from LISTEN/NOTIFY, I'm wondering if anybody here has done any research
into the cheapest statement to issue to check for such notifications.The backend certainly will push the notification. Maybe you just have
a client-side-software issue?
Testing with two plain old psql sessions to an 8.4.1 DB on linux (Ubuntu
9.10) here, I don't see the asynchronous notification until I send some
other command to the database from the client. It's the same over a
local UNIX socket or a loopback TCP/IP connection (with SSL).
Neither listener nor notifier are in a transaction, though it doesn't
seem to make any difference if I "BEGIN; NOTIFY test; COMMIT;" instead.
I only waited a few minutes after sending NOTIFY to see if psql would
notice, but if it's server-pushed it should be immediate, right?
Is this a psql limitation rather than a backend one? I know the JDBC
driver has such a limitation, and in fact the psql man page suggests
that it does too:
"
Whenever a command is executed, psql also polls for asynchronous noti‐
fication events generated by LISTEN [listen(7)] and NOTIFY [notify(7)].
"
... but if that is the case, is there any client software that _does_
support truly asynchronous receipt of notifications?
=====(SESSION 1, psql 8.4.1, local socket)====
test=> LISTEN x;
LISTEN
test=> SELECT current_timestamp;
now
-------------------------------
2009-12-10 23:54:16.988355+08
(1 row)
test=> SELECT current_timestamp;
now
-------------------------------
2009-12-10 23:55:04.872358+08
(1 row)
Asynchronous notification "x" received from server process with PID 14623.
test=>
=====(SESSION 2, psql 8.4.1, local socket)====
test=> SELECT current_timestamp;
now
-------------------------------
2009-12-10 23:54:34.252394+08
(1 row)
test=> NOTIFY x;
NOTIFY
test=>
*Self* notifications ( where listener == notifier ) are issued
immediately, but a NOTIFY to another backend doesn't seem to get pushed.
--
Craig Ringer
Merlin Moncure wrote:
On Thu, Dec 10, 2009 at 8:23 AM, Craig Ringer
<craig@postnewspapers.com.au> wrote:Hi
As Pg doesn't presently support client push for notifications arising from
LISTEN/NOTIFY, I'm wondering if anybody here has done any research into the
cheapest statement to issue to check for such notifications.notifications are sent to the client as soon as they are generated and
the socket is not otherwise being used. psql just prints the
notifcation message following a query because that happens to be a
convenient place to do it. The only polling that has to be done (if
any) is 100% client side. There is an excellent example (in C) in the
documentation showing a method of waiting on the connection socket for
notification events when the connection is not in use by the client.
Aah, that makes sense. So psql is being sent (and buffering) the
notification immediately, and could choose to report it promptly - say
by calling recv(...) on its socket periodically using an alarm signal.
Since it's not important for psql to report notifications immediately
though, it only checks after statements.
Right?
Anyway, thanks for the explanation, it's appreciated.
--
Craig Ringer
Craig Ringer <craig@postnewspapers.com.au> writes:
Tom Lane wrote:
The backend certainly will push the notification. Maybe you just have
a client-side-software issue?
Testing with two plain old psql sessions to an 8.4.1 DB on linux (Ubuntu
9.10) here, I don't see the asynchronous notification until I send some
other command to the database from the client. It's the same over a
local UNIX socket or a loopback TCP/IP connection (with SSL).
psql is not too bright about that; when it's waiting for user input it
just waits. But I don't think it matters because nobody is going to
write an application that depends on this in psql anyway. If you are
writing something that uses libpq directly, it's certainly possible to
watch for incoming notifies along with whatever else your event loop is
watching for. (I had an application that did so a dozen years ago...)
regards, tom lane
Right?
Right. The way I do it, very roughly:
Pqconnectdb(...)
PQexec( mDbConn, "listen notify" );
int dbsock = PQsocket( mDbConn );
mKq = kqueue();
struct kevent kev[1], kevs[1];
EV_SET( &kev[0], dbsock, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0 );
kevent( mKq, kev, 1, NULL, 0, NULL );
while( true ) {
bool needsnotify = false;
int evtcnt = kevent( mKq, NULL, 0, kevs, 1, NULL );
if( evtcnt == 1 && kevs[i].filter == EVFILT_READ && kevs[i].ident ==
dbsock ) {
while( true ) {
PQconsumeInput( mDbConn );
PGnotify * notify = PQnotifies( mDbConn );
if( notify ) {
free( notify );
needsnotify = true;
}
else
break;
}
}
if( needsnotify )
// go off & query the db & handle updates here
}
Of course you could also use select to wait on the socket.
--
Scott Ribe
scott_ribe@killerbytes.com
http://www.killerbytes.com/
(303) 722-0567 voice
Scott, Tom, Merlin:
Thanks for the comments and help. It's all sorted now - the origin of
the confusion was some outdated information in the JDBC driver
documentation.
The question arose because I was originally looking at polling from JDBC
(which I know I forgot to mention), where the docs state that:
"A key limitation of the JDBC driver is that it cannot receive
asynchronous notifications and must poll the backend to check if any
notifications were issued."
http://jdbc.postgresql.org/documentation/84/listennotify.html
.... and show a `SELECT 1' being issued to push any notifications.
I'd assumed that was a JDBC limitation until I tested with psql and
found that it, too, required some kind of client-initiated communication
to see NOTIFY events, at which point I began wondering if the backend
pushed them at all rather than waiting for client interaction. Hence my
question.
Anyway, as pointed out, psql just doesn't bother polling for
notifications because it's not important for psql, but it could if it
needed to - the notifications are waiting in its recieve buffer for it
to notice and care.
As for the JDBC driver - it turns out that the documentation is
out-of-date and/or misleading. The JDBC driver *does* support reading
notifications the backend has pushed to its receive buffer, and does
*not* have to poll the backend or issue a statement to receive
notifications. Some searching suggests that this changed in 8.0 or 8.1 .
The documentation needs adjusting, so I've sent a patch to it off to the
JDBC folks.
--
Craig Ringer
Hi folks
Just to follow up on an earlier discussion on -general that turns out to
be JDBC-specific: it turns out that there _is_ a need to poll for
notifications using a dummy statement when a Java/JDBC client is using
an SSL socket.
The short version is that the JDBC driver can't check an SSL socket to
see if any data is availible for reading - the check always returns 0
due to a limitation in the underling SSLSocket provided by the JRE. So a
JDBC client using SSL must still send dummy statements to get notifications.
So my question is: is there any particularly low-overhead statement that
might be suitable for generating pointless client/server chat to check
for received async notifications? Should I just use "SELECT 1" ? Or
would I be better off using a SHOW statement like "SHOW role" to avoid
creating a snapshot etc?
I had a look at the v3 protocol documentatation and didn't see any sort
of "echo" or "ping"-type message that might be used to (a) test the
server for aliveness and (b) guarantee readable data of a known size on
the client's input stream. So the polling looks like it has to be done
at the SQL level not the protocol level.
=== alternative: blocking getNotifications() ===
An alternative is to provide an alternate form of getNotifications()
that can block. It'd be unsynchronized, being intended for a dedicated
thread in the app to use to poll for notifications. It'd call a blocking
equivalent to QueryExecutorImpl.processNotifies() ( let's call it
waitForNoitifies() ) that didn't check available() before attempting to
read from the input stream.
The problem here is that while PGStream.readChar() as called from
QueryExecutorImpl.waitForNotifies() was blocked waiting for input,
someone else in another thread might try to read from the PGStream while
doing normal work. PGStream would have to be able to block that read
until the readChar() from processNotifies() returned, AND would have to
be able to push the result of readChar() back onto the
VisibleBufferedInputStream used by PGStream if it wasn't an async
notification message.
I don't know how to do that without incurring plenty of nasty
synchronization overhead.
So, what I'm wondering is if it's worth having an alternative stream
class, say PGSynchronizedStream, that extends a PGStream with thread
safety. The driver user could, via the PGConnection interface, request
that blocking notification checking be enabled, causing the usual
PGStream to be replaced with PGSynchronizedStream. It would then be
possible to call something like getNotificationsBlocking() to trigger
QueryExecutorImpl.waitForNotifies() as described above.
The only other change needed to support this would be add a pushChar()
method to VisibleBufferedInputStream to permit input the blocking
notification checker read but didn't want to be pushed back into the
input stream.
This way, if you wanted async notifications over an SSL socket you'd
have to pay the price of some read-synchronization overhead in
PGSynchronizedStream, but otherwise not much would change anywhere in
the code.
Is this crazy? Is there something obvious I've missed?
=== Why can't JDBC/SSL just test for data ready to read, anyway? ===
The server can send async notifications over an SSL socket, no problem.
It generates an appropriate SSL message via the ssl library and sends it
down the wire.
A Java SSL client, though, cannot check to see if it can read from an
SSL socket without blocking. InputStream.available() always returns zero
on a Java SSL socket, because while there may be data in the underlying
connection buffer, the SSL client isn't sure if (a) it's a full SSL
message, and (b) if that message contains data for the client. It
doesn't seem to want to do the processing and buffering required to
figure that out in the available() call.
It's an annoying issue. In theory there's nothing that prevents
non-blocking I/O on SSL sockets - OpenSSL can do it, with a few quirks.
Java's SSLSocket can't do it, though. To get non-blocking SSL in Java
you apparently have to completely re-write your client using java.nio
async I/O and use the SSLEngine to do manual SSL handling on top of
that. There's no standard friendly-to-use non-blocking SSL socket
wrapper on top of that, and direct use of SSLEngine is ... "interesting".
There are 3rd party implementations of wrappers that make a java.nio &
SSLEngine-based system look like a normal SSLSocket, like ScalableSSL's
SSLSocketChannel. that brings behaviour much like OpenSSL's
almost-transparent non-blocking SSL - ie it looks like a normal
non-blocking socket except for a few quirks re handshaking.
SSLSocketChannel still doesn't do available() checking, though the docs
say it can be added.
Even if that was sorted out though, SSLEngine is also present only in
JDK 1.5 and above, so the JDBC driver can't use it for a while yet.
Show quoted text
On 11/12/2009 11:39 PM, Craig Ringer wrote:
Scott, Tom, Merlin:
Thanks for the comments and help. It's all sorted now - the origin of
the confusion was some outdated information in the JDBC driver
documentation.The question arose because I was originally looking at polling from JDBC
(which I know I forgot to mention), where the docs state that:"A key limitation of the JDBC driver is that it cannot receive
asynchronous notifications and must poll the backend to check if any
notifications were issued."http://jdbc.postgresql.org/documentation/84/listennotify.html
.... and show a `SELECT 1' being issued to push any notifications.
I'd assumed that was a JDBC limitation until I tested with psql and
found that it, too, required some kind of client-initiated communication
to see NOTIFY events, at which point I began wondering if the backend
pushed them at all rather than waiting for client interaction. Hence my
question.Anyway, as pointed out, psql just doesn't bother polling for
notifications because it's not important for psql, but it could if it
needed to - the notifications are waiting in its recieve buffer for it
to notice and care.As for the JDBC driver - it turns out that the documentation is
out-of-date and/or misleading. The JDBC driver *does* support reading
notifications the backend has pushed to its receive buffer, and does
*not* have to poll the backend or issue a statement to receive
notifications. Some searching suggests that this changed in 8.0 or 8.1 .
The documentation needs adjusting, so I've sent a patch to it off to the
JDBC folks.--
Craig Ringer
On 11/12/2009 11:39 PM, Craig Ringer wrote:
As for the JDBC driver - it turns out that the documentation is
out-of-date and/or misleading. The JDBC driver *does* support reading
notifications the backend has pushed to its receive buffer, and does
*not* have to poll the backend or issue a statement to receive
notifications.
Update: The above is true only for non-SSL connections. For SSL
connections you still have to send dummy statements.
--
Craig Ringer
So my question is: is there any particularly low-overhead statement that
might be suitable for generating pointless client/server chat to check for
received async notifications? Should I just use "SELECT 1" ? Or would I be
better off using a SHOW statement like "SHOW role" to avoid creating a
snapshot etc?
Does the jdbc driver work with EmptyQueryResponse? At the protocol
level, if you send a Query message with an all-whitespace sql string,
the backend is supposed to reply with a single EmptyQueryResponse
message and then issue ReadyForQuery again. I have a feeling that jdbc
is probably "too smart" for this to work, but it could be worth a
shot.
---
Maciek Sakrejda | Software Engineer | Truviso
1065 E. Hillsdale Blvd., Suite 230
Foster City, CA 94404
(650) 242-3500 Main
(650) 242-3501 F
msakrejda@truviso.com
www.truviso.com
Maciek Sakrejda wrote:
Does the jdbc driver work with EmptyQueryResponse? At the protocol
level, if you send a Query message with an all-whitespace sql string,
the backend is supposed to reply with a single EmptyQueryResponse
message and then issue ReadyForQuery again. I have a feeling that jdbc
is probably "too smart" for this to work, but it could be worth a
shot.
Even if the JDBC driver avoids sending such a query, it'd be a handy
behavior to use if explicit polling was implemented in the driver
interface. Send an empty Query message, read() from the buffer now that
you know there's something in it (the server's empty query response) and
see if there are any async notifications in the buffer before the empty
query response.
Thanks for the tip. If the JDBC folks ever stick their heads up here to
comment on any of this or to respond to the docs patches I've sent them
I'll see if they'd accept a patch for this and put one together if so.
It wouldn't be as efficient as purely client-side polling as it'd
require a network round trip, but it'd be a whole lot simpler and would
avoid spamming the db logs.
--
Craig Ringer
Craig Ringer wrote:
=== Why can't JDBC/SSL just test for data ready to read, anyway? ===
Yeah, the SSL interface does suck :(
I am wondering if we should look at a dedicated read thread per
connection. In the past I avoided this because it doesn't scale to large
numbers of connections, and there may be some overhead from context
switching on I/O (though I haven't measured this). But between issues
with notifications+SSL, and the increasing number of reports of I/O
deadlocks, maybe we should look at it again.
-O
Oliver Jowett wrote:
Craig Ringer wrote:
=== Why can't JDBC/SSL just test for data ready to read, anyway? ===
Yeah, the SSL interface does suck :(
I am wondering if we should look at a dedicated read thread per
connection. In the past I avoided this because it doesn't scale to large
numbers of connections, and there may be some overhead from context
switching on I/O (though I haven't measured this). But between issues
with notifications+SSL, and the increasing number of reports of I/O
deadlocks, maybe we should look at it again.
That'll double the number of threads required for apps that already use
one thread per JDBC connection, though. Threads in Java are cheap, but
they're not free.
Are there likely to be circumstances where it's not acceptable for the
JDBC driver to require its own internal thread(s)? Where it _has_ to be
able to do its work on one thread?
Does the Pg JDBC driver have to be able to support green threads? If so,
it can't block in one thread and expect another thread's actions to
unblock the first thread, since they're both really the same OS thread
and when one blocks, they all block.
I'm a bit concerned that adding the requirement of one thread per JDBC
connection will do nasty things to some workloads that are currently
quite happy with the way things are. Ugly as the current SSL situation
and the handling of listen/notify is, it *is* a corner case and it does
have a decent workaround.
The other deadlocks you mention are more worrying, and I've seen
discussion here that suggests that some of the issues just aren't
fixable without the ability to read from a socket without blocking.
Consuming accumulating NOTICE messages so they don't fill up the read
buffer, for example.
I suspect that it'd be wise to make the use of one thread per connection
optional because of concerns like the above. I do think it might be
very, very nice to have, though.
I'll see if I can hack together a patch for the approach I was talking
about in my previous patch and see if it works.
Thinking less conservatively: If the java.nio API (JDK>=1.4) and
SSLEngine (JDK>=1.5) were used - for the JDBC4 driver only - then it'd
be possible to provide a pool of one or more threads dedicated to
handling the network chat for the driver's connection(s), by using the
connections in non-blocking mode via SelectableChannel:
http://java.sun.com/j2se/1.5.0/docs/api/java/nio/channels/SelectableChannel.html
http://java.sun.com/j2se/1.5.0/docs/api/java/nio/channels/Selector.html
then using SSLEngine on top to maintain the SSL state of each JDBC
connection. This would basically turn the JDBC driver connection/chat
handling into a state machine.
The popular SSLSocketChannel that hides much of the SSL gruesome-ness of
nio and SSLEngine use appears to be a SelectableChannel:
http://www.rmiproxy.com/ScalableSSL/javadoc/scalablessl/SSLSocketChannel.html
... so there are possibilities for hiding SSL gore behind that.
Of course, that's not exactly a trivial conversion (understatement of
the century award) even with SSLSocketChannel to hide much of the SSL
ugly-ness. It'd certainly be a nice way to keep connections separated
from threads working with those connections, though, and let apps use
anything from one thread for all work through to one thread per JDBC
connection. The Java 5 ExecutorService even provides a nice way to
configure and control that sort of thing.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ExecutorService.html
So ... if I was to go frothing, drooling mad and start writing a new
JDBC driver now, that's probably how I'd do it - as a PostgreSQL
protocol state machine with an associated SelectableChannel for comms,
controlled via a Selector using an ExecutorService to manage the work.
--
Craig Ringer
Maciek Sakrejda wrote:
So my question is: is there any particularly low-overhead statement that
might be suitable for generating pointless client/server chat to check for
received async notifications? Should I just use "SELECT 1" ? Or would I be
better off using a SHOW statement like "SHOW role" to avoid creating a
snapshot etc?Does the jdbc driver work with EmptyQueryResponse?
It seems to.
At the protocol
level, if you send a Query message with an all-whitespace sql string,
the backend is supposed to reply with a single EmptyQueryResponse
message and then issue ReadyForQuery again. I have a feeling that jdbc
is probably "too smart" for this to work, but it could be worth a
shot.
The JDBC driver isn't too smart for this - it doesn't catch the empty
query, but instead merrily passes it on to the backend as part of an
extended query request.
See the attached text file - it shows the client/server chat for one
such round trip.
So: the server doesn't consider the empty query an error, so it won't
affect any currently running transactions. It's a cheap single-message
each way round-trip, and it doesn't have any planning or snapshot
creation costs. Looks good to me :-)
Since I can't imagine an app _needing_ the JDBC driver to ignore empty
queries, is this something that it might be reasonable to document as
expected behavior from the driver - that it _will_ send even empty
queries to the server?
Thanks _VERY_ much for the tip. You've saved me and my logs quite a bit
of pain. It's not as good as client-side-only polling, but it's a whole
nicer than a full dummy statement, and I can easily do it only when an
SSL connection is detected.
--
Craig Ringer
Attachments:
trace.txttext/plain; name=trace.txtDownload
Hello,
I don't know exactly whole topic, but I assume you want to check if there
are some available bytes on SSL socket stream to check for some incoming
messages, but you can't make this, as the available() always return 0.
There is some solution, to check if something is on wire you need ask the
underlying "plain" socket. Currently in driver there is
PGStream.changeSocket, which replaces and makes unavailable underlying
socket with it SSL wrapper, in my opinion this could be renamed and
reconstructed to something like wrapSocket, or
it could be better to allow them creating of SSL socket, by passing the
SSLFactory to it (some solution to save old socket, and make method name
accurate). This could allow PGStream to store "plain" socket, use it the
hasMessagePending (and expose the method
e.g. getPlainAvailbaleBytes() { return
plainSocket.getInputStream().available() }).
BUT...
One of disadvantage of checking plain socket is that available or read
uses "under" SSL Layer bytes, which include encrypted data, handshaking
data (the SSL supports renegotiation and session changes...?), and other
protocol specific, with no sense for application. But generally when whole
SSL socket bytes are read, then plain socket _should_ be "empty" too.
I'll try to submit proposed patch, if you think this is a some solution.
Kind regards,
Radosław Smogura
On Sat, 19 Dec 2009 15:00:35 +0800, Craig Ringer
<craig@postnewspapers.com.au> wrote:
Hi folks
Just to follow up on an earlier discussion on -general that turns out to
be JDBC-specific: it turns out that there _is_ a need to poll for
notifications using a dummy statement when a Java/JDBC client is using
an SSL socket.The short version is that the JDBC driver can't check an SSL socket to
see if any data is availible for reading - the check always returns 0
due to a limitation in the underling SSLSocket provided by the JRE. So a
JDBC client using SSL must still send dummy statements to get
notifications.So my question is: is there any particularly low-overhead statement that
might be suitable for generating pointless client/server chat to check
for received async notifications? Should I just use "SELECT 1" ? Or
would I be better off using a SHOW statement like "SHOW role" to avoid
creating a snapshot etc?I had a look at the v3 protocol documentatation and didn't see any sort
of "echo" or "ping"-type message that might be used to (a) test the
server for aliveness and (b) guarantee readable data of a known size on
the client's input stream. So the polling looks like it has to be done
at the SQL level not the protocol level.=== alternative: blocking getNotifications() ===
An alternative is to provide an alternate form of getNotifications()
that can block. It'd be unsynchronized, being intended for a dedicated
thread in the app to use to poll for notifications. It'd call a blocking
equivalent to QueryExecutorImpl.processNotifies() ( let's call it
waitForNoitifies() ) that didn't check available() before attempting to
read from the input stream.The problem here is that while PGStream.readChar() as called from
QueryExecutorImpl.waitForNotifies() was blocked waiting for input,
someone else in another thread might try to read from the PGStream while
doing normal work. PGStream would have to be able to block that read
until the readChar() from processNotifies() returned, AND would have to
be able to push the result of readChar() back onto the
VisibleBufferedInputStream used by PGStream if it wasn't an async
notification message.I don't know how to do that without incurring plenty of nasty
synchronization overhead.So, what I'm wondering is if it's worth having an alternative stream
class, say PGSynchronizedStream, that extends a PGStream with thread
safety. The driver user could, via the PGConnection interface, request
that blocking notification checking be enabled, causing the usual
PGStream to be replaced with PGSynchronizedStream. It would then be
possible to call something like getNotificationsBlocking() to trigger
QueryExecutorImpl.waitForNotifies() as described above.The only other change needed to support this would be add a pushChar()
method to VisibleBufferedInputStream to permit input the blocking
notification checker read but didn't want to be pushed back into the
input stream.This way, if you wanted async notifications over an SSL socket you'd
have to pay the price of some read-synchronization overhead in
PGSynchronizedStream, but otherwise not much would change anywhere in
the code.Is this crazy? Is there something obvious I've missed?
=== Why can't JDBC/SSL just test for data ready to read, anyway? ===
The server can send async notifications over an SSL socket, no problem.
It generates an appropriate SSL message via the ssl library and sends it
down the wire.
A Java SSL client, though, cannot check to see if it can read from an
SSL socket without blocking. InputStream.available() always returns zero
on a Java SSL socket, because while there may be data in the underlying
connection buffer, the SSL client isn't sure if (a) it's a full SSL
message, and (b) if that message contains data for the client. It
doesn't seem to want to do the processing and buffering required to
figure that out in the available() call.It's an annoying issue. In theory there's nothing that prevents
non-blocking I/O on SSL sockets - OpenSSL can do it, with a few quirks.
Java's SSLSocket can't do it, though. To get non-blocking SSL in Java
you apparently have to completely re-write your client using java.nio
async I/O and use the SSLEngine to do manual SSL handling on top of
that. There's no standard friendly-to-use non-blocking SSL socket
wrapper on top of that, and direct use of SSLEngine is ...
"interesting".
There are 3rd party implementations of wrappers that make a java.nio &
SSLEngine-based system look like a normal SSLSocket, like ScalableSSL's
SSLSocketChannel. that brings behaviour much like OpenSSL's
almost-transparent non-blocking SSL - ie it looks like a normal
non-blocking socket except for a few quirks re handshaking.
SSLSocketChannel still doesn't do available() checking, though the docs
say it can be added.Even if that was sorted out though, SSLEngine is also present only in
JDK 1.5 and above, so the JDBC driver can't use it for a while yet.On 11/12/2009 11:39 PM, Craig Ringer wrote:
Scott, Tom, Merlin:
Thanks for the comments and help. It's all sorted now - the origin of
the confusion was some outdated information in the JDBC driver
documentation.The question arose because I was originally looking at polling from
JDBC
(which I know I forgot to mention), where the docs state that:
"A key limitation of the JDBC driver is that it cannot receive
asynchronous notifications and must poll the backend to check if any
notifications were issued."http://jdbc.postgresql.org/documentation/84/listennotify.html
.... and show a `SELECT 1' being issued to push any notifications.
I'd assumed that was a JDBC limitation until I tested with psql and
found that it, too, required some kind of client-initiated
communication
to see NOTIFY events, at which point I began wondering if the backend
pushed them at all rather than waiting for client interaction. Hence my
question.Anyway, as pointed out, psql just doesn't bother polling for
notifications because it's not important for psql, but it could if it
needed to - the notifications are waiting in its recieve buffer for it
to notice and care.As for the JDBC driver - it turns out that the documentation is
out-of-date and/or misleading. The JDBC driver *does* support reading
notifications the backend has pushed to its receive buffer, and does
*not* have to poll the backend or issue a statement to receive
notifications. Some searching suggests that this changed in 8.0 or 8.1
.
The documentation needs adjusting, so I've sent a patch to it off to
the
Show quoted text
JDBC folks.
--
Craig Ringer
On 21/12/2009 5:41 PM, rsmogura@softperience.pl wrote:
One of disadvantage of checking plain socket is that available or read
uses "under" SSL Layer bytes, which include encrypted data, handshaking
data (the SSL supports renegotiation and session changes...?), and other
protocol specific, with no sense for application. But generally when whole
SSL socket bytes are read, then plain socket _should_ be "empty" too.
Yes - when the plain socket is empty, the SSL socket is also empty. That
does not imply the inverse, though.
If the plain socket is NOT empty, that does not mean that the SSL socket
is also NOT empty. As you said, the plain socket underlying the SSL
socket may have bytes available() but they're just an SSL re-negotiation
request or the like. So if you try to read from the SSL socket in this
case, you will still block, even though the underlying socket did have
bytes available to read.
For available() on an ssl socket to work, you would need to implement it
by using the underlying SSL implementation to read any data buffered on
the plain socket (if any), process it, and return the amount of client
data in it if any. So if the underlying socket's available() returns 0,
your SSL available() returns 0 also. Otherwise it reads what's there,
decodes it if it can, and returns how much readable user data it got.
Unfortunately this can't be done with Java's SSLSocket, since you have
no access to the underlying SSL engine implementation. It *can* be done
with the Java 5 java.nio API and SSLEngine, possibly via a wrapper like
SSLSocketChannel, though. In fact, if you're building on java.nio and
SSLSocketChannel you can use exactly the approach described above.
Really, the requirements to make this work are:
- a non-blocking SSL socket; or
- a thread that can afford to block on the socket
--
Craig Ringer
(Re-sent, this time to the list)
On 21/12/2009 9:13 PM, rsmogura@softperience.pl wrote:
I haven't seen 1.4.2 JDK for ages :), but there is method
javax.net.ssl.SSLSocketFactory.createSocket that takes as the 1st argument
plain socket, there is no need to "forgot" plain socket (and it can be
used, actually it's used for SSL communication with lazy handshake
FTPS/SMTPS...).
That's how the PgJDBC driver works - it wraps an existing socket in an
SSLSocket when SSL negotiation begins. Pg always begins with a plain
text connection - it doesn't have a separate SSL port - so it has to
begin with a plaintext connection and then request SSL negotiation using
a plaintext message.
Unfortunatly plainSocket can have some available bytes, but sslsoket.read
can return EOF. But this was i saw on SUN JDK is that read(new byte[0]) on
SSL socket causes process plain data.
I'm pretty sure it also blocks if there's nothing to read. You'd have to
run that in a background thread to force processing of any pending data
by the SSL engine. If you're going to do that, you may as well just have
a dedicated background thread to do all the reading from the socket...
--
Craig Ringer
Import Notes
Reply to msg id not found: e6af340fadaaf23345fdf9aa6c2fed16@smogura-softworks.eu
On Mon, 21 Dec 2009 21:44:00 +0800, Craig Ringer
<craig@postnewspapers.com.au> wrote:
(Re-sent, this time to the list)
On 21/12/2009 9:13 PM, rsmogura@softperience.pl wrote:
I haven't seen 1.4.2 JDK for ages :), but there is method
javax.net.ssl.SSLSocketFactory.createSocket that takes as the 1st
argument
plain socket, there is no need to "forgot" plain socket (and it can be
used, actually it's used for SSL communication with lazy handshake
FTPS/SMTPS...).That's how the PgJDBC driver works - it wraps an existing socket in an
SSLSocket when SSL negotiation begins. Pg always begins with a plain
text connection - it doesn't have a separate SSL port - so it has to
begin with a plaintext connection and then request SSL negotiation using
a plaintext message.
But I think we can leave plain socket somewhere to call available().
Unfortunatly plainSocket can have some available bytes, but
sslsoket.read
can return EOF. But this was i saw on SUN JDK is that read(new byte[0])
on
SSL socket causes process plain data.I'm pretty sure it also blocks if there's nothing to read. You'd have to
run that in a background thread to force processing of any pending data
by the SSL engine. If you're going to do that, you may as well just have
a dedicated background thread to do all the reading from the socket...
It's not block, I tested on HTTPS.
I think the background thread, especially timer thread is better solution
then sending data to server.
2009/12/21 <rsmogura@softperience.pl>
I think the background thread, especially timer thread is better solution
then sending data to server.
I disagree because this task (receive notifications) is not an often task.
There are many many installations that do not need it. And prefer to
implement such a task in inefficient way than to make all the connections in
the world more complex and resource-consuming.
Best regards, Vitalii Tymchyshyn