Skip to content

Commit

Permalink
Time based cache refresh
Browse files Browse the repository at this point in the history
Previously to update the cache you had to clear the cache and fetch.
This leaves a window where a build may use uncached value and fail.  A
better way is to refresh the cache periodically *without* clearing the
cache, ensuring that builds will always use cached values.

To support time based refresh, we fetch data only if the cached item it
too old or missing. Upon successful fetch, the item age is set to zero,
so the next access to the cache will skip the fetch.

We use 2 threshold for refreshing the cache:

- REFRESH_THRSHOLD: 12 hours
- FETCH_THRESHOLD: 48 hours

This allows refreshing the cache once per day (e.g. from a cron job). On
errors, we can rerun the refresh until the refresh succeeds. Fresh items
are skipped during the refresh.

If refreshing the cache failed, jobs will trigger a refresh after 48
hours. The longer threshold ensures that this will never happen unless
the refresh job is broken.

The `fetch` command was renamed to `cache`. This describe better the
purpose of the command. The hook was update as well to match the command
name.

The `drenv.cache` module provides now 2 operations:

- cache.refresh() - rebuild the cache if older than
  cache.REFRESH_THRESHOLD (12 hours). Will is used in the `cache` hook.

- cache.get() - rebuild the cache if older than cache.FETCH_THRESHOLD
  (48 hours) and return the path to the cached value. Will is used in
  `start` hooks to get a cached value.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
  • Loading branch information
nirs committed May 15, 2024
1 parent 4a585f8 commit 977c65c
Show file tree
Hide file tree
Showing 23 changed files with 82 additions and 61 deletions.
4 changes: 2 additions & 2 deletions test/addons/cdi/fetch → test/addons/cdi/cache
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch("operator", "addons/cdi-operator.yaml")
cache.fetch("cr", "addons/cdi-cr.yaml")
cache.refresh("operator", "addons/cdi-operator.yaml")
cache.refresh("cr", "addons/cdi-cr.yaml")
4 changes: 2 additions & 2 deletions test/addons/cdi/start
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ NAMESPACE = "cdi"

def deploy(cluster):
print("Deploying cdi operator")
path = cache.fetch("operator", "addons/cdi-operator.yaml")
path = cache.get("operator", "addons/cdi-operator.yaml")
kubectl.apply("--filename", path, context=cluster)

print("Waiting until cdi-operator is rolled out")
Expand All @@ -27,7 +27,7 @@ def deploy(cluster):
)

print("Deploying cdi cr")
path = cache.fetch("cr", "addons/cdi-cr.yaml")
path = cache.get("cr", "addons/cdi-cr.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch(".", "addons/rook-cephfs.yaml")
cache.refresh(".", "addons/csi-addons.yaml")
2 changes: 1 addition & 1 deletion test/addons/csi-addons/start
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from drenv import cache

def deploy(cluster):
print("Deploying csi addon for volume replication")
path = cache.fetch(".", "addons/csi-addons.yaml")
path = cache.get(".", "addons/csi-addons.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
4 changes: 2 additions & 2 deletions test/addons/kubevirt/fetch → test/addons/kubevirt/cache
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch("operator", "addons/kubevirt-operator.yaml")
cache.fetch("cr", "addons/kubevirt-cr.yaml")
cache.refresh("operator", "addons/kubevirt-operator.yaml")
cache.refresh("cr", "addons/kubevirt-cr.yaml")
4 changes: 2 additions & 2 deletions test/addons/kubevirt/start
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ NAMESPACE = "kubevirt"

def deploy(cluster):
print("Deploying kubevirt operator")
path = cache.fetch("operator", "addons/kubevirt-operator.yaml")
path = cache.get("operator", "addons/kubevirt-operator.yaml")
kubectl.apply("--filename", path, context=cluster)

print("Waiting until virt-operator is rolled out")
Expand All @@ -27,7 +27,7 @@ def deploy(cluster):
)

print("Deploying kubevirt cr")
path = cache.fetch("cr", "addons/kubevirt-cr.yaml")
path = cache.get("cr", "addons/kubevirt-cr.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
10 changes: 10 additions & 0 deletions test/addons/ocm-controller/cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3

# SPDX-FileCopyrightText: The RamenDR authors
# SPDX-License-Identifier: Apache-2.0

import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.refresh(".", "addons/ocm-controller.yaml")
2 changes: 1 addition & 1 deletion test/addons/ocm-controller/start
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from drenv import cache

def deploy(cluster):
print("Deploying ocm controller")
path = cache.fetch(".", "addons/ocm-controller.yaml")
path = cache.get(".", "addons/ocm-controller.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
2 changes: 1 addition & 1 deletion test/addons/recipe/fetch → test/addons/recipe/cache
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch(".", "addons/recipe.yaml")
cache.refresh(".", "addons/recipe.yaml")
2 changes: 1 addition & 1 deletion test/addons/recipe/start
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ os.chdir(os.path.dirname(__file__))
cluster = sys.argv[1]

print("Deploying recipe crd")
path = cache.fetch(".", "addons/recipe.yaml")
path = cache.get(".", "addons/recipe.yaml")
kubectl.apply("--filename", path, context=cluster)
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch(".", "addons/csi-addons.yaml")
cache.refresh(".", "addons/rook-cephfs.yaml")
2 changes: 1 addition & 1 deletion test/addons/rook-cephfs/start
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ from drenv import kubectl

def deploy(cluster):
print("Creating CephFS instance and storage/snapshot classes")
path = cache.fetch(".", "addons/rook-cephfs.yaml")
path = cache.get(".", "addons/rook-cephfs.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.fetch(".", "addons/ocm-controller.yaml")
cache.refresh(".", "addons/rook-cluster.yaml")
10 changes: 0 additions & 10 deletions test/addons/rook-cluster/fetch

This file was deleted.

2 changes: 1 addition & 1 deletion test/addons/rook-cluster/start
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ TIMEOUT = 600

def deploy(cluster):
print("Deploying rook ceph cluster")
path = cache.fetch(".", "addons/rook-cluster.yaml")
path = cache.get(".", "addons/rook-cluster.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
10 changes: 10 additions & 0 deletions test/addons/rook-operator/cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3

# SPDX-FileCopyrightText: The RamenDR authors
# SPDX-License-Identifier: Apache-2.0

import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.refresh(".", "addons/rook-operator.yaml")
10 changes: 0 additions & 10 deletions test/addons/rook-operator/fetch

This file was deleted.

2 changes: 1 addition & 1 deletion test/addons/rook-operator/start
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from drenv import cache

def deploy(cluster):
print("Deploying rook ceph operator")
path = cache.fetch(".", "addons/rook-operator.yaml")
path = cache.get(".", "addons/rook-operator.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
10 changes: 10 additions & 0 deletions test/addons/rook-toolbox/cache
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3

# SPDX-FileCopyrightText: The RamenDR authors
# SPDX-License-Identifier: Apache-2.0

import os
from drenv import cache

os.chdir(os.path.dirname(__file__))
cache.refresh(".", "addons/rook-toolbox.yaml")
10 changes: 0 additions & 10 deletions test/addons/rook-toolbox/fetch

This file was deleted.

2 changes: 1 addition & 1 deletion test/addons/rook-toolbox/start
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ from drenv import cache

def deploy(cluster):
print("Deploying rook ceph toolbox")
path = cache.fetch(".", "addons/rook-toolbox.yaml")
path = cache.get(".", "addons/rook-toolbox.yaml")
kubectl.apply("--filename", path, context=cluster)


Expand Down
16 changes: 8 additions & 8 deletions test/drenv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def parse_args():
add_command(sp, "suspend", do_suspend, help="suspend virtual machines")
add_command(sp, "resume", do_resume, help="resume virtual machines")
add_command(sp, "dump", do_dump, help="dump an environment yaml")
add_command(sp, "fetch", do_fetch, help="cache environment resources")
add_command(sp, "cache", do_cache, help="cache environment resources")

add_command(sp, "clear", do_clear, help="cleared cached resources", envfile=False)
add_command(sp, "setup", do_setup, help="setup minikube for drenv", envfile=False)
Expand Down Expand Up @@ -177,20 +177,20 @@ def do_clear(args):
cache.clear()


def do_fetch(args):
def do_cache(args):
env = load_env(args)
start = time.monotonic()
logging.info("[%s] Fetching", env["name"])
logging.info("[%s] Refreshing cached addons", env["name"])
addons = collect_addons(env)
execute(
fetch_addon,
cache_addon,
addons,
"fetch",
"cache",
max_workers=args.max_workers,
ctx=env["name"],
)
logging.info(
"[%s] Fetching finishied in %.2f seconds",
"[%s] Cached addons refreshed in %.2f seconds",
env["name"],
time.monotonic() - start,
)
Expand Down Expand Up @@ -491,13 +491,13 @@ def run_worker(worker, hooks=(), reverse=False, allow_failure=False):
run_addon(addon, worker["name"], hooks=hooks, allow_failure=allow_failure)


def fetch_addon(addon, ctx="global"):
def cache_addon(addon, ctx="global"):
addon_dir = os.path.join(ADDONS_DIR, addon["name"])
if not os.path.isdir(addon_dir):
skip_addon(addon, ctx)
return

hook = os.path.join(addon_dir, "fetch")
hook = os.path.join(addon_dir, "cache")
if os.path.isfile(hook):
run_hook(hook, (), ctx)

Expand Down
29 changes: 25 additions & 4 deletions test/drenv/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import os
import shutil
import subprocess
import time

from . import commands

REFRESH_THRESHOLD = 12 * 3600
FETCH_THRESHOLD = 48 * 3600


def clear(key=""):
"""
Expand All @@ -19,17 +23,34 @@ def clear(key=""):
pass


def fetch(kustomization_dir, key, log=print):
def refresh(kustomization_dir, key, log=print):
"""
Build kustomization and cache the output yaml. Retrun the path to the
cached yaml.
Rebuild the cache if cached item age is bigger than REFRESH_THRESHOLD.
"""
dest = _path(key)
if not os.path.exists(dest):
if _age(dest) > REFRESH_THRESHOLD:
_fetch(kustomization_dir, dest, log=log)


def get(kustomization_dir, key, log=print):
"""
Rebuild the cache if cached item age is bigger than FETCH_THRESHOLD.
Return the path to cached kustomization yaml.
"""
dest = _path(key)
if _age(dest) > FETCH_THRESHOLD:
_fetch(kustomization_dir, dest, log=log)
return dest


def _age(path):
try:
mtime = os.path.getmtime(path)
except FileNotFoundError:
mtime = 0
return time.time() - mtime


def _fetch(kustomization_dir, dest, log=print):
log(f"Fetching {dest}")
dest_dir = os.path.dirname(dest)
Expand Down

0 comments on commit 977c65c

Please sign in to comment.