The Serverless Sensor Database aims to simplify sensor management by eliminating the need for separate systemd services, as the approach taken by the OpenBMC D-Bus sensor daemon today. This solution removes the daemon layer and inter-process communication (IPC), resulting in a more straightforward implementation.
The sensor database exposes three main APIs:
add_sensor(): Adds (or removes) a sensor to the database, which will then poll the sensor hardware for value updates.query_sensor(): Queries sensors by secondary key (one of sensor properties, such as the FRU it belongs to) or by primary key: sensor name.subscribe(): Allows subscribers to receive sensor events when sensor readings are updated or cross a threshold.Clients must provide a method to poll the sensor value when adding a sensor to the database, as defined by the asynchronous trait AsyncReadSensor. For I2C sensors, this can be a simple wrapper to read the I2C device via the sysfs interface. This interface abstraction facilitates integration with third-party vendors.
The src/main.rs file demonstrates a complete use case from a client's perspective.
Typically, one client handles the database creation, but there is currently no on how many clients can query and subscribe simultaneously.
Adding sensors
Sensors can be added to the sensor database, and the update task will run in the background automatically:
// Mock implementation of AsyncReadSensor struct MockSensorReader { pub is_polling: std::sync::atomic::AtomicBool, } #[async_trait::async_trait] impl AsyncReadSensor for MockSensorReader { async fn read_value( &self, sampling_interval: Duration, reporting_interval: Duration, ) -> Result<Vec<(f64, SystemTime)>, std::io::Error> { tokio::time::sleep(sampling_interval).await; let now = SystemTime::now(); let mut values = Vec::new(); let mut current_time = now; while current_time - now < reporting_interval { values.push((rand::random::<f64>() * 100.0, current_time)); current_time += sampling_interval; } Ok(values) } fn start_polling(&self) { self.is_polling.store(true, Ordering::Relaxed); } fn stop_polling(&self) { self.is_polling.store(false, Ordering::Relaxed); } } // The sensor database can be cloned and shared by other thread, task freely let sensor_database = Arc::new(SensorDatabase::default()); let mock_reader = Arc::new(MockSensorReader); // Add sensors to the database // A sensor can have arbitrary number of property as key/value pair // The property key names are not restricted, though it should follow the Redfish data scheme let sensor1 = Sensor::new( "Sensor1".to_string(), Some(vec![ ("FRU".to_string(), "CPU".to_string()), ("ReadingType".to_string(), "Temperature".to_string()), ]), 25.5, mock_reader.clone(), Duration::from_secs(1), Duration::from_millis(100), Duration::from_secs(5), ); sensor_database.add_sensor(sensor1).await; let sensor2 = Sensor::new( "Sensor2".to_string(), Some(vec![ ("FRU".to_string(), "GPU".to_string()), ("ReadingType".to_string(), "Temperature".to_string()), ]), 28.2, mock_reader.clone(), Duration::from_secs(2), Duration::from_millis(200), Duration::from_secs(5), ); sensor_database.add_sensor(sensor2).await; let sensor3 = Sensor::new( "Sensor3".to_string(), Some(vec![ ("FRU".to_string(), "CPU".to_string()), ("ReadingType".to_string(), "Voltage".to_string()), ]), 3.3, mock_reader.clone(), Duration::from_secs(3), Duration::from_millis(300), Duration::from_secs(5), ); sensor_database.add_sensor(sensor3).await;
Query sensors
2.1 By primary key:
// Query sensor by primary keys: all sensors if let Ok(sensors) = sensor_database.query_sensors_by_names(&[]) { println!("All sensors in database:"); for sensor in sensors { println!("{:?}", sensor); } } // Query sensor by primary keys: one sensor if let Ok(sensors) = sensor_database.query_sensors_by_names(&["Sensor1"]) { assert!(sensors.len() <= 1); }
2.2 By secondary keys:
// Query all availabe secondary keys in the database let keys = sensor_database.query_all_secondary_keys(); println!("secondary keys: {:?}", keys); // Query sensors by secondary key if let Ok(sensors) = sensor_database.query_sensors_by_property(("FRU", "CPU")) { for sensor in sensors { println!("Sensor with FRU CPU: {:?}", sensor); } }
Subscribe to sensor events
3.1 Query sensors by reading type, then subscribe sensor event from all of them
if let Ok(temperature_sensors) = sensor_database.query_sensors_by_property(("ReadingType", "Temperature")) { for sensor in temperature_sensors { println!("Subscribe to temperature sensor: {:?}", sensor); if let Some(mut subscription) = sensor_database.subscribe_events(&sensor.name, SubscriptionType::OnChange) { tokio::spawn(async move { while let Some(events) = subscription.event_receiver.recv().await { println!("Received {:?}", event); } }); } } }
3.2 Subscribe to Periodical events, it will increase the polling rate of this sensor in this case
if let Some(mut subscription) = sensor_database.subscribe_events( "Sensor2", SubscriptionType::Periodical(Duration::from_millis(500)), ) { let sensor_database_clone = sensor_database.clone(); if let Ok(sensors) = sensor_database_clone.query_sensors_by_names(&["Sensor2"]) { println!("Sensor2 before subscription dropped: {:?}", sensors[0]); } tokio::spawn(async move { let timeout = tokio::time::sleep(Duration::from_secs(15)); tokio::pin!(timeout); loop { tokio::select! { _ = &mut timeout => { drop(subscription); println!("Subscription to Sensor2 dropped after 15 seconds"); break; }, events = subscription.event_receiver.recv() => { if let Some(events) = events { println!("Periodical event: {:?}", event); } else { break; } } } } // After this subscription is dropped, polling rate should restore to default if let Ok(sensors) = sensor_database_clone.query_sensors_by_names(&["Sensor2"]) { println!("Sensor2 after subscription dropped: {:?}", sensors[0]); } }); }
All sensors in database:
Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Sensor { name: "Sensor3", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Voltage")]), value: 3.3, default_update_interval: 3s, minimum_update_interval: 300ms, current_update_interval: 3s }
Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
secondary keys: ["ReadingType", "FRU"]
Sensor with FRU CPU: Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
Sensor with FRU CPU: Sensor { name: "Sensor3", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Voltage")]), value: 3.3, default_update_interval: 3s, minimum_update_interval: 300ms, current_update_interval: 3s }
Subscribe to temperature sensor: Sensor { name: "Sensor1", secondary_keys: Some([("FRU", "CPU"), ("ReadingType", "Temperature")]), value: 25.5, default_update_interval: 1s, minimum_update_interval: 100ms, current_update_interval: 1s }
Subscribe to temperature sensor: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Sensor2 before subscription dropped: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 28.2, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 500ms }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 66.81906494537024, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949746575 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 21.584232735733732, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949765075 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 21.584232735733732, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949765075 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 69.1666621987969, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949819185 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 27.17841306446591, timestamp: SystemTime { tv_sec: 616376, tv_nsec: 949907735 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 84.47258342422754, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 450807398 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 84.47258342422754, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 450807398 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 52.19185780053795, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 949638010 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 41.0184389080178, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 950859630 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 41.0184389080178, timestamp: SystemTime { tv_sec: 616377, tv_nsec: 950859630 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.663082880221953, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 450710813 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 19.332324110778732, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 949541614 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.96464437129602, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 950754015 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.96464437129602, timestamp: SystemTime { tv_sec: 616378, tv_nsec: 950754015 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 60.130064755036315, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 450635678 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 70.11134209796973, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 949471390 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 69.2936468310648, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 949516641 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 23.670158981953605, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 950686761 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 23.670158981953605, timestamp: SystemTime { tv_sec: 616379, tv_nsec: 950686761 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 35.64272633290464, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 450565693 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 79.04166544362107, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 949381665 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 78.73320899792597, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 950587055 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 78.73320899792597, timestamp: SystemTime { tv_sec: 616380, tv_nsec: 950587055 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 83.97403520782987, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 450610554 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 83.97403520782987, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 450610554 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 12.907887112545191, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 949571346 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.49627930286773, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 950822646 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.49627930286773, timestamp: SystemTime { tv_sec: 616381, tv_nsec: 950822646 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.20421975311514, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 450770329 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 49.60535847359011, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949697881 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 70.12447978660987, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949761681 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 37.2271559676151, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949913131 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 37.2271559676151, timestamp: SystemTime { tv_sec: 616382, tv_nsec: 949913131 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.12690188343063, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 450912084 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 96.12690188343063, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 450912084 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 46.05559067326017, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 949793666 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 58.6062459835362, timestamp: SystemTime { tv_sec: 616383, tv_nsec: 949945386 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 90.1068155038148, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 450840449 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 90.1068155038148, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 450840449 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 97.66549110020112, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 949781520 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.749746695592933, timestamp: SystemTime { tv_sec: 616384, tv_nsec: 949904521 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.59713397398946, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 450419022 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 98.59713397398946, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 450419022 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 39.108196890669, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 949361553 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 44.311761498755544, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 949460443 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 54.214033390729, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 950586254 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 54.214033390729, timestamp: SystemTime { tv_sec: 616385, tv_nsec: 950586254 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 63.10464395868666, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 450337215 } }
Received SensorEvent { sensor_name: "Sensor1", event_type: "ValueChanged", value: 12.46727393215803, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 949295857 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 74.64159774182104, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 950569378 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 74.64159774182104, timestamp: SystemTime { tv_sec: 616386, tv_nsec: 950569378 } }
Sensor1 removed
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 67.72627000633484, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 450524749 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.61407360597495, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 950665052 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 10.61407360597495, timestamp: SystemTime { tv_sec: 616387, tv_nsec: 950665052 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 73.03188841980563, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 450580235 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 45.10623518566299, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 949513705 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 75.90624780730381, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 950773136 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 75.90624780730381, timestamp: SystemTime { tv_sec: 616388, tv_nsec: 950773136 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 36.24425676700527, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 450797448 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 36.24425676700527, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 450797448 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 35.18804590845163, timestamp: SystemTime { tv_sec: 616389, tv_nsec: 950786450 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 50.87769030250661, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 450759950 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 50.87769030250661, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 450759950 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 73.2571597068527, timestamp: SystemTime { tv_sec: 616390, tv_nsec: 950706282 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 26.875453446612042, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 450640964 } }
Periodical event: SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 26.875453446612042, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 450640964 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 96.91900929324757, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 949590695 } }
Subscription to Sensor2 dropped after 15 seconds
Sensor2 after subscription dropped: Sensor { name: "Sensor2", secondary_keys: Some([("FRU", "GPU"), ("ReadingType", "Temperature")]), value: 26.875453446612042, default_update_interval: 2s, minimum_update_interval: 200ms, current_update_interval: 2s }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 28.443838708091974, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 950837846 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 87.3475020265932, timestamp: SystemTime { tv_sec: 616391, tv_nsec: 952096976 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 87.54493122423006, timestamp: SystemTime { tv_sec: 616393, tv_nsec: 952624054 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 41.209779406863646, timestamp: SystemTime { tv_sec: 616394, tv_nsec: 949069079 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 11.548348109805461, timestamp: SystemTime { tv_sec: 616395, tv_nsec: 952321215 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 70.84722931073456, timestamp: SystemTime { tv_sec: 616397, tv_nsec: 949857082 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 51.72073433375517, timestamp: SystemTime { tv_sec: 616397, tv_nsec: 952071873 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 70.96642522743821, timestamp: SystemTime { tv_sec: 616399, tv_nsec: 952442080 } }
Threshold event: SensorEvent { sensor_name: "Sensor3", event_type: "ReadingAboveUpperCriticalThreshold", value: 91.40068010103276, timestamp: SystemTime { tv_sec: 616400, tv_nsec: 949875114 } }
Received SensorEvent { sensor_name: "Sensor2", event_type: "ValueChanged", value: 53.65360977964993, timestamp: SystemTime { tv_sec: 616401, tv_nsec: 952438499 } }
^CExiting...