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    /// The compartment may be specified as either a name (or path for nested compartments) or as an id (OCID).
107    /// A name (vs id) can only be used when authenticated using a specific user identity. It is not available if
108    /// the associated handle authenticated as an Instance Principal (which can be done when calling the service from
109    /// a compute instance in the Oracle Cloud Infrastructure: see [`HandleBuilder::cloud_auth_from_instance()`](crate::HandleBuilder::cloud_auth_from_instance()).)
110    ///
111    /// If no compartment is given, the root compartment of the tenancy will be used.
112    pub fn compartment_id(mut self, compartment_id: &str) -> Self {
113        self.compartment_id = compartment_id.to_string();
114        self
115    }
116
117    /// Specify the start time for TableUsage records.
118    pub fn start_time(mut self, t: DateTime<FixedOffset>) -> TableUsageRequest {
119        self.start_time = Some(t);
120        self
121    }
122
123    /// Specify the end time for TableUsage records.
124    pub fn end_time(mut self, t: DateTime<FixedOffset>) -> TableUsageRequest {
125        self.end_time = Some(t);
126        self
127    }
128
129    /// Specify the limit of TableUsage records to return.
130    pub fn limit(mut self, l: i32) -> TableUsageRequest {
131        self.limit = l;
132        self
133    }
134
135    /// Specify the starting index of TableUsage records.
136    /// This is usually set by adding one to a previous [`TableUsageResult::last_index_returned()`] call.
137    /// The indexes start at zero.
138    pub fn start_index(mut self, i: i32) -> TableUsageRequest {
139        self.start_index = i;
140        self
141    }
142
143    /// Execute the request, returning a [`TableUsageResult`].
144    pub async fn execute(&self, h: &Handle) -> Result<TableUsageResult, NoSQLError> {
145        let mut w: Writer = Writer::new();
146        w.write_i16(h.inner.serial_version);
147        let timeout = h.get_timeout(&self.timeout);
148        self.nson_serialize(&mut w, &timeout);
149        let mut opts = SendOptions {
150            timeout: timeout,
151            retryable: true,
152            compartment_id: self.compartment_id.clone(),
153            ..Default::default()
154        };
155        let mut r = h.send_and_receive(w, &mut opts).await?;
156        let resp = TableUsageRequest::nson_deserialize(&mut r)?;
157        Ok(resp)
158    }
159
160    pub(crate) fn nson_serialize(&self, w: &mut Writer, timeout: &Duration) {
161        let mut ns = NsonSerializer::start_request(w);
162        ns.start_header();
163        ns.write_header(OpCode::GetTableUsage, timeout, &self.table_name);
164        ns.end_header();
165
166        // payload
167        ns.start_payload();
168        // TODO: currently only in http headers. Add to NSON?
169        //ns.write_string_field(COMPARTMENT_OCID, &self.compartment_id);
170        if let Some(sval) = self.start_time {
171            let s = sval.to_rfc3339();
172            ns.write_string_field(START, &s);
173        }
174        if let Some(eval) = self.end_time {
175            let s = eval.to_rfc3339();
176            ns.write_string_field(END, &s);
177        }
178        ns.write_nonzero_i32_field(LIST_MAX_TO_READ, self.limit);
179        ns.write_nonzero_i32_field(LIST_MAX_TO_READ, self.start_index);
180        ns.end_payload();
181
182        ns.end_request();
183    }
184
185    pub(crate) fn nson_deserialize(r: &mut Reader) -> Result<TableUsageResult, NoSQLError> {
186        let mut walker = MapWalker::new(r)?;
187        let mut res: TableUsageResult = Default::default();
188        while walker.has_next() {
189            walker.next()?;
190            let name = walker.current_name();
191            match name.as_str() {
192                ERROR_CODE => {
193                    walker.handle_error_code()?;
194                }
195                TABLE_NAME => {
196                    res.table_name = walker.read_nson_string()?;
197                }
198                LAST_INDEX => {
199                    res.last_index_returned = walker.read_nson_i32()?;
200                }
201                TABLE_USAGE => {
202                    // array of index info
203                    MapWalker::expect_type(walker.r, FieldType::Array)?;
204                    let _ = walker.r.read_i32()?; // skip array size in bytes
205                    let num_elements = walker.r.read_i32()?;
206                    res.usage_records = Vec::with_capacity(num_elements as usize);
207                    for _n in 1..=num_elements {
208                        res.usage_records
209                            .push(TableUsageRequest::read_usage_record(walker.r)?);
210                    }
211                    //println!(" usage_records={:?}", res.usage_records);
212                }
213                _ => {
214                    //println!("   table_usage_result: skipping field '{}'", name);
215                    walker.skip_nson_field()?;
216                }
217            }
218        }
219        Ok(res)
220    }
221
222    fn read_usage_record(r: &mut Reader) -> Result<TableUsage, NoSQLError> {
223        let mut walker = MapWalker::new(r)?;
224        let mut res: TableUsage = Default::default();
225        while walker.has_next() {
226            walker.next()?;
227            let name = walker.current_name();
228            match name.as_str() {
229                START => {
230                    let s = walker.read_nson_string()?;
231                    res.start_time = DateTime::parse_from_rfc3339(&s)?;
232                }
233                TABLE_USAGE_PERIOD => {
234                    // TODO: Duration
235                    res.seconds_in_period = walker.read_nson_i32()?;
236                }
237                READ_UNITS => {
238                    res.read_units = walker.read_nson_i32()?;
239                }
240                WRITE_UNITS => {
241                    res.write_units = walker.read_nson_i32()?;
242                }
243                STORAGE_GB => {
244                    res.storage_gb = walker.read_nson_i32()?;
245                }
246                READ_THROTTLE_COUNT => {
247                    res.read_throttle_count = walker.read_nson_i32()?;
248                }
249                WRITE_THROTTLE_COUNT => {
250                    res.write_throttle_count = walker.read_nson_i32()?;
251                }
252                STORAGE_THROTTLE_COUNT => {
253                    res.storage_throttle_count = walker.read_nson_i32()?;
254                }
255                MAX_SHARD_USAGE_PERCENT => {
256                    res.max_shard_usage_percent = walker.read_nson_i32()?;
257                }
258                _ => {
259                    //println!("   read_usage_record: skipping field '{}'", name);
260                    walker.skip_nson_field()?;
261                }
262            }
263        }
264        Ok(res)
265    }
266}
267
268impl NsonRequest for TableUsageRequest {
269    fn serialize(&self, w: &mut Writer, timeout: &Duration) {
270        self.nson_serialize(w, timeout);
271    }
272}