Skip to content

Commit

Permalink
fix: reset does not work in multi-process mode
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Jun 7, 2024
1 parent 680d70a commit 9f4e830
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 35 deletions.
6 changes: 3 additions & 3 deletions ckanext/editable_config/logic/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def editable_config_update(
)

if data_dict["apply"]:
shared.apply_config_overrides(removed_keys=result["reset"])
shared.apply_config_overrides()

return result

Expand Down Expand Up @@ -225,7 +225,7 @@ def editable_config_reset(
sess.commit()

if data_dict["apply"]:
shared.apply_config_overrides(removed_keys=result)
shared.apply_config_overrides()

return result

Expand All @@ -236,6 +236,6 @@ def editable_config_apply(
data_dict: dict[str, Any],
) -> dict[str, Any]:
tk.check_access("editable_config_apply", context, data_dict)
count = shared.apply_config_overrides(removed_keys=data_dict["removed_keys"])
count = shared.apply_config_overrides()

return {"count": count}
37 changes: 24 additions & 13 deletions ckanext/editable_config/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import datetime
import logging
from typing import Any, Collection, Iterable
from typing import Any, Iterable
import sqlalchemy as sa
from typing_extensions import TypedDict

Expand Down Expand Up @@ -137,24 +137,33 @@ class _Updater:
# TODO: write bencharks for mutex vs. last updated
# TODO: prove that race-condition is safe here
_last_check: datetime.datetime | None
_active_overrides: set[str]

@property
def last_check(self):
return self._last_check

def __init__(self):
self._last_check = None
self._active_overrides = set()

def __call__(self, removed_keys: Collection[str] | None = None) -> int:
def __call__(self) -> int:
"""Override changed config options and remove options that do not
require customization.
Reckon total number of modifications and reload plugins if any change
detected.
"""
now = datetime.datetime.utcnow()

charge_timeout = datetime.timedelta(seconds=config.charge_timeout())
if self._last_check and now - self._last_check < charge_timeout:
return 0

count = self._apply_changes()
count += self._remove_keys(removed_keys)
count += self._remove_keys()

self._last_check = now
if count:
plugins_update()

Expand All @@ -164,13 +173,8 @@ def _apply_changes(self) -> int:
"""Override config options that were updated since last check."""
from ckanext.editable_config.model import Option

now = datetime.datetime.utcnow()
count = 0

charge_timeout = datetime.timedelta(seconds=config.charge_timeout())
if self._last_check and now - self._last_check < charge_timeout:
return count

if Option.is_updated_since(self._last_check):
for option in Option.updated_since(self._last_check):
if not is_editable(option.key):
Expand All @@ -189,17 +193,23 @@ def _apply_changes(self) -> int:
tk.config[option.key] = option.value
count += 1

self._last_check = now
return count

def _remove_keys(self, keys: Collection[str] | None) -> int:
def _remove_keys(self) -> int:
"""Restore original value(using config file) for specified options."""
count = 0
if not keys:
return count

src_conf = CKANConfigLoader(tk.config["__file__"]).get_config()
for key in keys:

try:
editable = tk.get_action("editable_config_list")({"ignore_auth": True}, {})
except KeyError:
log.debug("Do not check removed overrides because plugin is not loaded yet")
return 0

current_overrides = {k for k, v in editable.items() if v["option"]}

for key in self._active_overrides - current_overrides:
if key in src_conf:
# switch to the literal value from the config file.
log.debug(
Expand All @@ -225,6 +235,7 @@ def _remove_keys(self, keys: Collection[str] | None) -> int:

count += 1

self._active_overrides = current_overrides
return count


Expand Down
2 changes: 1 addition & 1 deletion ckanext/editable_config/tests/logic/test_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_one_per_group(self, faker, option_factory, ckan_config):
apply=False,
)

assert shared.apply_config_overrides(removed_keys=result["reset"]) == 3
assert shared.apply_config_overrides() == 3

assert (
ckan_config["ckan.site_title"]
Expand Down
32 changes: 14 additions & 18 deletions ckanext/editable_config/tests/test_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,23 @@ def test_apply_old_updates(self, faker, ckan_config, freezer, autoclean_option):
assert shared.apply_config_overrides() == 0
assert ckan_config[key] != value

def test_reset_configured_option(self, ckan_config, autoclean_option):
@pytest.mark.usefixtures("with_autoclean")
def test_reset_configured_option(self, ckan_config, option_factory, faker):
"""Config overrides for option in config file can are removed."""
key = autoclean_option["key"]
assert shared.apply_config_overrides(removed_keys=[key]) == 1
assert ckan_config[key] != autoclean_option["value"]
key = "ckan.site_title"
option = option_factory(key=key, value=faker.sentence())
assert shared.apply_config_overrides() == 0

call_action("editable_config_reset", keys=[key], apply=False)
assert shared.apply_config_overrides() == 1
assert ckan_config[key] != option["value"]

def test_reset_added_option(self, faker, ckan_config, option_factory):
"""Config overrides for option not available in the config file are removed."""
key = "ckan.site_intro_text"
option = option_factory(key=key, value=faker.sentence())
assert shared.apply_config_overrides() == 0

with option_factory.autoclean(key=key, value=faker.sentence()) as option:
assert shared.apply_config_overrides(removed_keys=[key]) == 1
assert ckan_config[key] != option["value"]

@pytest.mark.ckan_config("ckan.site_title", "hello world")
def test_reset_original_option(self, faker, ckan_config):
"""Config options can be restored to the state of config file even when
changed elsewhere.
"""
key = "ckan.site_title"
assert ckan_config[key] == "hello world"
assert shared.apply_config_overrides(removed_keys=[key]) == 1
assert ckan_config[key] != "hello world"
call_action("editable_config_reset", keys=[key], apply=False)
assert shared.apply_config_overrides() == 1
assert ckan_config[key] != option["value"]

0 comments on commit 9f4e830

Please sign in to comment.