Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport to 2.4.x: mod_h2 to return c1 to mpm_event #449

Closed
wants to merge 13 commits into from

Conversation

ylavic
Copy link
Member

@ylavic ylavic commented May 28, 2024

modules/http2/h2_session.c Outdated Show resolved Hide resolved
server/mpm/event/event.c Outdated Show resolved Hide resolved
include/httpd.h Outdated Show resolved Hide resolved
@icing
Copy link
Contributor

icing commented May 28, 2024

I'll check this out tomorrow.

@icing
Copy link
Contributor

icing commented May 29, 2024

This works excellent. I added some check defines where we can adapt the MPM magic once we have the value for the backport:

diff --git a/modules/http2/h2.h b/modules/http2/h2.h
index f496a6dcb2..ae0e845704 100644
--- a/modules/http2/h2.h
+++ b/modules/http2/h2.h
@@ -49,6 +49,12 @@ struct h2_stream;
 #define H2_USE_WEBSOCKETS       0
 #endif

+#if AP_MODULE_MAGIC_AT_LEAST(20211221, 19)
+#define H2_USE_STATE_PROCESS       1
+#else
+#define H2_USE_STATE_PROCESS       0
+#endif
+
 /**
  * The magic PRIamble of RFC 7540 that is always sent when starting
  * a h2 communication.
diff --git a/modules/http2/h2_c1.c b/modules/http2/h2_c1.c
index 0a3e8395ad..6ba6dffb1b 100644
--- a/modules/http2/h2_c1.c
+++ b/modules/http2/h2_c1.c
@@ -161,7 +161,11 @@ apr_status_t h2_c1_run(conn_rec *c)
                      * the Timeout behaviour instead of a KeepAliveTimeout
                      * See PR 63534.
                      */
+#if H2_USE_STATE_PROCESS
                     c->cs->state = CONN_STATE_PROCESS;
+#else
+                    c->cs->state = CONN_STATE_WRITE_COMPLETION;
+#endif
                     c->cs->sense = CONN_SENSE_WANT_READ;
                 }
                 break;
diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index 5add2ad2c1..e5809d59ac 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -1762,7 +1762,7 @@ static void unblock_c1_out(h2_session *session) {
     }
 }

-#if AP_MODULE_MAGIC_AT_LEAST(20211221, 19)
+#if H2_USE_STATE_PROCESS
 static int h2_send_flow_blocked(h2_session *session)
 {
     /* We are completely send blocked if either the connection window
@@ -1954,7 +1954,7 @@ apr_status_t h2_session_process(h2_session *session, int async,
                     break;
                 }
             }
-#if AP_MODULE_MAGIC_AT_LEAST(20211221, 19)
+#if H2_USE_STATE_PROCESS
             else if (async && h2_send_flow_blocked(session)) {
                 /* On a recent HTTPD, we can return to mpm c1 monitoring,
                  * as it does not treat all connections as having KeepAlive

I tested this with overwriting the definition using the following testcase in test_700_load_get.py:

    # test window sizes, connection and stream
    @pytest.mark.parametrize("connbits,streambits", [
        [10, 16],  # 1k connection window, 64k stream windows
        [10, 30],  # 1k connection window, huge stream windows
        [30, 8],  # huge conn window, 256 bytes stream windows
    ])
    def test_h2_700_20(self, env, connbits, streambits):
        assert env.is_live()
        n = 2000
        conns = 100
        parallel = 10
        args = [
            env.h2load,
            '-n', f'{n}', '-t', '1',
            '-c', f'{conns}', '-m', f'{parallel}',
            '-W', f'{connbits}',  # connection window bits
            '-w', f'{streambits}',  # stream window bits
            f'--connect-to=localhost:{env.https_port}',
            f'--base-uri={env.mkurl("https", "test1", "/")}',
            "/data-100k"
        ]
        r = env.run(args)
        self.check_h2load_ok(env, r, n)

This test fails quite reliably on a plain 2.4.x and succeeds with the changes in this PR.

@ylavic
Copy link
Member Author

ylavic commented May 29, 2024

This works excellent. I added some check defines where we can adapt the MPM magic once we have the value for the backport:

Great! So I bumped the MMN minor in trunk for the new CONN_STATE_'s and added your change, backported here now so we should be good ;)

@ylavic
Copy link
Member Author

ylavic commented May 29, 2024

Hm, something (related?) broke in the H2 tests (https://github.com/apache/httpd/actions/runs/9283938584/job/25545210350)..

@icing
Copy link
Contributor

icing commented May 29, 2024

Hm, something (related?) broke in the H2 tests (https://github.com/apache/httpd/actions/runs/9283938584/job/25545210350)..

Hmm, is this a return to the MPM during a graceful shutdown? Does it then close a connection in PROCESS state?

The test case does a graceful restart of the server during a download...

@icing
Copy link
Contributor

icing commented May 30, 2024

In case this is too complicated to "simply" fix in the mpm, I can make mod_http2 refrain from returning in case the server is stopping. There'd still be an edge case where the server gracefully shuts down with a connection being in processing already, but...

@icing
Copy link
Contributor

icing commented May 31, 2024

@ylavic if mpm_event cannot handle it, how about this:

diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index e5809d59ac..7618b64f77 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -1536,6 +1536,7 @@ static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *ms

 static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
 {
+    session->stopping = TRUE;
     switch (session->state) {
         case H2_SESSION_ST_DONE:
             /* nop */
@@ -1955,12 +1956,15 @@ apr_status_t h2_session_process(h2_session *session, int async,
                 }
             }
 #if H2_USE_STATE_PROCESS
-            else if (async && h2_send_flow_blocked(session)) {
+            else if (async && !session->stopping && h2_send_flow_blocked(session)) {
                 /* On a recent HTTPD, we can return to mpm c1 monitoring,
                  * as it does not treat all connections as having KeepAlive
                  * timing and being purgeable on load.
                  * By returning to the MPM, we do not block a worker
-                 * and async wait for the client send window updates. */
+                 * and async wait for the client send window updates.
+                 * We would also like to do this during a graceful shutdown,
+                 * but the MPM event implementation currentyl does not support
+                 * this. */
                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
                               H2_SSSN_LOG(APLOGNO(10502), session,
                               "BLOCKED, return to mpm c1 monitoring"));
diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h
index 2c8f334cce..1d1e334b1c 100644
--- a/modules/http2/h2_session.h
+++ b/modules/http2/h2_session.h
@@ -84,6 +84,7 @@ typedef struct h2_session {

     unsigned int reprioritize  : 1; /* scheduled streams priority changed */
     unsigned int flush         : 1; /* flushing output necessary */
+    unsigned int stopping      : 1; /* MPM is stopping gracefully */
     apr_interval_time_t  wait_us;   /* timeout during BUSY_WAIT state, micro secs */

     struct h2_push_diary *push_diary; /* remember pushes, avoid duplicates */

@ylavic
Copy link
Member Author

ylavic commented May 31, 2024

Sorry was busy$, I don't see why mpm_event would kill connections in PROCESS state. Let me try to reproduce..

@ylavic
Copy link
Member Author

ylavic commented May 31, 2024

Isn't it possible that the connection is rather returned to the MPM in WRITE_COMPLETION=>KEEPALIVE state (not a zero-window test/case it seems)?
Though in this case it should behave the same as before, was the test stable/reliable before this change?
Also the test in trunk does not fail, but it might be a matter running it multiple times maybe, because 2.4.x is no different in this regard..

@ylavic
Copy link
Member Author

ylavic commented May 31, 2024

So I could not reproduce on my laptop even with things like:

diff --git a/test/modules/http2/test_106_shutdown.py b/test/modules/http2/test_106_shutdown.py
index 83e143cef5..7cc6605cbb 100644
--- a/test/modules/http2/test_106_shutdown.py
+++ b/test/modules/http2/test_106_shutdown.py
@@ -54,7 +54,7 @@ class TestShutdown:
         # Create a low limit and only 2 children, so we'll encounter this easily
         conf = H2Conf(env, extras={
             'base': [
-                "ServerLimit 2",
+                "ServerLimit 1",
                 "MaxRequestsPerChild 3"
             ]
         })
@@ -62,11 +62,11 @@ class TestShutdown:
         conf.install()
         assert env.apache_restart() == 0
         url = env.mkurl("https", "test1", "/index.html")
-        for i in range(7):
+        for i in range(100):
             r = env.curl_get(url, options=['-v'])
             # requests should succeed, but rarely connections get closed
             # before the response is received
-            if r.exit_code in [16, 55]:
+            if False and r.exit_code in [16, 55]:
                 # curl send error
                 assert r.response is None
             else:

which has more chances to find the single child leaving at a bad timing.

But I think I see how this can happen. In the test_h2_106_02 case the IDLE c1 connection is returned to the MPM for WRITE_COMPLETION, thus it transitions to KEEPALIVE after the write is complete (or if there is nothing to write) and there it can be killed by the MPM on graceful restart. If a new request arrives in between the listener checking for readability and the kill this is the typical keepalive race condition.
I don't think we should kill kept alive connections since they are not consuming much, instead we should set c->keepalive = AP_CONN_CLOSE before processing them the next time so that it's the last time, and let that go gracefully (KeepAliveTimeout is usually small like 5s max, so it shouldn't take to much to much to actually stop because of this).
This is what is done in event WIP #294, I could try to upstream that part too..

@ylavic
Copy link
Member Author

ylavic commented May 31, 2024

I don't think we should kill kept alive connections since they are not consuming much, instead we should set c->keepalive = AP_CONN_CLOSE before processing them the next time so that it's the last time,

Note that mod_h2 will have to do something to avoid the race condition still, like gracefully stopping the streams and everything. With HTTP/1 it's easier because the "last time" is a single transaction..

@ylavic
Copy link
Member Author

ylavic commented Jun 18, 2024

Not sure if we could/should change mod_status output in 2.4, the options are:

  1. change the output like currently in this PR
  2. mpm_event to count for CONN_STATE_PROCESSING connexions in ps->write_completion
  3. same implicitely by not backporting CONN_STATE_PROCESSING either, i.e. use the WRITE_COMPLETION+clogged workaround

Thoughts?

Also, trunk (hence this PR) do not yet preserve keepalive connections on load (per my previous comment), thoughts on this too?

@covener
Copy link
Member

covener commented Jun 18, 2024

  1. change the output like currently in this PR
    +1

-/-

    CONN_STATE_PROCESSING,          /* Processed by process_connection() hooks, returning
                                     * AGAIN to the MPM in this state will make it wait for
                                     * the connection to be readable or writable according to
                                     * CONN_SENSE_WANT_READ or CONN_SENSE_WANT_WRITE respectively,
                                     * where Timeout applies */

Minor not, it seems easy to misunderstand CONN_STATE_PROCESSING 180 degrees from name alone. Could we capture something more like the comment like CONN_STATE_WAITIO (don't love this either but hopefully it gets the point across)

@ylavic
Copy link
Member Author

ylavic commented Jun 18, 2024

Minor not, it seems easy to misunderstand CONN_STATE_PROCESSING 180 degrees from name alone. Could we capture something more like the comment like CONN_STATE_WAITIO (don't love this either but hopefully it gets the point across)

Yes good point, the issue I think is that CONN_STATE_PROCESSING is both for when the connection is processed by the modules (set by MPM event before calling ap_run_process_connection()) and for when modules want to wait for IOs in the MPM.
So maybe we want to keep CONN_STATE_PROCESSING for the former case only (it wouldn't be shown in mod_status like CONN_STATE_READ_REQUEST_LINE wasn't before) and have a new CONN_STATE_WAITIO or CONN_STATE_POLLED or CONN_STATE_ASYNC_WAITIO (_WAITIO does not look that bad to me btw) which mod_status accounts in its "asyncs"?

@icing
Copy link
Contributor

icing commented Jun 18, 2024

All are fine. I like CONN_STATE_ASYNC_WAITIO best.

@covener
Copy link
Member

covener commented Jun 18, 2024

That explains the naming! I think two states is best and the explicit ASYNC is makes sense too.

asfgit pushed a commit that referenced this pull request Jun 21, 2024
…NC_WAITIO.

Per discussion on PR #449, have a separate state for returning the connection
to the MPM to wait for an IO (namely CONN_STATE_ASYNC_WAITIO), rather than
(ab)using CONN_STATE_PROCESSING.

This removes the need for AGAIN added in r1918257 (for now), and AP_MPMQ_CAN_AGAIN
is renamed to AP_MPMQ_CAN_WAITIO.

This is also the state that mod_status accounts for, so rename ->processing
to ->wait_io in process_score (shows as "wait-io" in mod_status and mod_lua).



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918482 13f79535-47bb-0310-9956-ffa450edef68
ylavic added a commit to ylavic/httpd that referenced this pull request Jun 21, 2024
…NC_WAITIO.

Per discussion on PR apache#449, have a separate state for returning the connection
to the MPM to wait for an IO (namely CONN_STATE_ASYNC_WAITIO), rather than
(ab)using CONN_STATE_PROCESSING.

This removes the need for AGAIN added in r1918257 (for now), and AP_MPMQ_CAN_AGAIN
is renamed to AP_MPMQ_CAN_WAITIO.

This is also the state that mod_status accounts for, so rename ->processing
to ->wait_io in process_score (shows as "wait-io" in mod_status and mod_lua).

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918482 13f79535-47bb-0310-9956-ffa450edef68
@icing
Copy link
Contributor

icing commented Jun 21, 2024

Excellent work, @ylavic ! Looking forward to get that merged.🎉

@ylavic
Copy link
Member Author

ylavic commented Jun 21, 2024

Thanks! Something to look at maybe though.
Between d01f018 and aa64387 the connection was erroneously closed when returned to MPM, and that seemed to cause a hang in the h2 tests using CONN_STATE_ASYNC_WAITIO.
This is fixed by the last commit but I'm wondering what would happen if for instance the connection times out in the MPM (which would also cause an async close, though later).
I didn't look at what happened exactly so I can't tell if the hang was in mod_h2 or in the test client. It just surprised me so maybe there is something to look at and/or cleanup from the pre_close_connection hook still should an async close happen, this is actually the ultimate callback for mod_h2 to forget about the h1 and potentially all the related h2s if needed..

@ylavic
Copy link
Member Author

ylavic commented Jun 21, 2024

Wait, it seems the issue is in the lingering close itself..

@icing
Copy link
Contributor

icing commented Jun 21, 2024

Which test may hang/timeout in the mpm? Any way to reproduce?

@ylavic
Copy link
Member Author

ylavic commented Jun 21, 2024

All right, so it was just caused by d01f018 reaching ap_process_lingering_close() with an unexpected cs->pub.state == CONN_STATE_ASYNC_WAITIO, so the lingering close wasn't nonblocking anymore...
It should not happen now after aa64387 but I added the CONN_STATE_IS_LINGERING_CLOSE() macro in b40ccd9 to avoid any further mistake like this.
Should be good now ;)

ylavic added a commit to ylavic/httpd that referenced this pull request Jun 24, 2024
…NC_WAITIO.

Per discussion on PR apache#449, have a separate state for returning the connection
to the MPM to wait for an IO (namely CONN_STATE_ASYNC_WAITIO), rather than
(ab)using CONN_STATE_PROCESSING.

This removes the need for AGAIN added in r1918257 (for now), and AP_MPMQ_CAN_AGAIN
is renamed to AP_MPMQ_CAN_WAITIO.

This is also the state that mod_status accounts for, so rename ->processing
to ->wait_io in process_score (shows as "wait-io" in mod_status and mod_lua).

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918482 13f79535-47bb-0310-9956-ffa450edef68
icing and others added 11 commits July 11, 2024 16:03
    - on newer HTTPD versions, return connection monitoring
      to the event MPM when block on client updates.
      2.4.x versions still treat connections in the event
      MPM as KeepAlive and purge them on load in the middle
      of response processing.
    - spelling fixes
    - support for yield calls in c2 "network" filter

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918003 13f79535-47bb-0310-9956-ffa450edef68
…ate.

* include/httpd.h:
  Rename CONN_STATE_CHECK_REQUEST_LINE_READABLE to CONN_STATE_KEEPALIVE
  and CONN_STATE_READ_REQUEST_LINE to CONN_STATE_PROCESS, keeping the
  old enums as aliases. Rework comments about each state.

* server/mpm/event/event.c:
  Use the new states names.
  Let the process_connection hooks return CONN_STATE_PROCESS for mpm_event
  to POLLIN or POLLOUT depending on c->cs->sense being CONN_SENSE_WANT_READ
  or CONN_SENSE_WANT_WRITE respectively.
  Remove (ab)use of CONN_STATE_WRITE_COMPLETION with CONN_SENSE_WANT_READ to
  mean poll() for read (and the need for the obscure c->clogging_input_filters
  to make it work as expected). This is what CONN_STATE_PROCESS is for now.
  Update the comment about the states that can be returned by process_connection
  hooks (and their usage).
  Use the same queue (process_q renamed from write_completion_q) for polling
  connections in both CONN_STATE_PROCESS and CONN_STATE_WRITE_COMPLETION
  states since they both use the same (server_rec's) Timeout. This implies
  that both states are accounted as "write-completion" in mod_status for now.

* server/mpm/motorz/motorz.c, server/mpm/simple/simple_io.c, modules/http/http_core.c:
  Use the new states names (only).

* include/scoreboard.h:
  Change comment about process_score->write_completion to note that the
  counter refers to CONN_STATE_PROCESS connections returned to the MPM
  too.

* modules/http2/h2_c1.c:
  Return the c1 connection with the CONN_STATE_PROCESS state rather than
  CONN_STATE_WRITE_COMPLETION when waiting for a window update (i.e. ask
  the MPM to poll for read directly). This avoids the transition to
  CONN_STATE_KEEPALIVE which could kill the connection under high load.

Github: closes apache#448

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918022 13f79535-47bb-0310-9956-ffa450edef68
…Child"

When MaxConnectionsPerChild is reached there may be some connections to process
still and the listener should stop writing this at every loop. Logging once
is enough.

* server/mpm/event/event.c(check_infinite_requests): Raise conns_this_child
  unconditionally.
  


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918078 13f79535-47bb-0310-9956-ffa450edef68
As a follow up to r1918022 which handled the new CONN_STATE_PROCESS(ing) and
existing CONN_STATE_WRITE_COMPLETION in the same async queue, let's now have
two separates ones which allows more relevant async accounting in mod_status.

Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING as it's how it will be
called in mod_status.

* include/ap_mmn.h:
  MMN minor bump for process_score->processing counter.

* include/httpd.h:
  Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING.

* include/scoreboard.h:
  Add process_score->processing field.

* include/httpd.h, modules/http/http_core.c, modules/http2/h2_c1.c,
    server/mpm/event/event.c, server/mpm/motorz/motorz.c,
    server/mpm/simple/simple_io.c:
  Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING.

* server/mpm/event/event.c:
  Restore write_completion_q to handle connections in CONN_STATE_WRITE_COMPLETION.
  Use processing_q (renamed from process_q) solely for CONN_STATE_PROCESSING.
  Update process_score->processing according to the length of processing_q.

* modules/generators/mod_status.c:
  Show the value of process_score->processing in the stats.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918098 13f79535-47bb-0310-9956-ffa450edef68
Submitted by: Vladimír Chlup <vchlup redhat.com>
Github: closes apache#452


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918127 13f79535-47bb-0310-9956-ffa450edef68
Before r1918022, returning OK with CONN_STATE_PROCESSING to mpm_event was
handled like/by CONN_STATE_LINGER "to not break old or third-party modules
which might return OK w/o touching the state and expect lingering close,
like with worker or prefork MPMs".

So we need a new return code to be allowed to apply the new POLLIN/POLLOUT
behaviour for CONN_STATE_PROCESSING, thus revive AGAIN as introduced by
Graham some times ago for a nonblocking WIP (moved to a branch/PR since then).

MPM event will advertise its ability to handle CONN_STATE_PROCESSING + AGAIN
with AP_MPMQ_CAN_AGAIN, and mod_http2 can use that to know how to return to
the MPM as expected. When !AP_MPMQ_CAN_AGAIN modules/mod_http2 can still use
CONN_STATE_WRITE_COMPLETION + CONN_SENSE_WANT_READ + c->clogging_input_filters
which will work in mpm_even-2.4.x still.

* include/ap_mmn.h:
  Bump MMN minor for AP_MPMQ_CAN_AGAIN and AGAIN.

* include/ap_mpm.h:
  Define AP_MPMQ_CAN_AGAIN.

* include/httpd.h:
  Define AGAIN.

* modules/http2/h2.h:
  No need for H2_USE_STATE_PROCESSING anymore with AP_MPMQ_CAN_AGAIN.

* modules/http2/h2_c1.c:
  For !keepalive case return to the MPM using CONN_STATE_PROCESSING + AGAIN
  or CONN_STATE_WRITE_COMPLETION + c->clogging_input_filters depending on
  AP_MPMQ_CAN_AGAIN only.

* modules/http2/h2_session.c:
  Can return to the MPM for h2_send_flow_blocked() provided it's async only.

* server/mpm/event/event.c:
  Rework process_socket()'s CONN_STATE_PROCESSING to handle AGAIN and preserve
  compatibility. Have a lingering_close label to goto there faster when
  process_lingering_close() is to be called. Improve relevant comments.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918257 13f79535-47bb-0310-9956-ffa450edef68
…NC_WAITIO.

Per discussion on PR apache#449, have a separate state for returning the connection
to the MPM to wait for an IO (namely CONN_STATE_ASYNC_WAITIO), rather than
(ab)using CONN_STATE_PROCESSING.

This removes the need for AGAIN added in r1918257 (for now), and AP_MPMQ_CAN_AGAIN
is renamed to AP_MPMQ_CAN_WAITIO.

This is also the state that mod_status accounts for, so rename ->processing
to ->wait_io in process_score (shows as "wait-io" in mod_status and mod_lua).

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918482 13f79535-47bb-0310-9956-ffa450edef68
… anymore.

Since CONN_STATE_ASYNC_WAITIO, we cannot check for < or >= CONN_STATE_LINGER
anymore to determine if in an lingering close state, so let's add a new
CONN_STATE_IS_LINGERING_CLOSE() macro for this and use it in mpm_event.

The test for state == CONN_STATE_LINGER in process_lingering_close() is a
bit weak too in order to call ap_start_lingering_close() the first time only,
so have a conn_state->linger_started flag instead.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1918491 13f79535-47bb-0310-9956-ffa450edef68
asfgit pushed a commit that referenced this pull request Jul 27, 2024
…18257, r1918482, r1918483, r1918491, r1919141, r1919148 from trunk

 *) mod_http2: sync with module's github.
    - on newer HTTPD versions, return connection monitoring
      to the event MPM when block on client updates.
      2.4.x versions still treat connections in the event
      MPM as KeepAlive and purge them on load in the middle
      of response processing.
    - spelling fixes
    - support for yield calls in c2 "network" filter


 mpm_event,core: Handle async POLLIN/POLLOUT in CONN_STATE_PROCESS state.

* include/httpd.h:
  Rename CONN_STATE_CHECK_REQUEST_LINE_READABLE to CONN_STATE_KEEPALIVE
  and CONN_STATE_READ_REQUEST_LINE to CONN_STATE_PROCESS, keeping the
  old enums as aliases. Rework comments about each state.

* server/mpm/event/event.c:
  Use the new states names.
  Let the process_connection hooks return CONN_STATE_PROCESS for mpm_event
  to POLLIN or POLLOUT depending on c->cs->sense being CONN_SENSE_WANT_READ
  or CONN_SENSE_WANT_WRITE respectively.
  Remove (ab)use of CONN_STATE_WRITE_COMPLETION with CONN_SENSE_WANT_READ to
  mean poll() for read (and the need for the obscure c->clogging_input_filters
  to make it work as expected). This is what CONN_STATE_PROCESS is for now.
  Update the comment about the states that can be returned by process_connection
  hooks (and their usage).
  Use the same queue (process_q renamed from write_completion_q) for polling
  connections in both CONN_STATE_PROCESS and CONN_STATE_WRITE_COMPLETION
  states since they both use the same (server_rec's) Timeout. This implies
  that both states are accounted as "write-completion" in mod_status for now.

* server/mpm/motorz/motorz.c, server/mpm/simple/simple_io.c, modules/http/http_core.c:
  Use the new states names (only).

* include/scoreboard.h:
  Change comment about process_score->write_completion to note that the
  counter refers to CONN_STATE_PROCESS connections returned to the MPM
  too.

* modules/http2/h2_c1.c:
  Return the c1 connection with the CONN_STATE_PROCESS state rather than
  CONN_STATE_WRITE_COMPLETION when waiting for a window update (i.e. ask
  the MPM to poll for read directly). This avoids the transition to
  CONN_STATE_KEEPALIVE which could kill the connection under high load.


Github: closes #448


Follow up to r1918022: MMN minor bump and checks for the new conn_state_e aliases' usability.

mpm_event: Don't spam with "Stopping process due to MaxConnectionsPerChild"

When MaxConnectionsPerChild is reached there may be some connections to process
still and the listener should stop writing this at every loop. Logging once
is enough.

* server/mpm/event/event.c(check_infinite_requests): Raise conns_this_child
  unconditionally.
  

mpm_event, mod_status: Separate processing and write completion queues.

As a follow up to r1918022 which handled the new CONN_STATE_PROCESS(ing) and
existing CONN_STATE_WRITE_COMPLETION in the same async queue, let's now have
two separates ones which allows more relevant async accounting in mod_status.

Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING as it's how it will be
called in mod_status.

* include/ap_mmn.h:
  MMN minor bump for process_score->processing counter.

* include/httpd.h:
  Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING.

* include/scoreboard.h:
  Add process_score->processing field.

* include/httpd.h, modules/http/http_core.c, modules/http2/h2_c1.c,
    server/mpm/event/event.c, server/mpm/motorz/motorz.c,
    server/mpm/simple/simple_io.c:
  Rename CONN_STATE_PROCESS to CONN_STATE_PROCESSING.

* server/mpm/event/event.c:
  Restore write_completion_q to handle connections in CONN_STATE_WRITE_COMPLETION.
  Use processing_q (renamed from process_q) solely for CONN_STATE_PROCESSING.
  Update process_score->processing according to the length of processing_q.
  
* modules/generators/mod_status.c:
  Show the value of process_score->processing in the stats.


Follow up to r1918098 (and r1918022): Push missing changes.

mpm_event,mod_http2: Keep compatibility with CONN_STATE_PROCESSING + OK

Before r1918022, returning OK with CONN_STATE_PROCESSING to mpm_event was
handled like/by CONN_STATE_LINGER "to not break old or third-party modules
which might return OK w/o touching the state and expect lingering close,
like with worker or prefork MPMs".

So we need a new return code to be allowed to apply the new POLLIN/POLLOUT
behaviour for CONN_STATE_PROCESSING, thus revive AGAIN as introduced by
Graham some times ago for a nonblocking WIP (moved to a branch/PR since then).

MPM event will advertise its ability to handle CONN_STATE_PROCESSING + AGAIN
with AP_MPMQ_CAN_AGAIN, and mod_http2 can use that to know how to return to
the MPM as expected. When !AP_MPMQ_CAN_AGAIN modules/mod_http2 can still use
CONN_STATE_WRITE_COMPLETION + CONN_SENSE_WANT_READ + c->clogging_input_filters
which will work in mpm_even-2.4.x still.

* include/ap_mmn.h:
  Bump MMN minor for AP_MPMQ_CAN_AGAIN and AGAIN.

* include/ap_mpm.h:
  Define AP_MPMQ_CAN_AGAIN.

* include/httpd.h:
  Define AGAIN.

* modules/http2/h2.h:
  No need for H2_USE_STATE_PROCESSING anymore with AP_MPMQ_CAN_AGAIN.

* modules/http2/h2_c1.c:
  For !keepalive case return to the MPM using CONN_STATE_PROCESSING + AGAIN
  or CONN_STATE_WRITE_COMPLETION + c->clogging_input_filters depending on
  AP_MPMQ_CAN_AGAIN only.

* modules/http2/h2_session.c:
  Can return to the MPM for h2_send_flow_blocked() provided it's async only.

* server/mpm/event/event.c:
  Rework process_socket()'s CONN_STATE_PROCESSING to handle AGAIN and preserve
  compatibility. Have a lingering_close label to goto there faster when
  process_lingering_close() is to be called. Improve relevant comments.


mpm_event,mod_http2,mod_status: Follow up to r1918257: CONN_STATE_ASYNC_WAITIO.

Per discussion on PR #449, have a separate state for returning the connection
to the MPM to wait for an IO (namely CONN_STATE_ASYNC_WAITIO), rather than
(ab)using CONN_STATE_PROCESSING.

This removes the need for AGAIN added in r1918257 (for now), and AP_MPMQ_CAN_AGAIN
is renamed to AP_MPMQ_CAN_WAITIO.

This is also the state that mod_status accounts for, so rename ->processing
to ->wait_io in process_score (shows as "wait-io" in mod_status and mod_lua).


mpm_event: Follow up to r1918482: CONN_STATE_ASYNC_WAITIO > CONN_STATE_LINGER.


mpm_event: Follow up to r1918482: CONN_STATE_LINGER* are not the last anymore.

Since CONN_STATE_ASYNC_WAITIO, we cannot check for < or >= CONN_STATE_LINGER
anymore to determine if in an lingering close state, so let's add a new
CONN_STATE_IS_LINGERING_CLOSE() macro for this and use it in mpm_event.

The test for state == CONN_STATE_LINGER in process_lingering_close() is a
bit weak too in order to call ap_start_lingering_close() the first time only,
so have a conn_state->linger_started flag instead.


mod_status: Follow up to r1918482: Bump colspan for the new wait-io colomn


mod_status: "Threads" span three colomns (busy, graceful, idle), not two.


Submitted by: icing, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic, ylavic
Reviewed by: ylavic, icing, gbechis
Github: closes #449


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1919548 13f79535-47bb-0310-9956-ffa450edef68
@ylavic
Copy link
Member Author

ylavic commented Jul 27, 2024

Backported in r1919548

@ylavic ylavic closed this Jul 27, 2024
@ylavic ylavic deleted the h2_c1_return_to_mpm branch July 27, 2024 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants