Skip to content

Commit b859087

Browse files
committed
Skip recreating ensemble directory in storage
When writing errors.json, we should not recreate the ensemble directory in storage if it does not exist. Then the user has probably deleted it manually while Ert is running, and whatever happens is undefined behaviour. The consequence of recreating the directory is essentially a garbled storage directory that Ert later will fail to load. Keep the possibility that the realization directory has not been written yet, as it (at least in tests) set_failure is called immediately on a realization after the ensemble is created in storage. (cherry picked from commit 54c689a)
1 parent a6a2bb1 commit b859087

2 files changed

Lines changed: 36 additions & 2 deletions

File tree

src/ert/storage/local_ensemble.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import asyncio
44
import logging
5-
import os
65
import time
76
from collections.abc import Iterable
87
from datetime import datetime
@@ -325,8 +324,14 @@ def set_failure(
325324
"""
326325

327326
filename: Path = self._realization_dir(realization) / self._error_log_name
328-
os.makedirs(os.path.dirname(filename), exist_ok=True)
329327
error = _Failure(type=failure_type, message=message or "", time=datetime.now())
328+
if not filename.parent.parent.exists():
329+
logger.warning(
330+
f"Storage {filename.parent.parent} does not exist, "
331+
f"skipping writing {error} to {filename}"
332+
)
333+
return
334+
filename.parent.mkdir(exist_ok=True)
330335
self._storage._write_transaction(
331336
filename, error.model_dump_json(indent=2).encode("utf-8")
332337
)

tests/ert/unit_tests/storage/test_local_storage.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,35 @@ def test_data_fetching_missing_key(snake_oil_case):
10841084
assert dataframe.empty
10851085

10861086

1087+
def test_set_failure_will_create_realization_directory(storage):
1088+
# Setup
1089+
dummy_ensemble = storage.create_experiment().create_ensemble(
1090+
name="dummy", ensemble_size=1
1091+
)
1092+
assert dummy_ensemble._path.exists(), "Assumptions for test has changed"
1093+
assert not (dummy_ensemble._path / "realization-0").exists()
1094+
1095+
# Function test:
1096+
dummy_ensemble.set_failure(0, RealizationStorageState.FAILURE_IN_CURRENT)
1097+
1098+
# Then
1099+
assert dummy_ensemble._path.glob(f"realization-0/{dummy_ensemble._error_log_name}")
1100+
1101+
1102+
def test_set_failure_will_not_recreate_ensemble_directory(storage):
1103+
dummy_ensemble = storage.create_experiment().create_ensemble(
1104+
name="dummy", ensemble_size=1
1105+
)
1106+
# Emulate a user deleting storage:
1107+
shutil.rmtree(dummy_ensemble._path)
1108+
1109+
# Then when a realization fails internalizing,
1110+
dummy_ensemble.set_failure(0, RealizationStorageState.FAILURE_IN_CURRENT)
1111+
1112+
# the ensemble path will not be created
1113+
assert not dummy_ensemble._path.exists()
1114+
1115+
10871116
@dataclass
10881117
class Ensemble:
10891118
uuid: UUID

0 commit comments

Comments
 (0)