Spaces:
Sleeping
Sleeping
| set -e | |
| RESULT_DIR=/data | |
| RESULT_FILE="$RESULT_DIR/space-lock-test-$(date -u +%Y%m%dT%H%M%SZ).txt" | |
| mkdir -p "$RESULT_DIR" | |
| # Dummy HTTP server so the Space stays running and serves the result | |
| python3 -c " | |
| import http.server, socketserver | |
| class H(http.server.BaseHTTPRequestHandler): | |
| def do_GET(self): | |
| self.send_response(200); self.send_header('Content-Type','text/plain'); self.end_headers() | |
| try: | |
| with open('$RESULT_FILE') as f: self.wfile.write(f.read().encode()) | |
| except FileNotFoundError: self.wfile.write(b'running...') | |
| def log_message(self,*a,**k): pass | |
| socketserver.TCPServer.allow_reuse_address = True | |
| with socketserver.TCPServer(('0.0.0.0',7860), H) as s: s.serve_forever() | |
| " & | |
| SERVER_PID=$! | |
| ( | |
| echo "=== Space bucket-mount lock repro ===" | |
| echo "date: $(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| echo "hostname: $(hostname)" | |
| echo "kernel: $(uname -r)" | |
| echo "sqlite: $(python3 -c 'import sqlite3;print(sqlite3.sqlite_version)')" | |
| echo | |
| echo "=== mount /data ===" | |
| mount | grep -E "data|fuse" || echo "(no fuse mount)" | |
| echo | |
| echo "=== fcntl POSIX inter-process lock ===" | |
| cat > /tmp/fcntl_test.py << 'PYEOF' | |
| import fcntl, os, sys, time, struct | |
| path, role = sys.argv[1], sys.argv[2] | |
| fd = os.open(path, os.O_RDWR | os.O_CREAT, 0o644) | |
| ld = struct.pack("hhqqi", fcntl.F_WRLCK, os.SEEK_SET, 0, 1, 0) | |
| if role == "holder": | |
| fcntl.fcntl(fd, fcntl.F_SETLK, ld); print("HOLDER: acquired"); time.sleep(3); print("HOLDER: done") | |
| else: | |
| time.sleep(0.5) | |
| try: fcntl.fcntl(fd, fcntl.F_SETLK, ld); print("CONTENDER: got it (BAD)"); sys.exit(2) | |
| except BlockingIOError: print("CONTENDER: EAGAIN (expected)") | |
| PYEOF | |
| rm -f /data/locktest.bin | |
| python3 /tmp/fcntl_test.py /data/locktest.bin holder > /tmp/h.out 2>&1 & | |
| HPID=$! | |
| sleep 0.1 | |
| python3 /tmp/fcntl_test.py /data/locktest.bin contender; CR=$? | |
| wait $HPID; HR=$? | |
| echo "HOLDER (exit=$HR):"; cat /tmp/h.out | |
| echo "CONTENDER (exit=$CR)" | |
| echo | |
| echo "=== fcntl.flock() inter-thread (same process) ===" | |
| cat > /tmp/flock_thr.py << 'PYEOF' | |
| import fcntl, os, sys, threading, time | |
| path = sys.argv[1] | |
| N_THR = 8; N_ITER = 100 | |
| counter = [0] | |
| def w(t): | |
| for _ in range(N_ITER): | |
| lf = open(path, "w") | |
| for _ in range(1000): | |
| try: fcntl.flock(lf.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB); break | |
| except BlockingIOError: time.sleep(0.001) | |
| v = counter[0]; time.sleep(0.0005); counter[0] = v + 1 | |
| fcntl.flock(lf.fileno(), fcntl.LOCK_UN); lf.close() | |
| threads = [threading.Thread(target=w, args=(i,)) for i in range(N_THR)] | |
| [t.start() for t in threads]; [t.join() for t in threads] | |
| exp = N_THR * N_ITER | |
| print(f"threads={N_THR} iters={N_ITER} got={counter[0]} expected={exp}") | |
| print("RESULT:", "PASS" if counter[0] == exp else "FAIL") | |
| sys.exit(0 if counter[0] == exp else 1) | |
| PYEOF | |
| rm -f /data/flocktest | |
| python3 /tmp/flock_thr.py /data/flocktest | |
| echo | |
| echo "=== trackio pre-fix multi-thread write on /data ===" | |
| export TRACKIO_DIR=/data/trackio-prefix-test | |
| export SYSTEM=spaces | |
| rm -rf "$TRACKIO_DIR" && mkdir -p "$TRACKIO_DIR" | |
| python3 -c "import trackio; print('trackio loaded OK')" | |
| python3 /app/hammer.py | |
| echo | |
| echo "=== cleanup ===" | |
| rm -f /data/locktest.bin /data/flocktest | |
| rm -rf "$TRACKIO_DIR" | |
| echo "DONE" | |
| ) 2>&1 | tee "$RESULT_FILE" | |
| echo "Result file: $RESULT_FILE" | |
| wait $SERVER_PID | |