def get_learner(
    url: str, log_fname: str, job_id: str, job_name: str
) -> tuple[BaseLearner, str | list[str]]:
    """Get a learner from the database running at `url` and this learner's
    process will be logged in `log_fname` and running under `job_id`.

    Parameters
    ----------
    url : str
        The url of the database manager running via
        (`adaptive_scheduler.server_support.manage_database`).
    log_fname : str
        The filename of the log-file. Should be passed in the job-script.
    job_id : str
        The job_id of the process the job. Should be passed in the job-script.
    job_name : str
        The name of the job. Should be passed in the job-script.

    Returns
    -------
    learner : `adaptive.BaseLearner`
        Learner that is chosen.
    fname : str
        The filename of the learner that was chosen.
    """
    _add_log_file_handler(log_fname)
    log.info(
        "trying to get learner", job_id=job_id, log_fname=log_fname, job_name=job_name
    )
    with ctx.socket(zmq.REQ) as socket:
        socket.setsockopt(zmq.LINGER, 0)
        socket.setsockopt(zmq.SNDTIMEO, 300_000)  # timeout after 300s
        socket.connect(url)
        socket.send_serialized(("start", job_id, log_fname, job_name), _serialize)
        log.info("sent start signal, going to wait 60s for a reply.")
        socket.setsockopt(zmq.RCVTIMEO, 300_000)  # timeout after 300s
        reply = socket.recv_serialized(_deserialize)
        log.info("got reply", reply=str(reply))
        if reply is None:
            msg = "No learners to be run."
            exception = RuntimeError(msg)
            log_exception(log, msg, exception)
            raise exception
        elif isinstance(reply, Exception):
            log_exception(log, "got an exception", exception=reply)
            raise reply
        else:
            learner, fname = reply
            log.info("got fname and learner")

    log.info("picked a learner")
    return learner, maybe_lst(fname)
def tell_done(url: str, fname: str) -> None:
    """Tell the database that the learner has reached it's goal.

    Parameters
    ----------
    url : str
        The url of the database manager running via
        (`adaptive_scheduler.server_support.manage_database`).
    fname : str
        The filename of the learner that is done.
    """
    log.info("goal reached! 🎉🎊🥳")
    with ctx.socket(zmq.REQ) as socket:
        socket.connect(url)
        socket.send_serialized(("stop", fname), _serialize)
        socket.setsockopt(zmq.RCVTIMEO, 10_000)  # timeout after 10s
        log.info("sent stop signal, going to wait 10s for a reply", fname=fname)
        socket.recv_serialized(_deserialize)  # Needed because of socket type