Skip to content

Commit 303aab1

Browse files
Add boosted hexes ability to accumulate multipliers
A cell can be boosted more than once. Old boosts will retain device_type of ::All. Any boost matching the device_type accumulates into the multiplier. ex: 3 boost with a multiplier of 10, will multiply by 30.
1 parent ed652e2 commit 303aab1

2 files changed

Lines changed: 114 additions & 34 deletions

File tree

boost_manager/src/activator.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,18 @@ pub async fn process_boosted_hex(
131131
hex: &BoostedHex,
132132
) -> Result<()> {
133133
match boosted_hexes.get(&hex.location) {
134-
Some(info) => {
135-
if info.start_ts.is_none() {
136-
db::insert_activated_hex(
137-
txn,
138-
hex.location.into_raw(),
139-
&info.boosted_hex_pubkey.to_string(),
140-
&info.boost_config_pubkey.to_string(),
141-
manifest_time,
142-
)
143-
.await?;
134+
Some(hexes) => {
135+
for info in hexes {
136+
if info.start_ts.is_none() {
137+
db::insert_activated_hex(
138+
txn,
139+
hex.location.into_raw(),
140+
&info.boosted_hex_pubkey.to_string(),
141+
&info.boost_config_pubkey.to_string(),
142+
manifest_time,
143+
)
144+
.await?;
145+
}
144146
}
145147
}
146148
None => {

mobile_config/src/boosted_hex_info.rs

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,13 @@ impl BoostedHexInfo {
105105
Some(self.multipliers[0])
106106
}
107107
}
108+
109+
fn matches_device_type(&self, device_type: &BoostedHexDeviceType) -> bool {
110+
self.device_type == *device_type || self.device_type == BoostedHexDeviceType::All
111+
}
108112
}
109113

110-
#[derive(Debug, Clone)]
114+
#[derive(Debug, Clone, PartialEq, Eq)]
111115
pub enum BoostedHexDeviceType {
112116
All,
113117
CbrsIndoor,
@@ -154,11 +158,6 @@ impl TryFrom<i32> for BoostedHexDeviceType {
154158
}
155159
}
156160

157-
#[derive(Debug, Clone, Default)]
158-
pub struct BoostedHexes {
159-
hexes: HashMap<Cell, BoostedHexInfo>,
160-
}
161-
162161
#[derive(PartialEq, Debug, Clone)]
163162
pub struct BoostedHex {
164163
pub location: Cell,
@@ -180,27 +179,33 @@ impl TryFrom<BoostedHexProto> for BoostedHex {
180179
}
181180
}
182181

182+
#[derive(Debug, Clone, Default)]
183+
pub struct BoostedHexes {
184+
hexes: HashMap<Cell, Vec<BoostedHexInfo>>,
185+
}
186+
183187
impl BoostedHexes {
184188
pub fn new(hexes: Vec<BoostedHexInfo>) -> Self {
185-
let hexes = hexes
186-
.into_iter()
187-
.map(|info| (info.location, info))
188-
.collect();
189-
Self { hexes }
189+
let mut me = Self::default();
190+
for hex in hexes {
191+
me.insert(hex);
192+
}
193+
me
190194
}
191195

192196
pub async fn get_all(
193197
hex_service_client: &impl HexBoostingInfoResolver<Error = ClientError>,
194198
) -> anyhow::Result<Self> {
195-
let mut map = HashMap::new();
196199
let mut stream = hex_service_client
197200
.clone()
198201
.stream_boosted_hexes_info()
199202
.await?;
203+
204+
let mut me = Self::default();
200205
while let Some(info) = stream.next().await {
201-
map.insert(info.location, info);
206+
me.insert(info);
202207
}
203-
Ok(Self { hexes: map })
208+
Ok(me)
204209
}
205210

206211
pub fn is_boosted(&self, location: &Cell) -> bool {
@@ -211,37 +216,50 @@ impl BoostedHexes {
211216
hex_service_client: &impl HexBoostingInfoResolver<Error = ClientError>,
212217
timestamp: DateTime<Utc>,
213218
) -> anyhow::Result<Self> {
214-
let mut map = HashMap::new();
215219
let mut stream = hex_service_client
216220
.clone()
217221
.stream_modified_boosted_hexes_info(timestamp)
218222
.await?;
223+
224+
let mut me = Self::default();
219225
while let Some(info) = stream.next().await {
220-
map.insert(info.location, info);
226+
me.insert(info);
221227
}
222-
Ok(Self { hexes: map })
228+
Ok(me)
223229
}
224230

225-
pub fn get_current_multiplier(&self, location: Cell, ts: DateTime<Utc>) -> Option<NonZeroU32> {
226-
self.hexes
227-
.get(&location)
228-
.and_then(|info| info.current_multiplier(ts))
231+
pub fn get_current_multiplier(
232+
&self,
233+
location: Cell,
234+
device_type: BoostedHexDeviceType,
235+
ts: DateTime<Utc>,
236+
) -> Option<NonZeroU32> {
237+
let current_multiplier = self
238+
.hexes
239+
.get(&location)?
240+
.iter()
241+
.filter(|info| info.matches_device_type(&device_type))
242+
.flat_map(|info| info.current_multiplier(ts))
243+
.map(|x| x.get())
244+
.sum::<u32>();
245+
246+
NonZeroU32::new(current_multiplier)
229247
}
230248

231249
pub fn count(&self) -> usize {
232250
self.hexes.len()
233251
}
234252

235253
pub fn iter_hexes(&self) -> impl Iterator<Item = &BoostedHexInfo> {
236-
self.hexes.values()
254+
self.hexes.values().flatten()
237255
}
238256

239-
pub fn get(&self, location: &Cell) -> Option<&BoostedHexInfo> {
257+
pub fn get(&self, location: &Cell) -> Option<&Vec<BoostedHexInfo>> {
240258
self.hexes.get(location)
241259
}
242260

243261
pub fn insert(&mut self, info: BoostedHexInfo) {
244-
self.hexes.insert(info.location, info);
262+
self.hexes.entry(info.location).or_default().push(info);
245263
}
246264
}
247265

@@ -379,6 +397,66 @@ mod tests {
379397
const BOOST_HEX_PUBKEY: &str = "J9JiLTpjaShxL8eMvUs8txVw6TZ36E38SiJ89NxnMbLU";
380398
const BOOST_HEX_CONFIG_PUBKEY: &str = "BZM1QTud72B2cpTW7PhEnFmRX7ZWzvY7DpPpNJJuDrWG";
381399

400+
#[test]
401+
fn boosted_hexes_accumulate_multipliers() -> anyhow::Result<()> {
402+
let cell = Cell::from_raw(631252734740306943)?;
403+
let now = Utc::now();
404+
405+
let hexes = vec![
406+
BoostedHexInfo {
407+
location: cell,
408+
start_ts: None,
409+
end_ts: None,
410+
period_length: Duration::seconds(2592000),
411+
multipliers: vec![NonZeroU32::new(2).unwrap()],
412+
boosted_hex_pubkey: Pubkey::from_str(BOOST_HEX_PUBKEY)?,
413+
boost_config_pubkey: Pubkey::from_str(BOOST_HEX_CONFIG_PUBKEY)?,
414+
version: 0,
415+
device_type: BoostedHexDeviceType::All,
416+
},
417+
BoostedHexInfo {
418+
location: cell,
419+
start_ts: None,
420+
end_ts: None,
421+
period_length: Duration::seconds(2592000),
422+
multipliers: vec![NonZeroU32::new(3).unwrap()],
423+
boosted_hex_pubkey: Pubkey::from_str(BOOST_HEX_PUBKEY)?,
424+
boost_config_pubkey: Pubkey::from_str(BOOST_HEX_CONFIG_PUBKEY)?,
425+
version: 0,
426+
device_type: BoostedHexDeviceType::CbrsIndoor,
427+
},
428+
// Expired boosts should not be considered
429+
BoostedHexInfo {
430+
location: cell,
431+
start_ts: Some(now - Duration::days(60)),
432+
end_ts: Some(now - Duration::days(30)),
433+
period_length: Duration::seconds(2592000),
434+
multipliers: vec![NonZeroU32::new(999).unwrap()],
435+
boosted_hex_pubkey: Pubkey::from_str(BOOST_HEX_PUBKEY)?,
436+
boost_config_pubkey: Pubkey::from_str(BOOST_HEX_CONFIG_PUBKEY)?,
437+
version: 0,
438+
device_type: BoostedHexDeviceType::All,
439+
},
440+
];
441+
442+
let boosted_hexes = BoostedHexes::new(hexes);
443+
let boosts = boosted_hexes.get(&cell).expect("boosts for test cell");
444+
assert_eq!(boosts.len(), 3, "a hex can be boosted multiple times");
445+
446+
assert_eq!(
447+
boosted_hexes.get_current_multiplier(cell, BoostedHexDeviceType::CbrsIndoor, now),
448+
NonZeroU32::new(5),
449+
"Specific boosts stack with ::ALL"
450+
);
451+
assert_eq!(
452+
boosted_hexes.get_current_multiplier(cell, BoostedHexDeviceType::WifiIndoor, now),
453+
NonZeroU32::new(2),
454+
"Missing boosts still return ::ALL"
455+
);
456+
457+
Ok(())
458+
}
459+
382460
#[test]
383461
fn boosted_hex_from_proto_valid_not_started() -> anyhow::Result<()> {
384462
let proto = BoostedHexInfoProto {

0 commit comments

Comments
 (0)