from__future__importannotationsfromtypingimportOptionalimporttimeimportloggingfromsqlalchemyimportcreate_enginefromsqlalchemy.sql.expressionimporttextfromsqlalchemy.excimportProgrammingError,OperationalErrorfromsqlalchemy_utilsimportdatabase_exists,create_databasefrom.database_modelimportModelLOG=logging.getLogger(__name__)# Default chunk_time_interval. Might become configurable at some point:TIMESCALEDB_DEFAULT_CHUNK_SIZE_INTERVAL=512def_create_timescaledb_extension(engine):"""Create the timescaledb extension. :param engine: The database engine. """withengine.begin()asconn:conn.execute("CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;")timescale_tables={"world_states","muscle_actions",}withengine.begin()asconn:fortblintimescale_tables:cmd=(f"SELECT * FROM create_hypertable("f"'{tbl}', "# Table namef"'id', "# Primary partitioning columnf"chunk_time_interval => "f"{TIMESCALEDB_DEFAULT_CHUNK_SIZE_INTERVAL}, "f"if_not_exists => TRUE)")res=conn.execute(text(cmd))LOG.debug('Result of executing "%s" during setup: %s',cmd,res.fetchall(),)res.close()LOG.info("Created TimescaleDB hypertables: %s, set 'chunk_time_interval' ""parameter to %d. HINT: The chunk_time_interval should be chosen such ""that all active chunks of all your hypertables fit in 25%% of your ""RAM. You can change the value with TimescaleDB's ""set_chunk_time_interval() function.",", ".join(timescale_tables),TIMESCALEDB_DEFAULT_CHUNK_SIZE_INTERVAL,)
[docs]defsetup_database(uri:Optional[str]=None):"""Creates the database from the current model in one go. Parameters ---------- uri : str The complete database connection URI. """ifnoturi:frompalaestrai.coreimportRuntimeConfiguri=RuntimeConfig().store_uriengine=create_engine(uri)whilenotdatabase_exists(uri):i=1ifi>3:# Hardcoded max tries. No real reason to configure this.LOG.critical("Could not create the database. See errors above for more ""details. Giving up now.")raiseRuntimeError("Could not create database")try:create_database(uri)exceptOperationalErrorase:try:importpsycopg2.errorsifisinstance(e.orig,psycopg2.errors.ObjectInUse):LOG.warning("Could not create database because the template was ""in use. Retrying in %d seconds.",i,)time.sleep(i)else:breakexceptImportError:passexceptProgrammingErrorase:LOG.error("There was an error creating the database. I will continue ""and hope for the best. The error was: %s",e,)i+=1withengine.begin()asconn:try:Model.metadata.create_all(engine)exceptProgrammingErrorase:LOG.error("Could not create database: %s"%e)raiseetry:frommidas.tools.palaestrai.database_viewimport(# type: ignorecreate_midas_views,)ifengine.url.drivername=="psycopg2":# type: ignorecreate_midas_views(conn)exceptModuleNotFoundError:pass# Ok, don't create specific views if the tools are not pres.try:_create_timescaledb_extension(engine)exceptOperationalErrorase:LOG.warning("Could not create extension timescaledb and create hypertables: ""%s. ""Your database setup might lead to noticeable slowdowns with ""larger experiment runs. Please upgrade to PostgreSQL with ""TimescaleDB for the best performance."%e)