Skip to content

Commit d5a6f71

Browse files
committed
fix: Flush pool as late as possible during schema cache reloading
retryingSchemaCacheLoad flushes the pool upon every retry before it starts reloading the schema. This is too early as schema reloading might take some time during which new connections might be acquired. The consequence is that: * upon successful schema cache reload we might have some connections created with the old schema cache * we close connections upon each retry and under load we will keep closing and re-opening connections until schema cache load succeeds This change is to make sure we flush the pool only after successful schema cache querying but before loading (so that connections acquired during loading wait for it and do not interfere with timing the loading process).
1 parent 99984d3 commit d5a6f71

2 files changed

Lines changed: 51 additions & 46 deletions

File tree

src/PostgREST/AppState.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,6 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
366366
observer $ ConnectionRetryObs delay
367367
putNextListenerDelay appState delay
368368

369-
flushPool appState
370-
371369
(,) <$> qPgVersion <*> (qInDbConfig *> qSchemaCache)
372370
)
373371
where
@@ -415,8 +413,12 @@ retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThrea
415413
-- IMPORTANT: While the pending schema cache state starts from running the above querySchemaCache, only at this stage we block API requests due to the usage of an
416414
-- IORef on putSchemaCache. This is why SCacheStatus is put at SCPending here to signal the Admin server (using isPending) that we're on a recovery state.
417415
putSCacheStatus appState SCPending
418-
putSchemaCache appState $ Just sCache
419416
observer $ SchemaCacheQueriedObs resultTime
417+
putSchemaCache appState $ Just sCache
418+
-- Flush the pool after loading the schema cache to reset any stale session cache entries
419+
-- We do it after successfully querying the schema cache
420+
-- and after marking sCacheStatus as pending,
421+
flushPool appState
420422
(t, _) <- timeItT $ observer $ SchemaCacheSummaryObs $ showSummary sCache
421423
observer $ SchemaCacheLoadedObs t
422424
putSCacheStatus appState SCLoaded

test/io/test_io.py

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -661,55 +661,58 @@ def test_log_level(level, defaultenv):
661661
response = postgrest.session.get("/")
662662
assert response.status_code == 200
663663

664-
output = sorted(postgrest.read_stdout(nlines=7))
664+
raw_ouptput = postgrest.read_stdout(nlines=9)
665+
output = sorted(raw_ouptput)
666+
667+
def match_log(matchers, output):
668+
ito = iter(output)
669+
itm = iter(matchers)
670+
nextMatcher = next(itm, None)
671+
while nextMatcher is not None and (line := next(ito, None)) is not None:
672+
if re.match(nextMatcher, line) is not None:
673+
nextMatcher = next(itm, None)
674+
if nextMatcher is not None:
675+
raise AssertionError(
676+
f"Expected log line matching {nextMatcher} not found in output"
677+
)
665678

666679
if level == "crit":
667680
assert len(output) == 0
668681
elif level == "error":
669-
assert re.match(
670-
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
671-
output[0],
682+
match_log(
683+
[r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"'],
684+
raw_ouptput,
672685
)
673686
assert len(output) == 1
674687
elif level == "warn":
675-
assert re.match(
676-
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
677-
output[0],
678-
)
679-
assert re.match(
680-
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
681-
output[1],
688+
match_log(
689+
[
690+
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
691+
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
692+
],
693+
raw_ouptput,
682694
)
683695
assert len(output) == 2
684696
elif level == "info":
685-
assert re.match(
686-
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
687-
output[0],
688-
)
689-
assert re.match(
690-
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
691-
output[1],
692-
)
693-
assert re.match(
694-
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
695-
output[2],
697+
match_log(
698+
[
699+
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
700+
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
701+
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
702+
],
703+
raw_ouptput,
696704
)
697705
assert len(output) == 3
698706
elif level == "debug":
699-
assert re.match(
700-
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
701-
output[0],
702-
)
703-
assert re.match(
704-
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
705-
output[1],
706-
)
707-
assert re.match(
708-
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
709-
output[2],
707+
match_log(
708+
[
709+
r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"',
710+
r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"',
711+
r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"',
712+
],
713+
raw_ouptput,
710714
)
711-
712-
assert len(output) == 7
715+
assert len(output) == 9
713716
assert any("Connection" and "is available" in line for line in output)
714717
assert any("Connection" and "is used" in line for line in output)
715718

@@ -1346,16 +1349,16 @@ def test_db_error_logging_to_stderr(level, defaultenv, metapostgrest):
13461349
assert response.status_code == 500
13471350

13481351
# ensure the message appears on the logs
1349-
output = sorted(postgrest.read_stdout(nlines=6))
1352+
output = postgrest.read_stdout(nlines=8)
13501353

13511354
if level == "crit":
13521355
assert len(output) == 0
13531356
elif level == "debug":
1354-
assert " 500 " in output[0]
1355-
assert "canceling statement due to statement timeout" in output[5]
1357+
assert " 500 " in output[7]
1358+
assert "canceling statement due to statement timeout" in output[6]
13561359
else:
1357-
assert " 500 " in output[0]
1358-
assert "canceling statement due to statement timeout" in output[1]
1360+
assert " 500 " in output[1]
1361+
assert "canceling statement due to statement timeout" in output[0]
13591362

13601363
reset_statement_timeout(metapostgrest, role)
13611364

@@ -1557,10 +1560,10 @@ def test_log_pool_req_observation(level, defaultenv):
15571560
postgrest.session.get("/authors_only", headers=headers)
15581561

15591562
if level == "debug":
1560-
output = postgrest.read_stdout(nlines=5)
1563+
output = postgrest.read_stdout(nlines=7)
1564+
assert len(output) == 7
15611565
assert pool_req in output[1]
1562-
assert pool_req_fullfill in output[4]
1563-
assert len(output) == 5
1566+
assert pool_req_fullfill in output[6]
15641567
elif level == "info":
15651568
output = postgrest.read_stdout(nlines=4)
15661569
assert len(output) == 1

0 commit comments

Comments
 (0)