Skip to content

Commit ecdbbde

Browse files
authored
Chore: Address more integration test flakiness (#5258)
1 parent b581636 commit ecdbbde

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

.circleci/manage-test-db.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ redshift_down() {
8080
EXIT_CODE=1
8181
ATTEMPTS=0
8282
while [ $EXIT_CODE -ne 0 ] && [ $ATTEMPTS -lt 5 ]; do
83-
redshift_exec "select pg_terminate_backend(procpid) from pg_stat_activity where datname = '$1'"
83+
# note: sometimes this pg_terminate_backend() call can randomly fail with: ERROR: Insufficient privileges
84+
# if it does, let's proceed with the drop anyway rather than aborting and never attempting the drop
85+
redshift_exec "select pg_terminate_backend(procpid) from pg_stat_activity where datname = '$1'" || true
86+
87+
# perform drop
8488
redshift_exec "drop database $1;" && EXIT_CODE=$? || EXIT_CODE=$?
8589
if [ $EXIT_CODE -ne 0 ]; then
8690
echo "Unable to drop database; retrying..."

sqlmesh/core/engine_adapter/fabric.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ def set_current_catalog(self, catalog_name: str) -> None:
153153

154154
logger.info(f"Switching from catalog '{current_catalog}' to '{catalog_name}'")
155155

156+
# commit the transaction before closing the connection to help prevent errors like:
157+
# > Snapshot isolation transaction failed in database because the object accessed by the statement has been modified by a
158+
# > DDL statement in another concurrent transaction since the start of this transaction
159+
# on subsequent queries in the new connection
160+
self._connection_pool.commit()
161+
156162
# note: we call close() on the connection pool instead of self.close() because self.close() calls close_all()
157163
# on the connection pool but we just want to close the connection for this thread
158164
self._connection_pool.close()

tests/conftest.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,51 @@ def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo):
245245
)
246246

247247

248+
def pytest_configure(config: pytest.Config):
249+
# we need to adjust the hook order if pytest-retry is present because it:
250+
# - also declares a `pytest_runtest_makereport` with `hookwrapper=True, tryfirst=True`
251+
# - this supersedes our one because pytest always loads plugins first and they take precedence over user code
252+
#
253+
# but, we need our one to run first because it's capturing and ignoring certain errors that cause pytest-retry to fail
254+
# and not retry. so we need to adjust the order the hooks are called which pytest does NOT make easy.
255+
#
256+
# we can't just unload the pytest-retry plugin, load our hook and reload the pytest-retry plugin either.
257+
# this causes an error:
258+
# > Hook 'pytest_set_excluded_exceptions' is already registered within namespace
259+
# because unregister() apparently doesnt unregister plugins cleanly in such a way they can be re-registered
260+
#
261+
# so what we end up doing below is a small monkey-patch to adjust the call order of the hooks
262+
pm = config.pluginmanager
263+
264+
from pluggy._hooks import HookCaller
265+
266+
hook_caller: HookCaller = pm.hook.pytest_runtest_makereport
267+
hook_impls = hook_caller.get_hookimpls()
268+
269+
# find the index of our one
270+
our_makereport_idx = next(
271+
(i for i, v in enumerate(hook_impls) if v.plugin_name.endswith("tests/conftest.py")), None
272+
)
273+
274+
# find the index of the pytest-retry one
275+
pytest_retry_makereport_idx = next(
276+
(i for i, v in enumerate(hook_impls) if v.plugin_name == "pytest-retry"), None
277+
)
278+
279+
if (
280+
pytest_retry_makereport_idx is not None
281+
and our_makereport_idx is not None
282+
and our_makereport_idx > pytest_retry_makereport_idx
283+
):
284+
our_makereport_hook = hook_impls.pop(our_makereport_idx)
285+
286+
# inject our one to run before the pytest-retry one
287+
hook_impls.insert(pytest_retry_makereport_idx, our_makereport_hook)
288+
289+
# HookCaller doesnt have a setter method for this.
290+
hook_caller._hookimpls = hook_impls # type: ignore
291+
292+
248293
# Ignore all local config files
249294
@pytest.fixture(scope="session", autouse=True)
250295
def ignore_local_config_files():

0 commit comments

Comments
 (0)