Skip to content

Persistent Client Mode

Persistent Client Mode enables a single long-running client process to execute multiple simulations sequentially without terminating. This differs from Legacy Mode, where each simulation spawns a new client process that terminates after completion.

In Legacy Mode, the server repeatedly submits new client scripts (via SLURM or job scheduler) for each simulation. This introduces overhead:

  • Process startup/teardown cost per simulation
  • Reinitialization of data structures
  • Potential delays between simulation completions and next job submission

Persistent Client Mode eliminates this overhead by maintaining a single client process in a fetch loop, allowing the server to dynamically assign simulations without spawning new processes.

Activate persistent client mode by setting:

{
    "study_options": {
        "persistent_client_mode": true
    }
}

Architecture

Client-Server Communication Pattern

Persistent clients follow a request-response fetch loop architecture:

  1. Initialization Phase (once):

    • Client calls melissa_init() to register fields and establish server connection
    • Client receives server configuration (memmap path, port mapping)
  2. Fetch Loop (repeats until termination):

    • Client sends melissa_fetch_next_parameters() request to server
    • Server responds with Simulation ID, Parameter values (loaded from memory-mapped array), Termination flag (indicating end of study)
    • Client runs simulation with fetched parameters
    • Client sends results via melissa_send_float32() calls
    • Loop continues or exits based on termination flag (manual break)
  3. Finalization Phase (once):

  4. Client calls melissa_finalize() to clean up resources

Note

Actual parameter values are read by the client directly from a memory-mapped file using sim_id as the row index, eliminating the need to transmit large parameter arrays over the network.

Server-Side Simulation Assignment

The server maintains a queue of simulation IDs (0 to N-1) and assigns them to persistent clients on demand:

  • When a persistent client requests the next parameters, the server pops a simulation ID from the queue
  • The mapping sim_id → persistent_client_id is recorded for failure detection

This allows the server to dynamically distribute simulations across available persistent clients without pre-assigning them at startup.

Example of a Persistent Client Simulation Loop

from mpi4py import MPI
from melissa.client.api import (
    melissa_init,
    melissa_finalize,
    melissa_send_float32,
    melissa_fetch_next_parameters,
)


def solve_system(parameters, tf: float, dt: float):
    t = 0.0
    state = parameters
    while t < tf:
        state = stepper(state, dt)
        t += dt
        yield state


def main():
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()

    vect_size = len(initial_conditions)
    melissa_init("field_state", vect_size, comm)

    while True:
        fetched = melissa_fetch_next_parameters()

        if fetched.is_terminated:
            break

        sim_id = fetched.sim_id
        parameters = fetched.parameters

        if rank == 0:
            print(f"Running simulation {sim_id} with parameters {parameters}")

        for timestep, state in enumerate(solve_system(parameters, tf=10.0, dt=0.01)):
            melissa_send_float32("field_state", state)

    melissa_finalize()


if __name__ == "__main__":
    main()

Practical Guidance

  • Parallelism: job_limit controls how many persistent clients run in parallel. Size it to fully utilize your allocated resources.

  • Job Status: timer_delay does not govern job submission in persistent client mode, but a short interval is still recommended for early failure detection.

  • Scheduling: The server assigns simulations from a queue to whichever client is available next. There is no affinity between a client and its simulations. All simulations are assumed to be homogeneous in resources, MPI communicator usage, and runtimes. It is expected that any simulation should run under any persistent client.

  • Server failure: All running clients are restarted and any incomplete simulations are moved to the front of the queue, so they run before the new ones.

  • Client failure: The failed persistent client is replaced with a new one under a fresh ID, preserving the original client's error logs. Its incomplete simulations are likewise prioritized at the front of the queue.

  • Timeouts: Persistent clients are expected to run for the full study walltime, so the server does not apply timeout logic to them i.e fault-tolerance will not check for the timeouts and restart these clients.