Re: [pylons-discuss] Subscribing to connection lost / failed transaction event?

2022-03-15 Thread 'Jonathan Vanasco' via pylons-discuss
100% with you. That's why I wrote it isn't "really" possible. It is 
possible, just under limited conditions. IIRC, one could also use the COMET 
chunking technique to faciliate this - though that would likely cause some 
client side issues.  If you're doing a direct browser-to-server connection, 
one can somewhat monitor the situation as you described - but most 
deployments use proxies, gateways, load balancers, wsgi, etc.  It's also 
possible the client has an open connection / keepalive into your LAN, but 
one of the services on your network timed out and "dropped" the 
connection.  From the user's vantage, the connection is active; but from 
the server's vantage it's dropped. 

In any event, my point should have been more clear: "Detecting client 
disconnects" isn't a **standard** concept across any web frameworks or 
technologies, and this isn't a deficiency of Pyramid.  It's possible to 
somewhat detect, but it's not something I've ever seen natively supported 
in a framework - whether it's Python, Go, C, Java, Ruby, PHP, etc - because 
of how applications are often deployed in layered environments.

On Tuesday, March 8, 2022 at 10:28:05 PM UTC-5 Bert JW Regeer wrote:

> I would disagree with this statement, but only narrowly, once you start 
> talking about proxies and reverse proxies and things of that nature, it 
> becomes much harder because the client isn’t directly connected anymore. In 
> most of todays environments you are right that it is really hard to know if 
> a remote client went away or if a request was actually successfully 
> returned to the remote client (and not just any intermediary 
> proxies/servers that may be buffering the request).
>
> It is hard to do in Python based threaded servers because even though the 
> code is running in a thread, and the main thread knows about the connection 
> being dropped, there’s no way for the main thread to cancel the running of 
> the worker thread and notify it. pthread_cancel does not work, and there is 
> no good way to signal to the thread to stop running code or to interrupt 
> it, especially during heavy computation. HTTP/1.1 also makes this somewhat 
> more difficult, in that the only way for the main thread to know is to 
> continue telling the kernel it wants to read from the socket the client is 
> connected on, with HTTP pipelining the client can send multiple requests at 
> once, and we’d have to buffer those requests, otherwise the call to 
> select()/poll() becomes a busy loop because each time we call 
> select()/poll() the OS would tell us the socket is ready for reading. 
> Thankfully HTTP pipelining these days is very rare because of incredibly 
> poor support for having multiple requests in flight, while the response 
> being returned would close the connection due to an error (the client would 
> have to retry any in-flight requests that were pipelined but not replied 
> to).
>
> You can emulate it somewhat by checking to see during various points of 
> computation whether the client has gone away, and then manually acting upon 
> it, and waitress has support for that. It is not enabled by default because 
> of the HTTP pipelining issue, and the issue of spinning on select(), but it 
> is configurable to attempt to buffer up to X requests by setting the flag 
> `channel_request_lookahead` to something that is non-zero.
>
> There’s just no predefined way to do it across WSGI servers, nor does 
> pyramid_tm provide any helpers for it since it can’t add those checks for 
> you as you are generating your response. This is a waitress extension.
>
> The feature was introduced in this PR:
>
> https://github.com/Pylons/waitress/pull/310
>
> I don’t think there’s good example of how to use it in the documentation, 
> but here’s a quick and dirty example:
>
> import logging
> import time
>
> log = logging.getLogger(__name__)
>
>
> def application(environ, start_response):
> check = environ["waitress.client_disconnected"]
>
> for i in range(10):
> # do some computation
> log.debug("Starting computation for %d", i)
> time.sleep(2)
> log.debug("Completed computation for %d", i)
>
> if check():
> log.debug("Remote client went away, processed %d items", i + 1)
> break
>
> start_response(
> "200 OK",
> [
> ("Content-Type", "application/octet-stream"),
> ],
> )
>
> return [b"work completed"]
>
>
> if __name__ == "__main__":
> import waitress
>
> logging.basicConfig(
> format="%(asctime)-15s %(levelname)-8s %(name)s %(message)s",
> level=logging.DEBUG,
> )
>
> waitress.serve(application, channel_request_lookahead=5)
>
> Now start this process, and then run curl but hit Ctrl + C on curl a 
> second or two after you start curl:
>
> You should see something like the following:
>
> python client_disconnected.py
> 2022-03-08 20:12:01,081 INFO waitress Serving on http://0.0.0.0:8080
> 2022-03-08 20:12:04,206 DEBUG __main__ Starting computation for 0
> 2022-03-08 20:12:06,211 DEBUG __main__ Completed computation for 0
> 2022-03-08 20:12:06,211 

Re: [pylons-discuss] Subscribing to connection lost / failed transaction event?

2022-03-08 Thread Bert JW Regeer
I would disagree with this statement, but only narrowly, once you start talking 
about proxies and reverse proxies and things of that nature, it becomes much 
harder because the client isn’t directly connected anymore. In most of todays 
environments you are right that it is really hard to know if a remote client 
went away or if a request was actually successfully returned to the remote 
client (and not just any intermediary proxies/servers that may be buffering the 
request).

It is hard to do in Python based threaded servers because even though the code 
is running in a thread, and the main thread knows about the connection being 
dropped, there’s no way for the main thread to cancel the running of the worker 
thread and notify it. pthread_cancel does not work, and there is no good way to 
signal to the thread to stop running code or to interrupt it, especially during 
heavy computation. HTTP/1.1 also makes this somewhat more difficult, in that 
the only way for the main thread to know is to continue telling the kernel it 
wants to read from the socket the client is connected on, with HTTP pipelining 
the client can send multiple requests at once, and we’d have to buffer those 
requests, otherwise the call to select()/poll() becomes a busy loop because 
each time we call select()/poll() the OS would tell us the socket is ready for 
reading. Thankfully HTTP pipelining these days is very rare because of 
incredibly poor support for having multiple requests in flight, while the 
response being returned would close the connection due to an error (the client 
would have to retry any in-flight requests that were pipelined but not replied 
to).

You can emulate it somewhat by checking to see during various points of 
computation whether the client has gone away, and then manually acting upon it, 
and waitress has support for that. It is not enabled by default because of the 
HTTP pipelining issue, and the issue of spinning on select(), but it is 
configurable to attempt to buffer up to X requests by setting the flag 
`channel_request_lookahead` to something that is non-zero.

There’s just no predefined way to do it across WSGI servers, nor does 
pyramid_tm provide any helpers for it since it can’t add those checks for you 
as you are generating your response. This is a waitress extension.

The feature was introduced in this PR:

https://github.com/Pylons/waitress/pull/310

I don’t think there’s good example of how to use it in the documentation, but 
here’s a quick and dirty example:

import logging
import time

log = logging.getLogger(__name__)


def application(environ, start_response):
check = environ["waitress.client_disconnected"]

for i in range(10):
# do some computation
log.debug("Starting computation for %d", i)
time.sleep(2)
log.debug("Completed computation for %d", i)

if check():
log.debug("Remote client went away, processed %d items", i + 1)
break

start_response(
"200 OK",
[
("Content-Type", "application/octet-stream"),
],
)

return [b"work completed"]


if __name__ == "__main__":
import waitress

logging.basicConfig(
format="%(asctime)-15s %(levelname)-8s %(name)s %(message)s",
level=logging.DEBUG,
)

waitress.serve(application, channel_request_lookahead=5)

Now start this process, and then run curl but hit Ctrl + C on curl a second or 
two after you start curl:

You should see something like the following:

python client_disconnected.py
2022-03-08 20:12:01,081 INFO waitress Serving on http://0.0.0.0:8080
2022-03-08 20:12:04,206 DEBUG__main__ Starting computation for 0
2022-03-08 20:12:06,211 DEBUG__main__ Completed computation for 0
2022-03-08 20:12:06,211 DEBUG__main__ Starting computation for 1
2022-03-08 20:12:08,215 DEBUG__main__ Completed computation for 1
2022-03-08 20:12:08,216 DEBUG__main__ Remote client went away, processed 2 
items
2022-03-08 20:12:08,217 INFO waitress Client disconnected while serving /

An app developer who knows that the clients are always going to be directly 
connected, can add code similar to the above in their response code and do 
these checks manually during their computation, and if they raise an error, 
pyramid_tm will appropriate abort the transaction, and pyramid should run the 
exception view machinery (although that response will never make it back to the 
client, it should be possible to use it at that point to do any extra cleanup 
or whatnot though)

Hopefully Andrew Free this helps somewhat, in that it is possible, it’s just 
extra code you have to write and be aware of, it is not something that comes 
for free, and requires that you use waitress, and it requires that you set the 
`channel_request_lookhead` flag, and it requires that you know your clients are 
directly connected.

Caveat emptor.

Thanks,
Bert JW Regeer

> On Mar 7, 2022, at 13:05, Jonathan Vanasco  wrote:

Re: [pylons-discuss] Subscribing to connection lost / failed transaction event?

2022-03-07 Thread Jonathan Vanasco
Just to clarify the above comment, this concept isn't really possible with 
any internet technology in general, not just Pyramid. 
On Thursday, February 17, 2022 at 8:35:25 PM UTC-5 Bert JW Regeer wrote:

> No, this is not possible *.
>
> * Except under some very narrow circumstances, but none that are easy to 
> use or directly supported in Pyramid
>
> On Feb 17, 2022, at 13:12, Andrew Free  wrote:
>
> Is there a way to subscribe to any events of a dropped/lost connection? 
>
> For example, if the user closed the browser window in the middle of a 
> request. I am using pyramid_tm and having a hard time finding a method 
> for this. I just want to run some code based on the request object state in 
> the event that the response doesn't make it back to the client and the 
> transaction does not complete. I've looked into the exception_view_config 
> and this doesn't appear to help. Would a tween be the best way to handle 
> this?
> Thanks. 
>
>
> -- 
> You received this message because you are subscribed to the Google Groups 
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to pylons-discus...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/pylons-discuss/cab03e28-c370-4dcb-917a-7b5d36e7a86fn%40googlegroups.com
>  
> 
> .
>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to pylons-discuss+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/113341b5-529b-4bb8-b1e8-5a3d28ce028dn%40googlegroups.com.


Re: [pylons-discuss] Subscribing to connection lost / failed transaction event?

2022-02-17 Thread Bert JW Regeer
No, this is not possible *.

* Except under some very narrow circumstances, but none that are easy to use or 
directly supported in Pyramid

> On Feb 17, 2022, at 13:12, Andrew Free  wrote:
> 
> Is there a way to subscribe to any events of a dropped/lost connection?
> 
> For example, if the user closed the browser window in the middle of a 
> request.  I am using pyramid_tm and having a hard time finding a method for 
> this. I just want to run some code based on the request object state in the 
> event that the response doesn't make it back to the client and the 
> transaction does not complete. I've looked into the exception_view_config and 
> this doesn't appear to help. Would a tween be the best way to handle this?
> Thanks.
> 
> 
> --
> You received this message because you are subscribed to the Google Groups 
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to pylons-discuss+unsubscr...@googlegroups.com 
> .
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/pylons-discuss/cab03e28-c370-4dcb-917a-7b5d36e7a86fn%40googlegroups.com
>  
> .

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to pylons-discuss+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/EDBC67C7-836E-425D-AFB7-95BDFFFC14FC%400x58.com.


signature.asc
Description: Message signed with OpenPGP


[pylons-discuss] Subscribing to connection lost / failed transaction event?

2022-02-17 Thread Andrew Free
Is there a way to subscribe to any events of a dropped/lost connection? 

For example, if the user closed the browser window in the middle of a 
request. I am using pyramid_tm and having a hard time finding a method for 
this. I just want to run some code based on the request object state in the 
event that the response doesn't make it back to the client and the 
transaction does not complete. I've looked into the exception_view_config 
and this doesn't appear to help. Would a tween be the best way to handle 
this?
Thanks. 

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to pylons-discuss+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/cab03e28-c370-4dcb-917a-7b5d36e7a86fn%40googlegroups.com.