Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Impl status-update interface for Session/Kernel ORM objects (#…
…2310) ## 1. DB lifetime Convention As #2088 has been merged, we can control the lifetime of DB connections and DB transactions granularly. I suggest how we should manage DB connections and transactions. ### Manager Entrypoints There are two main entrypoints of the code paths in the Manager. - API handlers (webapps) - Event dispatch handlers (consumers, subscribers) These entrypoints invoke inner layers like AgentRegistry and the database model functions. ### Connection Generally, it is better to reuse a single connection to perform multiple transactions. - Ideally, we should keep only one database connection for each code path starting from an entrypoint. - However, connection reuse should be implemented via connection pools, instead of the application logic, particularly when it is not feasible to predict how long an operation will take in the application (like Backend.AI). ### Mutations All database mutations should be expressed as explicit transactions. - If there are business logic branches between database updates, those codes and queries should go inside a single transaction. - If the code inside a transaction involves a long-running interaction with other systems (e.g., agent RPC, storage-proxy operations), it should be split out from the transaction block. There may be multiple ways to handle such situation: - Introduce a state machine to mark the transitional states and the target states. It is also better to acquire/release transient database connections to make state transitions, instead of holding a single connection for an arbitrarily long time. - Defer the long-running operation as a background task and return the bgtask ID as the API response for further client-side tracking. ## 2. About ORM SQLAlchemy ORM objects can be updated in a session which the ORM object is fetched from. ```python # Example with Session() as session: kernel_row = session.scalar(select(Kernel).where(Kernel.id == "kernel-id")) kernel_row.name = "new-name" session.commit() # UPDATE SQL queried, the name of kernel record has been updated to "new-name" in DB # and the `kernel_row` object has also been updated. assert kernel_row.name == "new-name" # No assertion error ``` - SQLAlchemy Session should be opened before fetching any ORM object. - Not recommend passing any SQLAlchemy Engine, Connection or Session object to model layer APIs unless it is an ORM-object fetch function. Fetch functions need any of SQLAlchemy Engine, Connection or Session object but update functions don't need them. We can simply assign values to ORM attributes like the code above. ## 3. Set-state & Transit-state - **Set-state API** is called by external requests, such as client requests. Set-state API changes the current state to a state specified by the API parameter regardless of any condition or any rule defined in the state machine (We can set conditions to allow or block certain states **BEFORE calling** the Set-state API) - **Transit-state API** is called by external requests or the state machine itself. This API does not have a "state" parameter. When this API is called, the state machine determines its state by own conditions or rules and moves to the determined state. **Checklist:** (if applicable) - [x] Milestone metadata specifying the target backport version - [x] Documentation - Contents in the `docs` directory - docstrings in public interfaces and type annotations <!-- readthedocs-preview sorna start --> ---- 📚 Documentation preview 📚: https://sorna--2310.org.readthedocs.build/en/2310/ <!-- readthedocs-preview sorna end --> <!-- readthedocs-preview sorna-ko start --> ---- 📚 Documentation preview 📚: https://sorna-ko--2310.org.readthedocs.build/ko/2310/ <!-- readthedocs-preview sorna-ko end -->
- Loading branch information