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

ADAP-761: [backport] Add retry logic to flaky MV tests to avoid "cannot open relation with OID" error #574

Merged
merged 1 commit into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/unreleased/Under the Hood-20230808-141645.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Under the Hood
body: Update flaky MV tests to use retry logic to avoid "cannot open relation with
OID" error
time: 2023-08-08T14:16:45.227308-04:00
custom:
Author: mikealfare
Issue: "569"
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
MaterializedViewChangesFailMixin,
)
from dbt.tests.adapter.materialized_view.files import MY_TABLE, MY_VIEW
from dbt.tests.util import get_model_file, set_model_file
from dbt.tests.util import assert_message_in_logs, get_model_file, set_model_file

from tests.functional.adapter.materialized_view_tests.utils import (
query_autorefresh,
query_dist,
query_relation_type,
query_sort,
run_dbt_and_capture_with_retries_redshift_mv,
)


Expand Down Expand Up @@ -61,6 +62,14 @@ def query_row_count(project, relation: BaseRelation) -> int:
def query_relation_type(project, relation: BaseRelation) -> Optional[str]:
return query_relation_type(project, relation)

def test_materialized_view_create_idempotent(self, project, my_materialized_view):
# setup creates it once; verify it's there and run once
assert self.query_relation_type(project, my_materialized_view) == "materialized_view"
run_dbt_and_capture_with_retries_redshift_mv(
["run", "--models", my_materialized_view.identifier]
)
assert self.query_relation_type(project, my_materialized_view) == "materialized_view"

@pytest.mark.skip(
"The current implementation does not support overwriting materialized views with tables."
)
Expand Down Expand Up @@ -120,16 +129,77 @@ def check_state_replace_change_is_applied(project, materialized_view):
class TestRedshiftMaterializedViewChangesApply(
RedshiftMaterializedViewChanges, MaterializedViewChangesApplyMixin
):
pass
def test_change_is_applied_via_alter(self, project, my_materialized_view):
self.check_start_state(project, my_materialized_view)

self.change_config_via_alter(project, my_materialized_view)
_, logs = run_dbt_and_capture_with_retries_redshift_mv(
["--debug", "run", "--models", my_materialized_view.name]
)

self.check_state_alter_change_is_applied(project, my_materialized_view)

assert_message_in_logs(f"Applying ALTER to: {my_materialized_view}", logs)
assert_message_in_logs(f"Applying REPLACE to: {my_materialized_view}", logs, False)

def test_change_is_applied_via_replace(self, project, my_materialized_view):
self.check_start_state(project, my_materialized_view)

self.change_config_via_alter(project, my_materialized_view)
self.change_config_via_replace(project, my_materialized_view)
_, logs = run_dbt_and_capture_with_retries_redshift_mv(
["--debug", "run", "--models", my_materialized_view.name]
)

self.check_state_alter_change_is_applied(project, my_materialized_view)
self.check_state_replace_change_is_applied(project, my_materialized_view)

assert_message_in_logs(f"Applying REPLACE to: {my_materialized_view}", logs)


class TestRedshiftMaterializedViewChangesContinue(
RedshiftMaterializedViewChanges, MaterializedViewChangesContinueMixin
):
pass
def test_change_is_not_applied_via_alter(self, project, my_materialized_view):
self.check_start_state(project, my_materialized_view)

self.change_config_via_alter(project, my_materialized_view)
_, logs = run_dbt_and_capture_with_retries_redshift_mv(
["--debug", "run", "--models", my_materialized_view.name]
)

self.check_start_state(project, my_materialized_view)

assert_message_in_logs(
f"Configuration changes were identified and `on_configuration_change` was set"
f" to `continue` for `{my_materialized_view}`",
logs,
)
assert_message_in_logs(f"Applying ALTER to: {my_materialized_view}", logs, False)
assert_message_in_logs(f"Applying REPLACE to: {my_materialized_view}", logs, False)

def test_change_is_not_applied_via_replace(self, project, my_materialized_view):
self.check_start_state(project, my_materialized_view)

self.change_config_via_alter(project, my_materialized_view)
self.change_config_via_replace(project, my_materialized_view)
_, logs = run_dbt_and_capture_with_retries_redshift_mv(
["--debug", "run", "--models", my_materialized_view.name]
)

self.check_start_state(project, my_materialized_view)

assert_message_in_logs(
f"Configuration changes were identified and `on_configuration_change` was set"
f" to `continue` for `{my_materialized_view}`",
logs,
)
assert_message_in_logs(f"Applying ALTER to: {my_materialized_view}", logs, False)
assert_message_in_logs(f"Applying REPLACE to: {my_materialized_view}", logs, False)


class TestRedshiftMaterializedViewChangesFail(
RedshiftMaterializedViewChanges, MaterializedViewChangesFailMixin
):
# Note: using retries doesn't work when we expect `dbt_run` to fail
pass
20 changes: 19 additions & 1 deletion tests/functional/adapter/materialized_view_tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Optional
from typing import List, Optional

from dbt.adapters.base.relation import BaseRelation
from dbt.tests.util import run_dbt_and_capture

from dbt.adapters.redshift.relation import RedshiftRelation

Expand Down Expand Up @@ -67,3 +68,20 @@ def query_autorefresh(project, relation: RedshiftRelation) -> bool:
and trim(mv.db_name) ilike '{ relation.database }'
"""
return project.run_sql(sql, fetch="one")[0]


def run_dbt_and_capture_with_retries_redshift_mv(args: List[str], max_retries: int = 10):
"""
We need to retry `run_dbt` calls on Redshift because we get sporadic failures of the form:

Database Error in model my_materialized_view (models/my_materialized_view.sql)
could not open relation with OID 14957412
"""
retries = 0
while retries < max_retries:
try:
# there's no point to using this with expect_pass=False
return run_dbt_and_capture(args, expect_pass=True)
except AssertionError:
retries += 1
return None