Skip to content

Commit 88d2e4a

Browse files
authored
fix: No module named 'fcntl' (#434)
1 parent ec2a881 commit 88d2e4a

File tree

3 files changed

+29
-30
lines changed

3 files changed

+29
-30
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,5 @@ dmypy.json
138138
.pyre/
139139
runpod/_version.py
140140
.runpod_jobs.pkl
141+
142+
*.lock

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ click >= 8.1.7
77
colorama >= 0.2.5, < 0.4.7
88
cryptography < 46.0.0
99
fastapi[all] >= 0.94.0
10+
filelock >= 3.0.0
1011
paramiko >= 3.3.1
1112
prettytable >= 3.9.0
1213
py-cpuinfo >= 9.0.0

runpod/serverless/modules/worker_state.py

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
import time
77
import uuid
88
import pickle
9-
import fcntl
109
import tempfile
1110
from typing import Any, Dict, Optional, Set
1211

12+
from filelock import FileLock
13+
1314
from .rp_logger import RunPodLogger
1415

1516

@@ -95,25 +96,23 @@ def _load_state(self):
9596
os.path.exists(self._STATE_FILE)
9697
and os.path.getsize(self._STATE_FILE) > 0
9798
):
98-
with open(self._STATE_FILE, "rb") as f:
99-
fcntl.flock(f, fcntl.LOCK_SH)
100-
try:
101-
loaded_jobs = pickle.load(f)
102-
# Clear current state and add loaded jobs
103-
super().clear()
104-
for job in loaded_jobs:
105-
set.add(
106-
self, job
107-
) # Use set.add to avoid triggering _save_state
108-
109-
except (EOFError, pickle.UnpicklingError):
110-
# Handle empty or corrupted file
111-
log.debug(
112-
"JobsProgress: Failed to load state file, starting with empty state"
113-
)
114-
pass
115-
finally:
116-
fcntl.flock(f, fcntl.LOCK_UN)
99+
with FileLock(self._STATE_FILE + '.lock'):
100+
with open(self._STATE_FILE, "rb") as f:
101+
try:
102+
loaded_jobs = pickle.load(f)
103+
# Clear current state and add loaded jobs
104+
super().clear()
105+
for job in loaded_jobs:
106+
set.add(
107+
self, job
108+
) # Use set.add to avoid triggering _save_state
109+
110+
except (EOFError, pickle.UnpicklingError):
111+
# Handle empty or corrupted file
112+
log.debug(
113+
"JobsProgress: Failed to load state file, starting with empty state"
114+
)
115+
pass
117116

118117
except FileNotFoundError:
119118
log.debug("JobsProgress: No state file found, starting with empty state")
@@ -123,17 +122,14 @@ def _save_state(self):
123122
"""Save jobs state to pickle file with atomic write and file locking."""
124123
try:
125124
# Use temporary file for atomic write
126-
with tempfile.NamedTemporaryFile(
127-
dir=self._STATE_DIR, delete=False, mode="wb"
128-
) as temp_f:
129-
fcntl.flock(temp_f, fcntl.LOCK_EX)
130-
try:
125+
with FileLock(self._STATE_FILE + '.lock'):
126+
with tempfile.NamedTemporaryFile(
127+
dir=self._STATE_DIR, delete=False, mode="wb"
128+
) as temp_f:
131129
pickle.dump(set(self), temp_f)
132-
finally:
133-
fcntl.flock(temp_f, fcntl.LOCK_UN)
134-
135-
# Atomically replace the state file
136-
os.replace(temp_f.name, self._STATE_FILE)
130+
131+
# Atomically replace the state file
132+
os.replace(temp_f.name, self._STATE_FILE)
137133
except Exception as e:
138134
log.error(f"Failed to save job state: {e}")
139135

0 commit comments

Comments
 (0)