Skip to main content

bacnet_server/server/
audit_log_purge.rs

1//! The application's purge of an Audit Log (#1238).
2//!
3//! No peer can purge an Audit Log (Clause 12.64.11 keeps its Record_Count
4//! read-only), so the server offers the purge to the application. It is
5//! staged like a durable write ([`super::durable_writes`]): the commit runs
6//! with the database guard dropped, and the log serves the purged state only
7//! once storage holds it.
8
9use super::*;
10
11impl<T: TransportPort + 'static> BACnetServer<T> {
12    /// Purge an Audit Log: clear its records and append a BUFFER_PURGED
13    /// status record, as [`AuditLogObject::purge`] describes.
14    ///
15    /// Peers have no way to do this, so it is the application's to call.
16    /// The log builds the purged state and queues its commit; the server
17    /// waits for the commit with the object database guard dropped, so other
18    /// requests carry on, and the log serves the purged state only once it is
19    /// durable. Changes to one log land one at a time, in the order they
20    /// were staged: a notification batch whose commit is running lands first
21    /// and is purged with the rest, and one that arrives while the purge
22    /// commits waits for it and follows the purge record.
23    ///
24    /// # Errors
25    ///
26    /// - OBJECT / UNKNOWN_OBJECT when `oid` names no object, and OBJECT /
27    ///   OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED when the object cannot be
28    ///   purged (it is not a built-in Audit Log).
29    /// - DEVICE / OPERATIONAL_PROBLEM without a valid local clock, or when
30    ///   the commit fails; the log's writer logs the storage error.
31    /// - The stopped-server error once [`stop`](Self::stop) has run.
32    ///
33    /// On any error the log is left as it was. A purge whose future is
34    /// dropped before it finishes is handled as an abandoned write request
35    /// is: the log keeps serving its state, drops the staged purge once it
36    /// has waited long enough, and sets storage back to that state.
37    ///
38    /// [`AuditLogObject::purge`]: bacnet_objects::audit::AuditLogObject::purge
39    pub async fn purge_audit_log(&self, oid: &ObjectIdentifier) -> Result<(), Error> {
40        self.active_network()?;
41        let staged =
42            durable_writes::stage(&self.db, durable_writes::DurableTarget::purge(*oid)).await;
43        let mut db = self.db.write().await;
44        let result = match db.get_mut(oid) {
45            None => Err(protocol_error(
46                ErrorClass::OBJECT,
47                ErrorCode::UNKNOWN_OBJECT,
48            )),
49            Some(object) => match object.durable_writes_internal() {
50                Some(writes) => writes.commit_purge(),
51                None => Err(protocol_error(
52                    ErrorClass::OBJECT,
53                    ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
54                )),
55            },
56        };
57        staged.release(&mut db);
58        result
59    }
60}
61
62fn protocol_error(class: ErrorClass, code: ErrorCode) -> Error {
63    Error::protocol(class, code)
64}
65
66#[cfg(test)]
67#[path = "audit_log_purge_tests.rs"]
68mod tests;