Skip to content

Commit 5c88d0c

Browse files
authored
Merge pull request #999 from Stremio/avoid-redundant-library-pulls
Fix(update_library): skip unchanged library pulls
2 parents 9f5a210 + 844869b commit 5c88d0c

1 file changed

Lines changed: 126 additions & 9 deletions

File tree

src/models/ctx/update_library.rs

Lines changed: 126 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -273,20 +273,28 @@ pub fn update_library<E: Env + 'static>(
273273
)) if Some(loading_auth_key) == auth_key => match result {
274274
Ok(items) => {
275275
// send an event that the missing library is now present
276-
let library_missing_effects = Effects::msg(Msg::Event(Event::UserLibraryMissing {
276+
let pulled_events_effects = Effects::msg(Msg::Event(Event::UserLibraryMissing {
277277
library_missing: false,
278278
}))
279279
.unchanged();
280280

281-
library_missing_effects
282-
.join(Effects::msg(Msg::Event(Event::LibraryItemsPulledFromAPI {
281+
let pulled_events_effects = pulled_events_effects.join(
282+
Effects::msg(Msg::Event(Event::LibraryItemsPulledFromAPI {
283283
ids: ids.to_owned(),
284-
})))
285-
.join(Effects::one(update_and_push_items_to_storage::<E>(
286-
library,
287-
items.to_owned(),
288-
)))
289-
.join(Effects::msg(Msg::Internal(Internal::LibraryChanged(true))))
284+
}))
285+
.unchanged(),
286+
);
287+
288+
if should_update_library_with_items(library, items) {
289+
pulled_events_effects
290+
.join(Effects::one(update_and_push_items_to_storage::<E>(
291+
library,
292+
items.to_owned(),
293+
)))
294+
.join(Effects::msg(Msg::Internal(Internal::LibraryChanged(true))))
295+
} else {
296+
pulled_events_effects
297+
}
290298
}
291299
Err(error) => Effects::msg(Msg::Event(Event::Error {
292300
error: error.to_owned(),
@@ -300,6 +308,13 @@ pub fn update_library<E: Env + 'static>(
300308
}
301309
}
302310

311+
fn should_update_library_with_items(library: &LibraryBucket, items: &[LibraryItem]) -> bool {
312+
items.iter().any(|item| match library.items.get(&item.id) {
313+
Some(current_item) => item.mtime >= current_item.mtime && item != current_item,
314+
None => true,
315+
})
316+
}
317+
303318
fn update_and_push_items_to_storage<E: Env + 'static>(
304319
library: &mut LibraryBucket,
305320
items: Vec<LibraryItem>,
@@ -484,3 +499,105 @@ fn plan_sync_with_api<E: Env + 'static>(library: &LibraryBucket, auth_key: &Auth
484499
)
485500
.into()
486501
}
502+
503+
#[cfg(test)]
504+
mod tests {
505+
use chrono::{TimeZone, Utc};
506+
507+
use crate::models::ctx::CtxStatus;
508+
use crate::runtime::msg::{Internal, Msg};
509+
use crate::types::api::{DatastoreCommand, DatastoreRequest};
510+
use crate::types::library::{LibraryBucket, LibraryItem};
511+
use crate::types::profile::{Auth, AuthKey, GDPRConsent, Profile, User};
512+
513+
use super::{should_update_library_with_items, update_library};
514+
515+
fn library_item(id: &str) -> LibraryItem {
516+
LibraryItem {
517+
id: id.to_owned(),
518+
r#type: "series".to_owned(),
519+
name: "name".to_owned(),
520+
poster: None,
521+
poster_shape: Default::default(),
522+
removed: false,
523+
temp: false,
524+
ctime: Some(Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap()),
525+
mtime: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(),
526+
state: Default::default(),
527+
behavior_hints: Default::default(),
528+
}
529+
}
530+
531+
#[test]
532+
fn should_update_library_with_items_only_when_merge_changes_library() {
533+
let item = library_item("id");
534+
let library = LibraryBucket::new(None, vec![item.to_owned()]);
535+
536+
assert!(!should_update_library_with_items(
537+
&library,
538+
&[item.to_owned()]
539+
));
540+
541+
let mut newer_item = item.to_owned();
542+
newer_item.name = "updated".to_owned();
543+
newer_item.mtime = Utc.with_ymd_and_hms(2020, 1, 2, 0, 0, 0).unwrap();
544+
assert!(should_update_library_with_items(&library, &[newer_item]));
545+
546+
let mut older_item = item.to_owned();
547+
older_item.name = "older".to_owned();
548+
older_item.mtime = Utc.with_ymd_and_hms(2019, 12, 31, 0, 0, 0).unwrap();
549+
assert!(!should_update_library_with_items(&library, &[older_item]));
550+
551+
assert!(should_update_library_with_items(
552+
&library,
553+
&[library_item("missing")]
554+
));
555+
}
556+
557+
#[test]
558+
fn library_pull_result_is_unchanged_when_items_match() {
559+
let item = library_item("id");
560+
let auth_key = AuthKey("auth_key".to_owned());
561+
let profile = Profile {
562+
auth: Some(Auth {
563+
key: auth_key.to_owned(),
564+
user: User {
565+
id: "user_id".into(),
566+
email: "user_email".to_owned(),
567+
last_modified: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(),
568+
date_registered: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(),
569+
gdpr_consent: GDPRConsent {
570+
tos: true,
571+
privacy: true,
572+
marketing: true,
573+
from: Some("tests".to_owned()),
574+
},
575+
..Default::default()
576+
},
577+
}),
578+
..Default::default()
579+
};
580+
let request = DatastoreRequest {
581+
auth_key,
582+
collection: "libraryItem".to_owned(),
583+
command: DatastoreCommand::Get {
584+
ids: vec!["id".to_owned()],
585+
all: false,
586+
},
587+
};
588+
let msg = Msg::Internal(Internal::LibraryPullResult(
589+
request,
590+
Ok(vec![item.to_owned()]),
591+
));
592+
let mut library = LibraryBucket::new(Some("user_id".into()), vec![item]);
593+
594+
let effects = update_library::<crate::unit_tests::TestEnv>(
595+
&mut library,
596+
&profile,
597+
&CtxStatus::Ready,
598+
&msg,
599+
);
600+
601+
assert!(!effects.has_changed);
602+
}
603+
}

0 commit comments

Comments
 (0)