Skip to content

Commit cb73e3b

Browse files
committed
Added scale to unit for memory (#407)
## Description - added method for Memory to be scaled to a desired unit (required for ZooKeeper) Co-authored-by: Malte Sander <malte.sander.it@gmail.com>
1 parent dbfa6d4 commit cb73e3b

File tree

3 files changed

+136
-5
lines changed

3 files changed

+136
-5
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added
8+
9+
- `scale_to` and `to_java_heap_value` in `Memory` to scale units up or down ([#407]).
10+
11+
### Changed
12+
13+
- Visibility of `Memory` in `memory.rs` to private ([#407]).
14+
15+
[#407]: https://github.com/stackabletech/operator-rs/pull/407
16+
717
## [0.21.0] - 2022-05-16
818

919
### Changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ pub enum Error {
8282

8383
#[error("Cannot convert quantity [{value}] to Java heap.")]
8484
CannotConvertToJavaHeap { value: String },
85+
86+
#[error("Cannot convert quantity [{value}] to Java heap value with unit [{target_unit}].")]
87+
CannotConvertToJavaHeapValue { value: String, target_unit: String },
8588
}
8689

8790
pub type OperatorResult<T> = std::result::Result<T, Error>;

src/memory.rs

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ impl BinaryMultiple {
3434
BinaryMultiple::Exbi => "e".to_string(),
3535
}
3636
}
37+
38+
/// The exponential scale factor used when converting a `BinaryMultiple`
39+
/// to another one.
40+
fn exponential_scale_factor(&self) -> i32 {
41+
match self {
42+
BinaryMultiple::Kibi => 1,
43+
BinaryMultiple::Mebi => 2,
44+
BinaryMultiple::Gibi => 3,
45+
BinaryMultiple::Tebi => 4,
46+
BinaryMultiple::Pebi => 5,
47+
BinaryMultiple::Exbi => 6,
48+
}
49+
}
3750
}
3851

3952
impl FromStr for BinaryMultiple {
@@ -56,13 +69,13 @@ impl FromStr for BinaryMultiple {
5669

5770
/// Easily transform K8S memory resources to Java heap options.
5871
#[derive(Clone, Copy, Debug, PartialEq)]
59-
pub struct Memory {
72+
struct Memory {
6073
value: f32,
6174
unit: BinaryMultiple,
6275
}
6376

6477
/// Convert a (memory) [`Quantity`] to Java heap settings.
65-
/// Quantities are usually passed on to container resources whily Java heap
78+
/// Quantities are usually passed on to container resources while Java heap
6679
/// sizes need to be scaled accordingly.
6780
/// This implements a very simple heuristic to ensure that:
6881
/// - the quantity unit has been mapped to a java supported heap unit. Java only
@@ -84,11 +97,41 @@ pub fn to_java_heap(q: &Quantity, factor: f32) -> OperatorResult<String> {
8497
}
8598
}
8699

100+
/// Convert a (memory) [`Quantity`] to a raw Java heap value of the desired `target_unit`.
101+
/// Quantities are usually passed on to container resources while Java heap
102+
/// sizes need to be scaled accordingly.
103+
/// The raw heap value is converted to the specified `target_unit` (this conversion
104+
/// is done even if specified a unit greater that Gibibytes. It is not recommended to scale
105+
/// to anything bigger than Gibibytes.
106+
/// This implements a very simple heuristic to ensure that:
107+
/// - the quantity unit has been mapped to a java supported heap unit. Java only
108+
/// supports up to Gibibytes while K8S quantities can be expressed in Exbibytes.
109+
/// - the heap size has a non-zero value.
110+
/// Fails if it can't enforce the above restrictions.
111+
pub fn to_java_heap_value(
112+
q: &Quantity,
113+
factor: f32,
114+
target_unit: BinaryMultiple,
115+
) -> OperatorResult<u32> {
116+
let scaled = (q.0.parse::<Memory>()? * factor)
117+
.scale_for_java()
118+
.scale_to(target_unit);
119+
120+
if scaled.value < 1.0 {
121+
Err(Error::CannotConvertToJavaHeapValue {
122+
value: q.0.to_owned(),
123+
target_unit: format!("{:?}", target_unit),
124+
})
125+
} else {
126+
Ok(scaled.value as u32)
127+
}
128+
}
129+
87130
impl Memory {
88131
/// Scales the unit to a value supported by Java and may even scale
89132
/// further down, in an attempt to avoid having zero sizes or losing too
90133
/// much precision.
91-
pub fn scale_for_java(&self) -> Self {
134+
fn scale_for_java(&self) -> Self {
92135
let (norm_value, norm_unit) = match self.unit {
93136
BinaryMultiple::Kibi => (self.value, self.unit),
94137
BinaryMultiple::Mebi => (self.value, self.unit),
@@ -114,7 +157,22 @@ impl Memory {
114157
unit: scaled_unit,
115158
}
116159
}
160+
161+
/// Scale up or down to the desired `BinaryMultiple`. Returns a new `Memory` and does
162+
/// not change itself.
163+
fn scale_to(&self, binary_multiple: BinaryMultiple) -> Self {
164+
let from_exponent: i32 = self.unit.exponential_scale_factor();
165+
let to_exponent: i32 = binary_multiple.exponential_scale_factor();
166+
167+
let exponent_diff = from_exponent - to_exponent;
168+
169+
Memory {
170+
value: self.value * 1024f32.powi(exponent_diff),
171+
unit: binary_multiple,
172+
}
173+
}
117174
}
175+
118176
impl Mul<f32> for Memory {
119177
type Output = Memory;
120178

@@ -167,7 +225,7 @@ mod test {
167225
#[case("0.8Ti", Memory { value: 0.8f32, unit: BinaryMultiple::Tebi })]
168226
#[case("3.2Pi", Memory { value: 3.2f32, unit: BinaryMultiple::Pebi })]
169227
#[case("0.2Ei", Memory { value: 0.2f32, unit: BinaryMultiple::Exbi })]
170-
pub fn test_memory_parse(#[case] input: &str, #[case] output: Memory) {
228+
fn test_memory_parse(#[case] input: &str, #[case] output: Memory) {
171229
let got = input.parse::<Memory>().unwrap();
172230
assert_eq!(got, output);
173231
}
@@ -178,7 +236,67 @@ mod test {
178236
#[case("2Mi", 0.8, "-Xmx1638k")]
179237
#[case("1.5Gi", 0.8, "-Xmx1229m")]
180238
#[case("2Gi", 0.8, "-Xmx1638m")]
181-
pub fn test_memory_scale(#[case] q: &str, #[case] factor: f32, #[case] heap: &str) {
239+
pub fn test_to_java_heap(#[case] q: &str, #[case] factor: f32, #[case] heap: &str) {
182240
assert_eq!(heap, to_java_heap(&Quantity(q.to_owned()), factor).unwrap());
183241
}
242+
243+
#[rstest]
244+
#[case(2000f32, BinaryMultiple::Kibi, BinaryMultiple::Kibi, 2000f32)]
245+
#[case(2000f32, BinaryMultiple::Kibi, BinaryMultiple::Mebi, 2000f32/1024f32)]
246+
#[case(2000f32, BinaryMultiple::Kibi, BinaryMultiple::Gibi, 2000f32/1024f32/1024f32)]
247+
#[case(2000f32, BinaryMultiple::Kibi, BinaryMultiple::Tebi, 2000f32/1024f32/1024f32/1024f32)]
248+
#[case(2000f32, BinaryMultiple::Kibi, BinaryMultiple::Pebi, 2000f32/1024f32/1024f32/1024f32/1024f32)]
249+
#[case(2000f32, BinaryMultiple::Pebi, BinaryMultiple::Mebi, 2000f32*1024f32*1024f32*1024f32)]
250+
#[case(2000f32, BinaryMultiple::Pebi, BinaryMultiple::Kibi, 2000f32*1024f32*1024f32*1024f32*1024f32)]
251+
#[case(2000f32, BinaryMultiple::Exbi, BinaryMultiple::Pebi, 2000f32*1024f32)]
252+
pub fn test_scale_to(
253+
#[case] value: f32,
254+
#[case] unit: BinaryMultiple,
255+
#[case] target_unit: BinaryMultiple,
256+
#[case] expected: f32,
257+
) {
258+
let memory = Memory { value, unit };
259+
let scaled_memory = memory.scale_to(target_unit);
260+
assert_eq!(scaled_memory.value, expected);
261+
}
262+
263+
#[rstest]
264+
#[case("256Ki", 1.0, BinaryMultiple::Kibi, 256)]
265+
#[case("256Ki", 0.8, BinaryMultiple::Kibi, 204)]
266+
#[case("2Mi", 0.8, BinaryMultiple::Kibi, 1638)]
267+
#[case("1.5Gi", 0.8, BinaryMultiple::Mebi, 1228)]
268+
#[case("2Gi", 0.8, BinaryMultiple::Mebi, 1638)]
269+
#[case("2Ti", 0.8, BinaryMultiple::Mebi, 1677721)]
270+
#[case("2Ti", 0.8, BinaryMultiple::Gibi, 1638)]
271+
#[case("2Ti", 1.0, BinaryMultiple::Gibi, 2048)]
272+
#[case("2048Ki", 1.0, BinaryMultiple::Mebi, 2)]
273+
#[case("2000Ki", 1.0, BinaryMultiple::Mebi, 1)]
274+
#[case("4000Mi", 1.0, BinaryMultiple::Gibi, 3)]
275+
#[case("4000Mi", 0.8, BinaryMultiple::Gibi, 3)]
276+
pub fn test_to_java_heap_value(
277+
#[case] q: &str,
278+
#[case] factor: f32,
279+
#[case] target_unit: BinaryMultiple,
280+
#[case] heap: u32,
281+
) {
282+
assert_eq!(
283+
to_java_heap_value(&Quantity(q.to_owned()), factor, target_unit).unwrap(),
284+
heap
285+
);
286+
}
287+
288+
#[rstest]
289+
#[case("1000Ki", 0.8, BinaryMultiple::Gibi)]
290+
#[case("1000Ki", 0.8, BinaryMultiple::Mebi)]
291+
#[case("1000Mi", 0.8, BinaryMultiple::Gibi)]
292+
#[case("1000Mi", 1.0, BinaryMultiple::Gibi)]
293+
#[case("1023Mi", 1.0, BinaryMultiple::Gibi)]
294+
#[case("1024Mi", 0.8, BinaryMultiple::Gibi)]
295+
pub fn test_to_java_heap_value_failure(
296+
#[case] q: &str,
297+
#[case] factor: f32,
298+
#[case] target_unit: BinaryMultiple,
299+
) {
300+
assert!(to_java_heap_value(&Quantity(q.to_owned()), factor, target_unit).is_err());
301+
}
184302
}

0 commit comments

Comments
 (0)