oracle_nosql_rust_sdk/
table_usage_request.rs

1//
2// Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
3//
4// Licensed under the Universal Permissive License v 1.0 as shown at
5//  https://oss.oracle.com/licenses/upl/
6//
7use crate::error::NoSQLError;
8use crate::handle::Handle;
9use crate::handle::SendOptions;
10use crate::nson::*;
11use crate::reader::Reader;
12use crate::types::{FieldType, OpCode};
13use crate::writer::Writer;
14use chrono::{DateTime, FixedOffset};
15use std::result::Result;
16use std::time::Duration;
17
18/// Struct used for querying the usage of a NoSQL Database table.
19#[derive(Default, Debug)]
20pub struct TableUsageRequest {
21    pub(crate) table_name: String,
22    pub(crate) compartment_id: String,
23    pub(crate) start_time: Option<DateTime<FixedOffset>>,
24    pub(crate) end_time: Option<DateTime<FixedOffset>>,
25    pub(crate) limit: i32,
26    pub(crate) start_index: i32,
27    pub(crate) timeout: Option<Duration>,
28}
29
30/// A single time slice usage record for a specific table.
31///
32/// This contains information about read and write throughput consumed during that period
33/// as well as the current information regarding storage capacity. In
34/// addition the count of throttling exceptions for the period is reported.
35#[derive(Default, Debug)]
36pub struct TableUsage {
37    pub start_time: DateTime<FixedOffset>,
38    pub seconds_in_period: i32,
39    pub read_units: i32,
40    pub write_units: i32,
41    pub storage_gb: i32,
42    pub read_throttle_count: i32,
43    pub write_throttle_count: i32,
44    pub storage_throttle_count: i32,
45    pub max_shard_usage_percent: i32,
46    // One private field so we don't break SemVer if we add more public fields
47    #[allow(dead_code)]
48    singluar_private: bool,
49}
50
51/// Struct representing the result of a [`TableUsageRequest`] operation.
52#[derive(Default, Debug)]
53pub struct TableUsageResult {
54    pub(crate) table_name: String,
55    pub(crate) usage_records: Vec<TableUsage>,
56    pub(crate) last_index_returned: i32,
57}
58
59/// Struct representing the result of a TableUsageRequest execution.
60impl TableUsageResult {
61    /// Get the table name.
62    pub fn table_name(&self) -> String {
63        self.table_name.clone()
64    }
65    /// Get a reference to a vector of usage records.
66    pub fn usage_records(&self) -> &Vec<TableUsage> {
67        &self.usage_records
68    }
69    /// Take the Result's vector of usage records, leaving the Result with
70    /// a new empty vector.
71    pub fn take_usage_records(&mut self) -> Vec<TableUsage> {
72        std::mem::take(&mut self.usage_records)
73    }
74    /// Get the last index returned.
75    ///
76    /// This is typically used when specifying a `limit` on the usage request. If this value
77    /// is greater than the `start_index` plus `limit`, there are more records to retreive. In the
78    /// next request, set the `start_index` to this value plus one.
79    pub fn last_index_returned(&self) -> i32 {
80        self.last_index_returned
81    }
82}
83
84impl TableUsageRequest {
85    /// Create a new TableUsageRequest.
86    pub fn new(table_name: &str) -> TableUsageRequest {
87        TableUsageRequest {
88            table_name: table_name.to_string(),
89            ..Default::default()
90        }
91    }
92
93    /// Specify the timeout value for the request.
94    ///
95    /// This is optional.
96    /// If set, it must be greater than or equal to 1 millisecond, otherwise an
97    /// IllegalArgument error will be returned.
98    /// If not set, the default timeout value configured for the [`Handle`](crate::HandleBuilder::timeout()) is used.
99    pub fn timeout(mut self, t: &Duration) -> Self {
100        self.timeout = Some(t.clone());
101        self
102    }
103
104    /// Cloud Service only: set the name or id of a compartment to be used for this operation.
105    ///
106    /// If the associated handle authenticated as an Instance Principal, this value must be an OCID.
107    /// In all other cases, the value may be specified as either a name (or path for nested compartments) or as an OCID.
108    ///
109    /// If no compartment is given, the default compartment id for the handle is used. If that value was
110    /// not specified, the root compartment of the tenancy will be used.
111    pub fn compartment_id(mut self, compartment_id: &str) -> Self {
112        self.compartment_id = compartment_id.to_string();
113        self
114    }
115
116    /// Specify the start time for TableUsage records.
117    pub fn start_time(mut self, t: DateTime<FixedOffset>) -> TableUsageRequest {
118        self.start_time = Some(t);
119        self
120    }
121
122    /// Specify the end time for TableUsage records.
123    pub fn end_time(mut self, t: DateTime<FixedOffset>) -> TableUsageRequest {
124        self.end_time = Some(t);
125        self
126    }
127
128    /// Specify the limit of TableUsage records to return.
129    pub fn limit(mut self, l: i32) -> TableUsageRequest {
130        self.limit = l;
131        self
132    }
133
134    /// Specify the starting index of TableUsage records.
135    /// This is usually set by adding one to a previous [`TableUsageResult::last_index_returned()`] call.
136    /// The indexes start at zero.
137    pub fn start_index(mut self, i: i32) -> TableUsageRequest {
138        self.start_index = i;
139        self
140    }
141
142    /// Execute the request, returning a [`TableUsageResult`].
143    pub async fn execute(&self, h: &Handle) -> Result<TableUsageResult, NoSQLError> {
144        let mut w: Writer = Writer::new();
145        w.write_i16(h.inner.serial_version);
146        let timeout = h.get_timeout(&self.timeout);
147        self.nson_serialize(&mut w, &timeout);
148        let mut opts = SendOptions {
149            timeout: timeout,
150            retryable: true,
151            compartment_id: self.compartment_id.clone(),
152            ..Default::default()
153        };
154        let mut r = h.send_and_receive(w, &mut opts).await?;
155        let resp = TableUsageRequest::nson_deserialize(&mut r)?;
156        Ok(resp)
157    }
158
159    pub(crate) fn nson_serialize(&self, w: &mut Writer, timeout: &Duration) {
160        let mut ns = NsonSerializer::start_request(w);
161        ns.start_header();
162        ns.write_header(OpCode::GetTableUsage, timeout, &self.table_name);
163        ns.end_header();
164
165        // payload
166        ns.start_payload();
167        // TODO: currently only in http headers. Add to NSON?
168        //ns.write_string_field(COMPARTMENT_OCID, &self.compartment_id);
169        if let Some(sval) = self.start_time {
170            let s = sval.to_rfc3339();
171            ns.write_string_field(START, &s);
172        }
173        if let Some(eval) = self.end_time {
174            let s = eval.to_rfc3339();
175            ns.write_string_field(END, &s);
176        }
177        ns.write_nonzero_i32_field(LIST_MAX_TO_READ, self.limit);
178        ns.write_nonzero_i32_field(LIST_MAX_TO_READ, self.start_index);
179        ns.end_payload();
180
181        ns.end_request();
182    }
183
184    pub(crate) fn nson_deserialize(r: &mut Reader) -> Result<TableUsageResult, NoSQLError> {
185        let mut walker = MapWalker::new(r)?;
186        let mut res: TableUsageResult = Default::default();
187        while walker.has_next() {
188            walker.next()?;
189            let name = walker.current_name();
190            match name.as_str() {
191                ERROR_CODE => {
192                    walker.handle_error_code()?;
193                }
194                TABLE_NAME => {
195                    res.table_name = walker.read_nson_string()?;
196                }
197                LAST_INDEX => {
198                    res.last_index_returned = walker.read_nson_i32()?;
199                }
200                TABLE_USAGE => {
201                    // array of index info
202                    MapWalker::expect_type(walker.r, FieldType::Array)?;
203                    let _ = walker.r.read_i32()?; // skip array size in bytes
204                    let num_elements = walker.r.read_i32()?;
205                    res.usage_records = Vec::with_capacity(num_elements as usize);
206                    for _n in 1..=num_elements {
207                        res.usage_records
208                            .push(TableUsageRequest::read_usage_record(walker.r)?);
209                    }
210                    //println!(" usage_records={:?}", res.usage_records);
211                }
212                _ => {
213                    //println!("   table_usage_result: skipping field '{}'", name);
214                    walker.skip_nson_field()?;
215                }
216            }
217        }
218        Ok(res)
219    }
220
221    fn read_usage_record(r: &mut Reader) -> Result<TableUsage, NoSQLError> {
222        let mut walker = MapWalker::new(r)?;
223        let mut res: TableUsage = Default::default();
224        while walker.has_next() {
225            walker.next()?;
226            let name = walker.current_name();
227            match name.as_str() {
228                START => {
229                    let s = walker.read_nson_string()?;
230                    res.start_time = DateTime::parse_from_rfc3339(&s)?;
231                }
232                TABLE_USAGE_PERIOD => {
233                    // TODO: Duration
234                    res.seconds_in_period = walker.read_nson_i32()?;
235                }
236                READ_UNITS => {
237                    res.read_units = walker.read_nson_i32()?;
238                }
239                WRITE_UNITS => {
240                    res.write_units = walker.read_nson_i32()?;
241                }
242                STORAGE_GB => {
243                    res.storage_gb = walker.read_nson_i32()?;
244                }
245                READ_THROTTLE_COUNT => {
246                    res.read_throttle_count = walker.read_nson_i32()?;
247                }
248                WRITE_THROTTLE_COUNT => {
249                    res.write_throttle_count = walker.read_nson_i32()?;
250                }
251                STORAGE_THROTTLE_COUNT => {
252                    res.storage_throttle_count = walker.read_nson_i32()?;
253                }
254                MAX_SHARD_USAGE_PERCENT => {
255                    res.max_shard_usage_percent = walker.read_nson_i32()?;
256                }
257                _ => {
258                    //println!("   read_usage_record: skipping field '{}'", name);
259                    walker.skip_nson_field()?;
260                }
261            }
262        }
263        Ok(res)
264    }
265}
266
267impl NsonRequest for TableUsageRequest {
268    fn serialize(&self, w: &mut Writer, timeout: &Duration) {
269        self.nson_serialize(w, timeout);
270    }
271}