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;