Skip to content

Commit

Permalink
Feature/SK-1367 | Refactor the creation of the databaseconnection (#807)
Browse files Browse the repository at this point in the history
  • Loading branch information
carl-andersson authored Feb 4, 2025
1 parent e367385 commit ed0c95a
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 179 deletions.
75 changes: 22 additions & 53 deletions fedn/network/api/shared.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,39 @@
import os

import pymongo
from pymongo.database import Database
from werkzeug.security import safe_join

from fedn.common.config import get_modelstorage_config, get_network_config, get_statestore_config
from fedn.common.config import get_modelstorage_config, get_network_config
from fedn.network.controller.control import Control
from fedn.network.storage.dbconnection import DatabaseConnection
from fedn.network.storage.s3.base import RepositoryBase
from fedn.network.storage.s3.miniorepository import MINIORepository
from fedn.network.storage.s3.repository import Repository
from fedn.network.storage.statestore.stores.client_store import ClientStore, MongoDBClientStore, SQLClientStore
from fedn.network.storage.statestore.stores.combiner_store import CombinerStore, MongoDBCombinerStore, SQLCombinerStore
from fedn.network.storage.statestore.stores.model_store import MongoDBModelStore, SQLModelStore
from fedn.network.storage.statestore.stores.package_store import MongoDBPackageStore, PackageStore, SQLPackageStore
from fedn.network.storage.statestore.stores.prediction_store import MongoDBPredictionStore, PredictionStore, SQLPredictionStore
from fedn.network.storage.statestore.stores.round_store import MongoDBRoundStore, RoundStore, SQLRoundStore
from fedn.network.storage.statestore.stores.session_store import MongoDBSessionStore, SQLSessionStore
from fedn.network.storage.statestore.stores.client_store import ClientStore
from fedn.network.storage.statestore.stores.combiner_store import CombinerStore
from fedn.network.storage.statestore.stores.model_store import ModelStore
from fedn.network.storage.statestore.stores.package_store import PackageStore
from fedn.network.storage.statestore.stores.prediction_store import PredictionStore
from fedn.network.storage.statestore.stores.round_store import RoundStore
from fedn.network.storage.statestore.stores.session_store import SessionStore
from fedn.network.storage.statestore.stores.shared import EntityNotFound
from fedn.network.storage.statestore.stores.status_store import MongoDBStatusStore, SQLStatusStore, StatusStore
from fedn.network.storage.statestore.stores.store import MyAbstractBase, engine
from fedn.network.storage.statestore.stores.validation_store import MongoDBValidationStore, SQLValidationStore, ValidationStore
from fedn.network.storage.statestore.stores.status_store import StatusStore
from fedn.network.storage.statestore.stores.validation_store import ValidationStore
from fedn.utils.checksum import sha

statestore_config = get_statestore_config()
modelstorage_config = get_modelstorage_config()
network_id = get_network_config()

client_store: ClientStore = None
validation_store: ValidationStore = None
combiner_store: CombinerStore = None
status_store: StatusStore = None
prediction_store: PredictionStore = None
round_store: RoundStore = None
package_store: PackageStore = None
model_store: SQLModelStore = None
session_store: SQLSessionStore = None

if statestore_config["type"] == "MongoDB":
mc = pymongo.MongoClient(**statestore_config["mongo_config"])
mc.server_info()
mdb: Database = mc[network_id]

client_store = MongoDBClientStore(mdb, "network.clients")
validation_store = MongoDBValidationStore(mdb, "control.validations")
combiner_store = MongoDBCombinerStore(mdb, "network.combiners")
status_store = MongoDBStatusStore(mdb, "control.status")
prediction_store = MongoDBPredictionStore(mdb, "control.predictions")
round_store = MongoDBRoundStore(mdb, "control.rounds")
package_store = MongoDBPackageStore(mdb, "control.packages")
model_store = MongoDBModelStore(mdb, "control.models")
session_store = MongoDBSessionStore(mdb, "control.sessions")

elif statestore_config["type"] in ["SQLite", "PostgreSQL"]:
MyAbstractBase.metadata.create_all(engine, checkfirst=True)

client_store = SQLClientStore()
validation_store = SQLValidationStore()
combiner_store = SQLCombinerStore()
status_store = SQLStatusStore()
prediction_store = SQLPredictionStore()
round_store = SQLRoundStore()
package_store = SQLPackageStore()
model_store = SQLModelStore()
session_store = SQLSessionStore()
else:
raise ValueError("Unknown statestore type")
# TODO: Refactor all access to the stores to use the DatabaseConnection
stores = DatabaseConnection().get_stores()
session_store: SessionStore = stores.session_store
model_store: ModelStore = stores.model_store
round_store: RoundStore = stores.round_store
package_store: PackageStore = stores.package_store
combiner_store: CombinerStore = stores.combiner_store
client_store: ClientStore = stores.client_store
status_store: StatusStore = stores.status_store
validation_store: ValidationStore = stores.validation_store
prediction_store: PredictionStore = stores.prediction_store


repository = Repository(modelstorage_config["storage_config"])
Expand Down
63 changes: 22 additions & 41 deletions fedn/network/combiner/shared.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,31 @@
import pymongo
from pymongo.database import Database

from fedn.common.config import get_modelstorage_config, get_network_config, get_statestore_config
from fedn.common.config import get_modelstorage_config
from fedn.network.combiner.modelservice import ModelService
from fedn.network.storage.dbconnection import DatabaseConnection
from fedn.network.storage.s3.repository import Repository
from fedn.network.storage.statestore.stores.client_store import ClientStore, MongoDBClientStore, SQLClientStore
from fedn.network.storage.statestore.stores.combiner_store import CombinerStore, MongoDBCombinerStore, SQLCombinerStore
from fedn.network.storage.statestore.stores.prediction_store import MongoDBPredictionStore, PredictionStore, SQLPredictionStore
from fedn.network.storage.statestore.stores.round_store import MongoDBRoundStore, RoundStore, SQLRoundStore
from fedn.network.storage.statestore.stores.status_store import MongoDBStatusStore, SQLStatusStore, StatusStore
from fedn.network.storage.statestore.stores.store import MyAbstractBase, engine
from fedn.network.storage.statestore.stores.validation_store import MongoDBValidationStore, SQLValidationStore, ValidationStore
from fedn.network.storage.statestore.stores.client_store import ClientStore
from fedn.network.storage.statestore.stores.combiner_store import CombinerStore
from fedn.network.storage.statestore.stores.model_store import ModelStore
from fedn.network.storage.statestore.stores.package_store import PackageStore
from fedn.network.storage.statestore.stores.prediction_store import PredictionStore
from fedn.network.storage.statestore.stores.round_store import RoundStore
from fedn.network.storage.statestore.stores.session_store import SessionStore
from fedn.network.storage.statestore.stores.status_store import StatusStore
from fedn.network.storage.statestore.stores.validation_store import ValidationStore

statestore_config = get_statestore_config()
modelstorage_config = get_modelstorage_config()
network_id = get_network_config()

client_store: ClientStore = None
validation_store: ValidationStore = None
combiner_store: CombinerStore = None
status_store: StatusStore = None
prediction_store: PredictionStore = None
round_store: RoundStore = None

if statestore_config["type"] == "MongoDB":
mc = pymongo.MongoClient(**statestore_config["mongo_config"])
mc.server_info()
mdb: Database = mc[network_id]

client_store = MongoDBClientStore(mdb, "network.clients")
validation_store = MongoDBValidationStore(mdb, "control.validations")
combiner_store = MongoDBCombinerStore(mdb, "network.combiners")
status_store = MongoDBStatusStore(mdb, "control.status")
prediction_store = MongoDBPredictionStore(mdb, "control.predictions")
round_store = MongoDBRoundStore(mdb, "control.rounds")
elif statestore_config["type"] in ["SQLite", "PostgreSQL"]:
MyAbstractBase.metadata.create_all(engine, checkfirst=True)
# TODO: Refactor all access to the stores to use the DatabaseConnection
stores = DatabaseConnection().get_stores()
session_store: SessionStore = stores.session_store
model_store: ModelStore = stores.model_store
round_store: RoundStore = stores.round_store
package_store: PackageStore = stores.package_store
combiner_store: CombinerStore = stores.combiner_store
client_store: ClientStore = stores.client_store
status_store: StatusStore = stores.status_store
validation_store: ValidationStore = stores.validation_store
prediction_store: PredictionStore = stores.prediction_store

client_store = SQLClientStore()
validation_store = SQLValidationStore()
combiner_store = SQLCombinerStore()
status_store = SQLStatusStore()
prediction_store = SQLPredictionStore()
round_store = SQLRoundStore()
else:
raise ValueError("Unknown statestore type")

repository = Repository(modelstorage_config["storage_config"], init_buckets=False)

Expand Down
180 changes: 180 additions & 0 deletions fedn/network/storage/dbconnection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""This module provides classes for managing database connections and stores in a federated network environment.
Classes:
StoreContainer: A container for various store instances.
DatabaseConnection: A singleton class for managing database connections and stores.
"""

import pymongo
from pymongo.database import Database
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from fedn.common.config import get_network_config, get_statestore_config
from fedn.network.storage.statestore.stores.client_store import ClientStore, MongoDBClientStore, SQLClientStore
from fedn.network.storage.statestore.stores.combiner_store import CombinerStore, MongoDBCombinerStore, SQLCombinerStore
from fedn.network.storage.statestore.stores.model_store import ModelStore, MongoDBModelStore, SQLModelStore
from fedn.network.storage.statestore.stores.package_store import MongoDBPackageStore, PackageStore, SQLPackageStore
from fedn.network.storage.statestore.stores.prediction_store import MongoDBPredictionStore, PredictionStore, SQLPredictionStore
from fedn.network.storage.statestore.stores.round_store import MongoDBRoundStore, RoundStore, SQLRoundStore
from fedn.network.storage.statestore.stores.session_store import MongoDBSessionStore, SessionStore, SQLSessionStore
from fedn.network.storage.statestore.stores.status_store import MongoDBStatusStore, SQLStatusStore, StatusStore
from fedn.network.storage.statestore.stores.store import MyAbstractBase
from fedn.network.storage.statestore.stores.validation_store import MongoDBValidationStore, SQLValidationStore, ValidationStore


class StoreContainer:
"""A container for various store instances."""

def __init__( # noqa: PLR0913
self,
client_store: ClientStore,
validation_store: ValidationStore,
combiner_store: CombinerStore,
status_store: StatusStore,
prediction_store: PredictionStore,
round_store: RoundStore,
package_store: PackageStore,
model_store: ModelStore,
session_store: SessionStore,
) -> None:
"""Initialize the StoreContainer with various store instances."""
self.client_store = client_store
self.validation_store = validation_store
self.combiner_store = combiner_store
self.status_store = status_store
self.prediction_store = prediction_store
self.round_store = round_store
self.package_store = package_store
self.model_store = model_store
self.session_store = session_store


class DatabaseConnection:
"""Singleton class for managing database connections and stores."""

_instance = None

def __new__(cls, *, force_create_new: bool = False) -> "DatabaseConnection":
"""Create a new instance of DatabaseConnection or return the existing singleton instance.
Args:
force_create_new (bool): If True, a new instance will be created regardless of the singleton pattern.
Returns:
DatabaseConnection: A new instance if force_create_new is True, otherwise the existing singleton instance.
"""
if cls._instance is None or force_create_new:
obj = super(DatabaseConnection, cls).__new__(cls)
obj._init_connection()
cls._instance = obj

return cls._instance

def _init_connection(self, statestore_config: dict = None, network_id: dict = None) -> None:
if statestore_config is None:
statestore_config = get_statestore_config()
if network_id is None:
network_id = get_network_config()

if statestore_config["type"] == "MongoDB":
mdb: Database = self._setup_mongo(statestore_config, network_id)

client_store = MongoDBClientStore(mdb, "network.clients")
validation_store = MongoDBValidationStore(mdb, "control.validations")
combiner_store = MongoDBCombinerStore(mdb, "network.combiners")
status_store = MongoDBStatusStore(mdb, "control.status")
prediction_store = MongoDBPredictionStore(mdb, "control.predictions")
round_store = MongoDBRoundStore(mdb, "control.rounds")
package_store = MongoDBPackageStore(mdb, "control.packages")
model_store = MongoDBModelStore(mdb, "control.models")
session_store = MongoDBSessionStore(mdb, "control.sessions")

elif statestore_config["type"] in ["SQLite", "PostgreSQL"]:
Session = self._setup_sql(statestore_config) # noqa: N806

client_store = SQLClientStore(Session)
validation_store = SQLValidationStore(Session)
combiner_store = SQLCombinerStore(Session)
status_store = SQLStatusStore(Session)
prediction_store = SQLPredictionStore(Session)
round_store = SQLRoundStore(Session)
package_store = SQLPackageStore(Session)
model_store = SQLModelStore(Session)
session_store = SQLSessionStore(Session)
else:
raise ValueError("Unknown statestore type")

self.sc = StoreContainer(
client_store, validation_store, combiner_store, status_store, prediction_store, round_store, package_store, model_store, session_store
)

def close(self) -> None:
"""Close the database connection."""
pass

def _setup_mongo(self, statestore_config: dict, network_id: str) -> "DatabaseConnection":
mc = pymongo.MongoClient(**statestore_config["mongo_config"])
mc.server_info()
mdb: Database = mc[network_id]

return mdb

def _setup_sql(self, statestore_config: dict) -> "DatabaseConnection":
if statestore_config["type"] == "SQLite":
engine = create_engine("sqlite:///my_database.db", echo=False)
elif statestore_config["type"] == "PostgreSQL":
postgres_config = statestore_config["postgres_config"]
username = postgres_config["username"]
password = postgres_config["password"]
host = postgres_config["host"]
port = postgres_config["port"]

engine = create_engine(f"postgresql://{username}:{password}@{host}:{port}/fedn_db", echo=False)

Session = sessionmaker(engine) # noqa: N806

MyAbstractBase.metadata.create_all(engine, checkfirst=True)

return Session

def get_stores(self) -> StoreContainer:
"""Get the StoreContainer instance."""
return self.sc

@property
def client_store(self) -> ClientStore:
return self.sc.client_store

@property
def validation_store(self) -> ValidationStore:
return self.sc.validation_store

@property
def combiner_store(self) -> CombinerStore:
return self.sc.combiner_store

@property
def status_store(self) -> StatusStore:
return self.sc.status_store

@property
def prediction_store(self) -> PredictionStore:
return self.sc.prediction_store

@property
def round_store(self) -> RoundStore:
return self.sc.round_store

@property
def package_store(self) -> PackageStore:
return self.sc.package_store

@property
def model_store(self) -> ModelStore:
return self.sc.model_store

@property
def session_store(self) -> SessionStore:
return self.sc.session_store
Loading

0 comments on commit ed0c95a

Please sign in to comment.