Skip to content

Commit 287447b

Browse files
committed
refactor(puller): Create database driver from factory in poller thread
Use a readable database factory in the puller actor and create the concrete driver from the database poller thread, so database resources are initialized inside the actor process.
1 parent 39eb5d9 commit 287447b

3 files changed

Lines changed: 26 additions & 19 deletions

File tree

src/powerapi/puller/database_poller.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@
3636
from time import sleep
3737
from typing import TYPE_CHECKING
3838

39-
from powerapi.database import ConnectionFailed, ReadFailed
39+
from powerapi.database.exceptions import ConnectionFailed, ReadFailed
4040

4141
if TYPE_CHECKING:
42-
from powerapi.database import ReadableDatabase
42+
from powerapi.database.driver import ReadableDatabaseFactory
4343
from powerapi.filter import ReportFilter
4444

4545

@@ -49,16 +49,16 @@ class DatabasePollerThread(Thread):
4949
Fetches reports from a database and forward it to theirs corresponding dispatcher.
5050
"""
5151

52-
def __init__(self, database: ReadableDatabase, report_filter: ReportFilter, stream_mode: bool, poll_interval: float = 1.0):
52+
def __init__(self, database_factory: ReadableDatabaseFactory, report_filter: ReportFilter, stream_mode: bool, poll_interval: float = 1.0):
5353
"""
54-
:param database: Database to retrieve the reports from
54+
:param database_factory: Factory used to create the database driver
5555
:param report_filter: Report filter used to dispatch the received reports
5656
:param stream_mode: If true, poll continuously from the database; otherwise, stop the poller thread on empty result
5757
:param poll_interval: Interval in seconds between database polls
5858
"""
5959
super().__init__(name='database-poller-thread', daemon=True)
6060

61-
self.database = database
61+
self.database_factory = database_factory
6262
self.report_filter = report_filter
6363
self.stream_mode = stream_mode
6464
self.poll_interval = poll_interval
@@ -87,7 +87,11 @@ def run(self) -> None:
8787
Entrypoint of the database poller thread.
8888
"""
8989
try:
90-
self.database.connect()
90+
database = self.database_factory.create()
91+
database.connect()
92+
except ValueError as exn:
93+
logging.error('Failed to create the database driver: %s', exn)
94+
return
9195
except ConnectionFailed as exn:
9296
logging.error('Failed to connect the database driver: %s', exn.msg)
9397
return
@@ -100,7 +104,7 @@ def run(self) -> None:
100104

101105
while not self._stop_event.is_set():
102106
try:
103-
for report in self.database.read(self.stream_mode):
107+
for report in database.read(self.stream_mode):
104108
for dispatcher in self.report_filter.route(report):
105109
dispatcher.send_data(report)
106110

@@ -115,7 +119,7 @@ def run(self) -> None:
115119
for dispatcher in self.report_filter.dispatchers():
116120
dispatcher.disconnect()
117121

118-
self.database.disconnect()
122+
database.disconnect()
119123
logging.info('Database poller thread stopped')
120124

121125
def wait_ready(self, timeout: float | None = None) -> bool:

src/powerapi/puller/puller_actor.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
from powerapi.actor import Actor, State
3333
from powerapi.actor.message import StartMessage, PoisonPillMessage
34-
from powerapi.database import ReadableDatabase
34+
from powerapi.database.driver import ReadableDatabaseFactory
3535
from powerapi.filter import ReportFilter
3636
from powerapi.puller.database_poller import DatabasePollerThread
3737
from powerapi.puller.handlers import PullerStartMessageHandler, PullerPoisonPillMessageHandler
@@ -42,20 +42,20 @@ class PullerState(State):
4242
Puller Actor State class.
4343
"""
4444

45-
def __init__(self, actor: Actor, database: ReadableDatabase, report_filter: ReportFilter, stream_mode: bool):
45+
def __init__(self, actor: Actor, database_factory: ReadableDatabaseFactory, report_filter: ReportFilter, stream_mode: bool):
4646
"""
4747
:param actor: Puller actor instance
48-
:param database: Database driver
48+
:param database_factory: Factory used to create the database driver
4949
:param report_filter: Filter to use when dispatching reports
5050
:param stream_mode: If true, poll continuously from the database; otherwise, stop the poller thread on empty result
5151
"""
5252
super().__init__(actor)
5353

54-
self.database = database
54+
self.database_factory = database_factory
5555
self.report_filter = report_filter
5656
self.stream_mode = stream_mode
5757

58-
self.db_poller_thread = DatabasePollerThread(database, report_filter, stream_mode)
58+
self.db_poller_thread = DatabasePollerThread(database_factory, report_filter, stream_mode)
5959

6060

6161
class PullerActor(Actor):
@@ -64,24 +64,24 @@ class PullerActor(Actor):
6464
This actor allows to retrieve reports from a database and send them to theirs corresponding dispatcher.
6565
"""
6666

67-
def __init__(self, name: str, database: ReadableDatabase, report_filter: ReportFilter, stream_mode: bool = False, level_logger: int = logging.WARNING):
67+
def __init__(self, name: str, database_factory: ReadableDatabaseFactory, report_filter: ReportFilter, stream_mode: bool = False, level_logger: int = logging.WARNING):
6868
"""
6969
:param name: Name of the puller actor
70-
:param database: Database driver to use to persist reports
70+
:param database_factory: Factory used to create the database driver
7171
:param report_filter: Filter to use when dispatching reports
7272
:param level_logger: Define the level of the logger for the actor
7373
"""
7474
super().__init__(name, level_logger, 1000)
7575

76-
self.database = database
76+
self.database_factory = database_factory
7777
self.report_filter = report_filter
7878
self.stream_mode = stream_mode
7979

8080
def setup(self) -> None:
8181
"""
8282
Set up the Puller actor message handlers.
8383
"""
84-
self.state = PullerState(self, self.database, self.report_filter, self.stream_mode)
84+
self.state = PullerState(self, self.database_factory, self.report_filter, self.stream_mode)
8585

8686
self.add_handler(StartMessage, PullerStartMessageHandler(self.state))
8787
self.add_handler(PoisonPillMessage, PullerPoisonPillMessageHandler(self.state))

tests/unit/puller/conftest.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from powerapi.filter import BroadcastReportFilter
3737
from powerapi.puller.handlers import PullerStartMessageHandler, PullerPoisonPillMessageHandler
3838
from powerapi.puller.puller_actor import PullerState
39+
from tests.utils.db import PrebuiltDatabaseFactory
3940

4041
if TYPE_CHECKING:
4142
from powerapi.filter import ReportFilter
@@ -88,8 +89,9 @@ def puller_start_handler(make_fake_failing_database, fake_database_poller):
8889
def _create_handler(report_filter: ReportFilter) -> PullerStartMessageHandler:
8990
actor = Mock()
9091
database = make_fake_failing_database()
92+
database_factory = PrebuiltDatabaseFactory(database)
9193

92-
state = PullerState(actor, database, report_filter, stream_mode=False)
94+
state = PullerState(actor, database_factory, report_filter, stream_mode=False)
9395
state.db_poller_thread = fake_database_poller
9496

9597
handler = PullerStartMessageHandler(state)
@@ -108,8 +110,9 @@ def _create_handler() -> PullerPoisonPillMessageHandler:
108110
actor.socket_interface.receive.return_value = None # Prevents an infinite loop when triggering a graceful shutdown.
109111

110112
database = make_fake_failing_database()
113+
database_factory = PrebuiltDatabaseFactory(database)
111114

112-
state = PullerState(actor, database, empty_report_filter, stream_mode=False)
115+
state = PullerState(actor, database_factory, empty_report_filter, stream_mode=False)
113116
state.db_poller_thread = fake_database_poller
114117

115118
handler = PullerPoisonPillMessageHandler(state)

0 commit comments

Comments
 (0)