- 
                Notifications
    
You must be signed in to change notification settings  - Fork 32
 
♻️ Preparations in webserver to integrate asyncpg engine #6466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
            pcrespov
  merged 21 commits into
  ITISFoundation:master
from
pcrespov:mai/db-async-engine
  
      
      
   
  Sep 30, 2024 
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            21 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      28663c9
              
                asyncpg interface for both fastapi and aiohttp
              
              
                pcrespov 76f1c77
              
                PostgresSettings url helper members
              
              
                pcrespov c862398
              
                rename app's state key for aiopg engine
              
              
                pcrespov 6c1074a
              
                same in storage service
              
              
                pcrespov 7aa76dd
              
                rename get_database_engine -> get_aiopg_engine
              
              
                pcrespov cf49d8f
              
                uses api
              
              
                pcrespov f5da4f2
              
                splits aiopg and asyncpg contributions
              
              
                pcrespov 4e12b09
              
                reverts names
              
              
                pcrespov b0ec158
              
                not active
              
              
                pcrespov f60a38f
              
                use api to get engine from app
              
              
                pcrespov c9485df
              
                catalog
              
              
                pcrespov 8a077fa
              
                payments
              
              
                pcrespov 9bf9d04
              
                storage
              
              
                pcrespov b745870
              
                test
              
              
                pcrespov f36ea9f
              
                pylint
              
              
                pcrespov 70f4d87
              
                cleanup
              
              
                pcrespov c18b58f
              
                minor fix in tests
              
              
                pcrespov 54179cd
              
                server settings
              
              
                pcrespov 08a6261
              
                Merge branch 'master' into mai/db-async-engine
              
              
                pcrespov 0a8de81
              
                Merge branch 'master' into mai/db-async-engine
              
              
                pcrespov da1b807
              
                Merge branch 'master' into mai/db-async-engine
              
              
                pcrespov File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
  
    
      
          
            74 changes: 74 additions & 0 deletions
          
          74 
        
  packages/service-library/src/servicelib/aiohttp/db_asyncpg_engine.py
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| """ | ||
| Helpers on asyncpg specific for aiohttp | ||
| 
     | 
||
| SEE migration aiopg->asyncpg https://github.com/ITISFoundation/osparc-simcore/issues/4529 | ||
| """ | ||
| 
     | 
||
| 
     | 
||
| import logging | ||
| from typing import Final | ||
| 
     | 
||
| from aiohttp import web | ||
| from servicelib.logging_utils import log_context | ||
| from settings_library.postgres import PostgresSettings | ||
| from simcore_postgres_database.utils_aiosqlalchemy import ( # type: ignore[import-not-found] # this on is unclear | ||
| get_pg_engine_stateinfo, | ||
| ) | ||
| from sqlalchemy.ext.asyncio import AsyncEngine | ||
| 
     | 
||
| from ..db_asyncpg_utils import create_async_engine_and_pg_database_ready | ||
| from ..logging_utils import log_context | ||
| 
     | 
||
| APP_DB_ASYNC_ENGINE_KEY: Final[str] = f"{__name__ }.AsyncEngine" | ||
| 
     | 
||
| 
     | 
||
| _logger = logging.getLogger(__name__) | ||
| 
     | 
||
| 
     | 
||
| def _set_async_engine_to_app_state(app: web.Application, engine: AsyncEngine): | ||
| if exists := app.get(APP_DB_ASYNC_ENGINE_KEY, None): | ||
| msg = f"An instance of {type(exists)} already in app[{APP_DB_ASYNC_ENGINE_KEY}]={exists}" | ||
| raise ValueError(msg) | ||
| 
     | 
||
| app[APP_DB_ASYNC_ENGINE_KEY] = engine | ||
| return get_async_engine(app) | ||
| 
     | 
||
| 
     | 
||
| def get_async_engine(app: web.Application) -> AsyncEngine: | ||
| engine: AsyncEngine = app[APP_DB_ASYNC_ENGINE_KEY] | ||
| assert engine # nosec | ||
| return engine | ||
| 
     | 
||
| 
     | 
||
| async def connect_to_db(app: web.Application, settings: PostgresSettings) -> None: | ||
| """ | ||
| - db services up, data migrated and ready to use | ||
| - sets an engine in app state (use `get_async_engine(app)` to retrieve) | ||
| """ | ||
| if settings.POSTGRES_CLIENT_NAME: | ||
| settings = settings.copy( | ||
| update={"POSTGRES_CLIENT_NAME": settings.POSTGRES_CLIENT_NAME + "-asyncpg"} | ||
| ) | ||
| 
     | 
||
| with log_context( | ||
| _logger, | ||
| logging.INFO, | ||
| "Connecting app[APP_DB_ASYNC_ENGINE_KEY] to postgres with %s", | ||
| f"{settings=}", | ||
| ): | ||
| engine = await create_async_engine_and_pg_database_ready(settings) | ||
| _set_async_engine_to_app_state(app, engine) | ||
| 
     | 
||
| _logger.info( | ||
| "app[APP_DB_ASYNC_ENGINE_KEY] ready : %s", | ||
| await get_pg_engine_stateinfo(engine), | ||
| ) | ||
| 
     | 
||
| 
     | 
||
| async def close_db_connection(app: web.Application) -> None: | ||
| engine = get_async_engine(app) | ||
| with log_context( | ||
| _logger, logging.DEBUG, f"app[APP_DB_ASYNC_ENGINE_KEY] disconnect of {engine}" | ||
| ): | ||
| if engine: | ||
| await engine.dispose() | 
        
          
  
    
      
          
            63 changes: 63 additions & 0 deletions
          
          63 
        
  packages/service-library/src/servicelib/db_asyncpg_utils.py
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import logging | ||
| import time | ||
| from datetime import timedelta | ||
| 
     | 
||
| from models_library.healthchecks import IsNonResponsive, IsResponsive, LivenessResult | ||
| from settings_library.postgres import PostgresSettings | ||
| from simcore_postgres_database.utils_aiosqlalchemy import ( # type: ignore[import-not-found] # this on is unclear | ||
| raise_if_migration_not_ready, | ||
| ) | ||
| from sqlalchemy.exc import SQLAlchemyError | ||
| from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine | ||
| from tenacity import retry | ||
| 
     | 
||
| from .retry_policies import PostgresRetryPolicyUponInitialization | ||
| 
     | 
||
| _logger = logging.getLogger(__name__) | ||
| 
     | 
||
| 
     | 
||
| @retry(**PostgresRetryPolicyUponInitialization(_logger).kwargs) | ||
| async def create_async_engine_and_pg_database_ready( | ||
| settings: PostgresSettings, | ||
| ) -> AsyncEngine: | ||
| """ | ||
| - creates asyncio engine | ||
| - waits until db service is up | ||
| - waits until db data is migrated (i.e. ready to use) | ||
| - returns engine | ||
| """ | ||
| server_settings = None | ||
| if settings.POSTGRES_CLIENT_NAME: | ||
| server_settings = { | ||
| "application_name": settings.POSTGRES_CLIENT_NAME, | ||
| } | ||
| 
     | 
||
| engine: AsyncEngine = create_async_engine( | ||
| settings.dsn_with_async_sqlalchemy, | ||
| pool_size=settings.POSTGRES_MINSIZE, | ||
| max_overflow=settings.POSTGRES_MAXSIZE - settings.POSTGRES_MINSIZE, | ||
| connect_args={"server_settings": server_settings}, | ||
| pool_pre_ping=True, # https://docs.sqlalchemy.org/en/14/core/pooling.html#dealing-with-disconnects | ||
| future=True, # this uses sqlalchemy 2.0 API, shall be removed when sqlalchemy 2.0 is released | ||
| ) | ||
| 
     | 
||
| try: | ||
| await raise_if_migration_not_ready(engine) | ||
| except Exception: | ||
| # NOTE: engine must be closed because retry will create a new engine | ||
| await engine.dispose() | ||
| raise | ||
| 
     | 
||
| return engine | ||
| 
     | 
||
| 
     | 
||
| async def check_postgres_liveness(engine: AsyncEngine) -> LivenessResult: | ||
| try: | ||
| tic = time.time() | ||
| # test | ||
| async with engine.connect(): | ||
| ... | ||
| elapsed_time = time.time() - tic | ||
| return IsResponsive(elapsed=timedelta(seconds=elapsed_time)) | ||
| except SQLAlchemyError as err: | ||
| return IsNonResponsive(reason=f"{err}") | ||
        
          
  
    
      
          
            33 changes: 33 additions & 0 deletions
          
          33 
        
  packages/service-library/src/servicelib/fastapi/db_asyncpg_engine.py
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import logging | ||
| 
     | 
||
| from fastapi import FastAPI | ||
| from settings_library.postgres import PostgresSettings | ||
| from simcore_postgres_database.utils_aiosqlalchemy import ( # type: ignore[import-not-found] # this on is unclear | ||
| get_pg_engine_stateinfo, | ||
| ) | ||
| 
     | 
||
| from ..db_asyncpg_utils import create_async_engine_and_pg_database_ready | ||
| from ..logging_utils import log_context | ||
| 
     | 
||
| _logger = logging.getLogger(__name__) | ||
| 
     | 
||
| 
     | 
||
| async def connect_to_db(app: FastAPI, settings: PostgresSettings) -> None: | ||
| with log_context( | ||
| _logger, | ||
| logging.DEBUG, | ||
| f"Connecting and migraging {settings.dsn_with_async_sqlalchemy}", | ||
| ): | ||
| engine = await create_async_engine_and_pg_database_ready(settings) | ||
| 
     | 
||
| app.state.engine = engine | ||
| _logger.debug( | ||
| "Setup engine: %s", | ||
| await get_pg_engine_stateinfo(engine), | ||
| ) | ||
| 
     | 
||
| 
     | 
||
| async def close_db_connection(app: FastAPI) -> None: | ||
| with log_context(_logger, logging.DEBUG, f"db disconnect of {app.state.engine}"): | ||
| if engine := app.state.engine: | ||
| await engine.dispose() | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
  
    
      
          
            2 changes: 1 addition & 1 deletion
          
          2 
        
  .../resource-usage-tracker/src/simcore_service_resource_usage_tracker/modules/db/__init__.py
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
      
      Oops, something went wrong.
        
    
  
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.