flip_api.fl_services.services.fl_scheduler_service

Functions

remove_job(→ None)

Sets the job status to DELETED and clears the started timestamp.

remove_job_from_queue(→ None)

Sets the job status to DELETED for all jobs associated with the given model ID.

log_queue_positions(→ None)

Emit one typed activity row per queued job whose queue position changed.

revert_scheduler_pickup(→ None)

Sets the scheduler status to AVAILABLE and clears the job_id.

get_net_by_model_id(...)

Get information for a net by model ID.

get_net_by_name(...)

Get information for a net by name

get_slot_names_by_trust_ids(→ dict[uuid.UUID, str])

Map each trust id to the slot_name of its bound FL kit slot.

get_nets(→ list[flip_api.domain.interfaces.fl.INetDetails])

Fetches all nets from the database.

resolve_backend(→ flip_api.domain.schemas.types.FLBackend)

Resolve the active FL backend at runtime from the nets (never from a static env var).

check_for_available_net(...)

Checks for any available nets and marks one as busy if found.

check_for_queued_jobs(...)

Checks for any queued jobs for a given scheduler.

prepare_and_start_training(→ None)

Prepares and starts the training process for a given model.

get_required_training_details(...)

Fetches the necessary details for training a model, including the project ID and the latest cohort query.

update_fl_scheduler(→ None)

Updates the FL job status to COMPLETED and sets the associated scheduler status to AVAILABLE.

Module Contents

flip_api.fl_services.services.fl_scheduler_service.remove_job(job_id: uuid.UUID, session: sqlmodel.Session) None

Sets the job status to DELETED and clears the started timestamp.

Parameters:
  • job_id (UUID) – The ID of the job to remove.

  • session (Session) – SQLModel session.

Returns:

None

Raises:
flip_api.fl_services.services.fl_scheduler_service.remove_job_from_queue(model_id: uuid.UUID, session: sqlmodel.Session) None

Sets the job status to DELETED for all jobs associated with the given model ID.

Parameters:
  • model_id (UUID) – The model ID whose jobs are to be removed.

  • session (Session) – SQLModel session.

Returns:

None

Raises:

DatabaseError – If the update fails at the DB layer.

flip_api.fl_services.services.fl_scheduler_service.log_queue_positions(session: sqlmodel.Session) None

Emit one typed activity row per queued job whose queue position changed.

The FL queue is FLJob rows in QUEUED status, ordered by created ascending — exactly the order check_for_queued_jobs picks from, so position 1 is the next model to start when a net frees up. For each queued job the last logged QUEUE_POSITION row is compared (keyed by job id, so a re-initiated model always re-logs its first position); a row is written only when the position is new or changed, making the function idempotent — callers invoke it after any queue mutation without worrying about noise. Rows are batched into a single commit so a deep queue never costs O(N) commits per mutation.

Idempotence holds for serialized callers only: emit-on-change is a read-then-write with nothing locked, so concurrent calls (the scheduler tick on its BackgroundScheduler thread vs a request thread, or a second flip-api replica) can both read the same prior rows and both write, leaving duplicate — or transiently contradictory — feed rows. That is tolerated: positions are a display nicety and the next mutation self-heals. The hub currently runs one flip-api replica with a single Uvicorn worker (ECS desired_count = 1, no autoscaling; fastapi run without --workers), so only in-process thread interleaving can race. Scaling flip-api out multiplies the emission sites per tick — close the race then with SELECT ... FOR UPDATE on the queued jobs or a partial unique index over the details (job_id, position) pair.

Positions are a display nicety: any failure is logged, the batch is rolled back, and nothing is raised — an emission problem can never wedge the scheduler tick or the caller’s queue mutation. Precondition: call this only after the caller has committed its own mutation; the failure-path rollback would otherwise discard the caller’s uncommitted state.

Parameters:

session (Session) – The database session.

Returns:

None

flip_api.fl_services.services.fl_scheduler_service.revert_scheduler_pickup(scheduler_id: uuid.UUID, session: sqlmodel.Session) None

Sets the scheduler status to AVAILABLE and clears the job_id.

Parameters:
  • scheduler_id (UUID) – The ID of the scheduler to revert.

  • session (Session) – SQLModel session.

Returns:

None

Raises:
flip_api.fl_services.services.fl_scheduler_service.get_net_by_model_id(model_id: uuid.UUID, session: sqlmodel.Session) flip_api.domain.interfaces.fl.INetDetails

Get information for a net by model ID.

Parameters:
  • model_id (UUID) – The model ID.

  • session (Session) – SQLModel session.

Returns:

Details of the net.

Return type:

INetDetails

Raises:
flip_api.fl_services.services.fl_scheduler_service.get_net_by_name(name: str, session: sqlmodel.Session) flip_api.domain.interfaces.fl.INetDetails | None

Get information for a net by name

Parameters:
  • name (str) – Name of the net

  • session (Session) – SQLModel session

Returns:

Details of the net or None if not found

Return type:

INetDetails | None

Raises:

DatabaseError – If the query fails at the DB layer.

flip_api.fl_services.services.fl_scheduler_service.get_slot_names_by_trust_ids(trust_ids: list[uuid.UUID], session: sqlmodel.Session) dict[uuid.UUID, str]

Map each trust id to the slot_name of its bound FL kit slot.

The FL protocol identifies participants by the slot identity (the CN baked into the kit’s cert), which is independent of the trust’s display name on the hub. Callers that need to talk to / compare against an FL participant must look up the slot name rather than using Trust.name — admin-chosen display names can change without rotating the kit, and don’t carry into the FL protocol.

Parameters:
  • trust_ids (list[UUID]) – Trust ids to resolve. Empty input → empty mapping.

  • session (Session) – SQLModel session.

Returns:

trust_id slot_name. Trusts without an assigned slot are absent from the result; callers should treat a miss as “no FL identity yet”.

Return type:

dict[UUID, str]

flip_api.fl_services.services.fl_scheduler_service.get_nets(session: sqlmodel.Session) list[flip_api.domain.interfaces.fl.INetDetails]

Fetches all nets from the database.

Parameters:

session (Session) – The database session.

Returns:

A list of all nets.

Return type:

list[INetDetails]

Raises:
flip_api.fl_services.services.fl_scheduler_service.resolve_backend(session: sqlmodel.Session, net: flip_api.domain.interfaces.fl.INetDetails | None = None) flip_api.domain.schemas.types.FLBackend

Resolve the active FL backend at runtime from the nets (never from a static env var).

Every net carries a non-null fl_backend set at seed time from FL_BACKEND. That seeded value is canonical — there is no runtime reconciliation — so resolution always reads the DB.

Parameters:
  • session (Session) – SQLModel session (used when no net is given).

  • net (INetDetails | None) – When given, use this net’s backend (the job is already pinned to it). When None (e.g. at model creation, before scheduling), use any net’s backend — all nets run the same backend in single-backend mode.

Returns:

The resolved backend (nvflare or flower).

Return type:

FLBackend

Raises:

ValueError – If no FL nets are registered at all (empty NET_ENDPOINTS / misconfig).

flip_api.fl_services.services.fl_scheduler_service.check_for_available_net(session: sqlmodel.Session) flip_api.domain.interfaces.fl.ISchedulerResponse | None

Checks for any available nets and marks one as busy if found.

Parameters:

session (Session) – The database session.

Returns:

The scheduler response if an available net is found, otherwise None.

Return type:

ISchedulerResponse | None

Raises:

DatabaseError – If the update fails at the DB layer.

flip_api.fl_services.services.fl_scheduler_service.check_for_queued_jobs(scheduler_id: uuid.UUID, session: sqlmodel.Session) flip_api.domain.interfaces.fl.IJobResponse | None

Checks for any queued jobs for a given scheduler.

Parameters:
  • scheduler_id (UUID) – The ID of the scheduler to check.

  • session (Session) – The database session.

Returns:

The job response if a queued job is found, otherwise None.

Return type:

IJobResponse | None

Raises:
flip_api.fl_services.services.fl_scheduler_service.prepare_and_start_training(model_id: uuid.UUID, fl_job_id: uuid.UUID, trust_ids: list[uuid.UUID], session: sqlmodel.Session) None

Prepares and starts the training process for a given model.

Parameters:
  • model_id (UUID) – The ID of the model to train.

  • fl_job_id (UUID) – The ID of the federated learning job.

  • trust_ids (list[UUID]) – The trust ids participating in the training. Names are looked up from the trust table here — at the FL backend boundary, which is the only place the FL protocol’s name-based addressing matters.

  • session (Session) – The database session.

Returns:

None

Raises:

Exception – If the FL backend is unsupported, the net endpoint cannot be resolved, client availability validation fails, or training fails to start. On failure the job is removed, the model is marked as errored, and the original exception is re-raised.

flip_api.fl_services.services.fl_scheduler_service.get_required_training_details(model_id: uuid.UUID, session: sqlmodel.Session) flip_api.domain.interfaces.fl.IRequiredTrainingInformation

Fetches the necessary details for training a model, including the project ID and the latest cohort query.

Parameters:
  • model_id (UUID) – The ID of the model to train.

  • session (Session) – The database session.

Returns:

The required training information.

Return type:

IRequiredTrainingInformation

Raises:
flip_api.fl_services.services.fl_scheduler_service.update_fl_scheduler(model_id: uuid.UUID, session: sqlmodel.Session) None

Updates the FL job status to COMPLETED and sets the associated scheduler status to AVAILABLE.

Parameters:
  • model_id (UUID) – The ID of the model to update.

  • session (Session) – The database session.

Returns:

None

Raises:

DatabaseError – If the update fails at the DB layer.