Skip to content

pd_session_orchestration

Manages the complete lifecycle of a plant design editing session: lock acquisition, working copy download/upload, crash recovery, and lock release.

Replaces S3ProjektManager (write-access flow) and S3ProjectShutdownHandler.

Structure

pd_session_orchestration/
    model/
        session_state.py          SessionState enum, SessionError, constants
    controller/
        session_controller.py     PlantDesignSession - main lifecycle API
        crash_recovery.py         CrashRecoveryHandler - atexit/signal hooks, emergency backups
        file_transfer.py          Parallel download/upload, R2 key builders, content type mapping

Usage

from src.modules.plant_design.startup_system.bound_subsystems.pd_session_orchestration.controller.session_controller import (
    PlantDesignSession,
)

session = PlantDesignSession()

# Open session (acquires R2 lock, downloads working copy)
work_path = session.open(projekt_id="Kraftwerk_1234567", user="admin", stage="planning")

# ... caller works on files at work_path ...

# Close session (uploads changes, releases lock)
session.close()

Session Lifecycle

Online:   IDLE -> ACQUIRING -> DOWNLOADING -> ACTIVE -> UPLOADING -> CLOSED
Resume:   IDLE -> ACQUIRING -> RESTORING  -> ACTIVE -> UPLOADING -> CLOSED
Crash:    ACTIVE -> RECOVERING -> CLOSED
Abort:    any -> CLOSED

open(projekt_id, user, stage)

  1. Acquires stage lock in R2 (planning.lock.json) via BucketService.acquire_lock()
  2. Downloads working copy from R2 to .app_temp/plant_design/ in parallel (10 workers)
  3. Registers atexit/signal hooks for crash recovery
  4. Returns the local Path where the caller can edit files

close()

  1. Uploads working directory back to R2 in parallel
  2. Releases the R2 lock
  3. Unregisters crash hooks, cleans up local working directory

abort()

Releases the lock and cleans up without uploading. Used when the user cancels editing.

Crash Recovery

When the application terminates unexpectedly during an active session:

  1. During crash (automatic via atexit/signal hooks):
  2. Working copy backed up from .app_temp/ to persistent_data/plant_design/recovery/
  3. R2 lock released (or expires automatically after 12h)
  4. Recovery info file preserved on disk

  5. Next startup (caller-driven):

  6. Call session.check_previous_crash() to detect orphaned sessions
  7. Returns recovery data (projekt_id, user, stage, backup_path) or None
  8. Caller shows recovery dialog, then either resumes or discards

Lock Management

  • Lock file: {stage}.lock.json with user, since, machine, expires fields
  • Atomic acquisition via IfNoneMatch="*" (R2 conditional put)
  • Same-user re-entry allowed (idempotent)
  • 12-hour expiry as safety net

Signals

Signal Args Description
state_changed int (SessionState value) Emitted on every state transition
progress str, int (message, percentage) Emitted during download/upload

Dependencies

  • src.shared_services.cloud_com.r2_com - BucketService for all R2 operations
  • src.shared_services.path_management - PathDef-based path resolution
  • src.modules.plant_design.startup_system.constants.paths - SessionPaths, RecoveryPaths