file_monitor_analysis.md - isndev/qb GitHub Wiki
Case Study: Building a File System Monitor with QB Actors
- Location:
example/core_io/file_monitor/
- Objective: This example demonstrates how to build a real-time file system monitoring application using QB actors. It showcases how to leverage
qb-io
's asynchronous file watching capabilities to detect changes (creations, modifications, deletions) in specified directories and process these events through a pipeline of actors.
By examining this example, you will learn about:
- Integrating asynchronous file/directory watching (
qb::io::async::directory_watcher
) into an actor. - Designing an event-driven workflow triggered by file system events.
- Separating concerns: watching, processing, and client interaction into distinct actors.
- Using custom events for robust inter-actor communication.
- Employing
qb::io::async::callback
for simulating external interactions or periodic tasks within an actor.
Architectural Overview: A Reactive System
The file_monitor
example is structured around three main actor types:
DirectoryWatcher
Actor
1. - Headers:
watcher.h
,watcher.cpp
- Core Assignment (Typical): Core 0 (often suitable for I/O-bound tasks like file watching).
- Role: The primary component responsible for monitoring specified directory paths for any changes to files or subdirectories.
- QB Integration & Internal Mechanism:
- Inherits from
qb::Actor
. - It internally manages one or more instances of a helper class,
DirectoryMonitor
(also defined inwatcher.h/.cpp
). - Each
DirectoryMonitor
instance inherits fromqb::io::async::directory_watcher<DirectoryMonitor>
, which is a CRTP base class fromqb-io
. This base class useslibev
'sev_stat
watchers to poll for attribute changes on the specified path.
- Inherits from
- Request Handling:
on(WatchDirectoryRequest&)
: When this event is received (typically fromClientActor
), theDirectoryWatcher
initiates monitoring for the requested path. It stores therequestor
'sActorId
as a subscriber for events from this path. If recursive watching is requested, it will also set up watchers for subdirectories.on(UnwatchDirectoryRequest&)
: Stops monitoring a previously watched path for a given subscriber.
- Event Detection & Propagation:
- The
DirectoryMonitor::on(qb::io::async::event::file const& os_event)
method is invoked by theqb-io
event loop (listener::current
) when the OS (viaev_stat
) detects a change in a monitored file or directory's attributes (e.g., size, modification time, link count). - This handler analyzes
os_event.attr
(current stat data) andos_event.prev
(previous stat data) to determine the nature of the change, categorizing it into aFileEventType
(e.g.,CREATED
,MODIFIED
,DELETED
,ATTRIBUTES_CHANGED
). - The
DirectoryWatcher
then calls itspublishFileEvent
method. This method identifies all actors subscribed to the affected path andpush
es an application-levelFileEvent
(containing the path andFileEventType
) to each of them.
- The
- Recursion & State: Manages a tree-like structure (
WatchInfo
) to handle recursive directory watching and track subscribers per path.
FileProcessor
Actor
2. - Headers:
processor.h
,processor.cpp
- Core Assignment (Typical): Core 1 (separating processing logic from I/O watching).
- Role: Receives
FileEvent
notifications from theDirectoryWatcher
and performs application-specific actions based on these events. - QB Integration: Standard
qb::Actor
. - State Management: Maintains a
_tracked_files
map (std::map<std::string, FileMetadata>
) to store information about files it's aware of, such as path, size, content hash, and last modification time. - **Event Handling (
on(FileEvent&)
):- This is the primary entry point for this actor.
- Based on
event.event_type
, it dispatches to internal methods likeprocessFileCreated
,processFileModified
, orprocessFileDeleted
.
- Processing Logic:
processFileCreated
: Adds new file metadata to_tracked_files
, potentially calculates an initial hash.processFileModified
: Updates metadata for an existing file, recalculates hash if content likely changed (e.g., size or mtime differs).processFileDeleted
: Removes the file from_tracked_files
.extractMetadata()
: A helper that usesqb::io::sys::file
for synchronous read operations to get file size and potentiallyqb::crypto::md5
(or another hash) for content hashing. Note: If hashing large files, this synchronous read could block. In a production system, this might be offloaded to anasync::callback
or a dedicated hashing worker actor if performance is critical.- Logs detected changes to the console.
- Configuration & Stats: Also handles
SetProcessingConfigRequest
(e.g., to toggle processing of hidden files) andGetProcessingStatsRequest
(replying withMonitoringStats
).
ClientActor
(Simulation & Interaction)
3. - Header:
main.cpp
(defined within) - Core Assignment (Typical): Core 0 (can co-exist with
DirectoryWatcher
for this example). - Role: Simulates a client application that uses the file monitoring service. It also actively creates, modifies, and deletes files in a test directory to generate file system events for the
DirectoryWatcher
to pick up. - QB Integration: Standard
qb::Actor
. - Interaction Flow:
onInit()
: Sends aWatchDirectoryRequest
to theDirectoryWatcher
to start monitoring a specified_test_directory
.on(WatchDirectoryResponse&)
: Receives confirmation that watching has started. It then begins its file manipulation tests.- File Operations (via
qb::io::async::callback
): Usesqb::io::async::callback
to schedule a sequence of file operations (createTestFile
,modifyTestFile
,deleteTestFile
). These methods usestd::filesystem
(orqb::io::sys::file
for content) to perform actual file system modifications. Usingasync::callback
ensures these potentially blocking OS calls don't stall theClientActor
'sVirtualCore
. on(FileEvent&)
: ReceivesFileEvent
notifications from theDirectoryWatcher
in response to its own file operations (and any other changes) and logs them.- Shutdown: Manages a test duration. After the duration, it
broadcast<qb::KillEvent>()
to stop all actors and sends anUnwatchDirectoryRequest
in its ownon(qb::KillEvent&)
handler.
Key QB Concepts Illustrated
- Asynchronous File System Watching: Demonstrates the practical application of
qb::io::async::directory_watcher
(via theDirectoryMonitor
helper) integrated within an actor (DirectoryWatcher
) to react to OS-level file system notifications without blocking. - Event-Driven Pipeline: Shows a clear flow: OS file system event ->
qb::io::async::event::file
->DirectoryMonitor
->DirectoryWatcher
(transforms toFileEvent
) ->FileProcessor
. - Separation of Concerns: Responsibilities are well-defined:
DirectoryWatcher
: Low-level OS interaction and event detection/filtering.FileProcessor
: Application-specific logic based on file events.ClientActor
: User interaction, simulation, and system orchestration for the demo.
- Custom Event Types: Effective use of application-specific events (
WatchDirectoryRequest
,FileEvent
,SetProcessingConfigRequest
, etc.) for clear and type-safe communication between actors. - Asynchronous Task Execution in Actors: The
ClientActor
usesqb::io::async::callback
to perform potentially blocking file creation/modification/deletion operations off its main event processing path, maintaining responsiveness. - Resource Management (Watchers): The
DirectoryWatcher
is responsible for creating (startWatching
) and cleaning up (stopWatching
) the underlyingDirectoryMonitor
instances, which in turn manage theev_stat
watchers.
This example provides a solid blueprint for building applications that need to react to file system changes in a robust and asynchronous manner using the QB Actor Framework.
(Next Example Analysis: file_processor Example Analysis**)