Fixed DHT republish after TTL
This commit is contained in:
Generated
+190
-182
File diff suppressed because it is too large
Load Diff
@@ -84,7 +84,7 @@ pub trait MusicDhtStorage: std::fmt::Debug + Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Searches locally owned active items: exact normalized match, or all
|
/// Searches locally owned active items: exact normalized match, or all
|
||||||
/// query tokens present in the item's token set.
|
/// query tokens present in the item's token set, including artist names.
|
||||||
async fn search_local(&self, normalized_query: String) -> Result<Vec<LibraryItem>> {
|
async fn search_local(&self, normalized_query: String) -> Result<Vec<LibraryItem>> {
|
||||||
let all = self.list_local_items(false).await?;
|
let all = self.list_local_items(false).await?;
|
||||||
let query_tokens = tokenize(&normalized_query);
|
let query_tokens = tokenize(&normalized_query);
|
||||||
@@ -97,7 +97,7 @@ pub trait MusicDhtStorage: std::fmt::Debug + Send + Sync {
|
|||||||
if query_tokens.is_empty() {
|
if query_tokens.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let artist_tokens = tokenize(&item.normalized_name);
|
let artist_tokens = item.search_tokens();
|
||||||
query_tokens
|
query_tokens
|
||||||
.iter()
|
.iter()
|
||||||
.all(|token| artist_tokens.iter().any(|t| t == token))
|
.all(|token| artist_tokens.iter().any(|t| t == token))
|
||||||
@@ -237,7 +237,7 @@ impl Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Searches locally owned active items: exact normalized match, or all
|
/// Searches locally owned active items: exact normalized match, or all
|
||||||
/// query tokens present in the item's token set.
|
/// query tokens present in the item's token set, including artist names.
|
||||||
pub async fn search_local(&self, normalized_query: String) -> Result<Vec<LibraryItem>> {
|
pub async fn search_local(&self, normalized_query: String) -> Result<Vec<LibraryItem>> {
|
||||||
let all = self.list_local_items(false).await?;
|
let all = self.list_local_items(false).await?;
|
||||||
let query_tokens = tokenize(&normalized_query);
|
let query_tokens = tokenize(&normalized_query);
|
||||||
@@ -250,7 +250,7 @@ impl Database {
|
|||||||
if query_tokens.is_empty() {
|
if query_tokens.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let artist_tokens = tokenize(&item.normalized_name);
|
let artist_tokens = item.search_tokens();
|
||||||
query_tokens
|
query_tokens
|
||||||
.iter()
|
.iter()
|
||||||
.all(|token| artist_tokens.iter().any(|t| t == token))
|
.all(|token| artist_tokens.iter().any(|t| t == token))
|
||||||
@@ -522,6 +522,22 @@ mod tests {
|
|||||||
assert!(none.is_empty());
|
assert!(none.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn local_search_uses_artist_names() {
|
||||||
|
let (_dir, db) = open_temp().await;
|
||||||
|
let owner = test_peer(1);
|
||||||
|
let mut track = item(owner, "Teardrop", 1, false);
|
||||||
|
track.kind = ItemKind::Track;
|
||||||
|
track.artist_names = vec!["Massive Attack".into()];
|
||||||
|
db.upsert_local_item(&track).await.expect("upsert");
|
||||||
|
|
||||||
|
let found = db
|
||||||
|
.search_local("massive attack".into())
|
||||||
|
.await
|
||||||
|
.expect("search");
|
||||||
|
assert_eq!(found, vec![track]);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn expired_dht_record_is_not_returned() {
|
async fn expired_dht_record_is_not_returned() {
|
||||||
let (_dir, db) = open_temp().await;
|
let (_dir, db) = open_temp().await;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use federation_net::NetworkId;
|
|||||||
use crate::message::StoreRecordRequest;
|
use crate::message::StoreRecordRequest;
|
||||||
use crate::normalization::{normalize_name, tokenize};
|
use crate::normalization::{normalize_name, tokenize};
|
||||||
use crate::record::{
|
use crate::record::{
|
||||||
ACTIVE_RECORD_TTL, DhtKey, MAX_ITEM_NAME_BYTES, MAX_TOKENS_PER_ITEM, StoredRecord,
|
ACTIVE_RECORD_TTL, DhtKey, MAX_ARTISTS_PER_ITEM, MAX_ITEM_NAME_BYTES, MAX_TOKENS_PER_ITEM,
|
||||||
TOMBSTONE_TTL,
|
StoredRecord, TOMBSTONE_TTL,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Outcome of comparing an incoming record with the stored one.
|
/// Outcome of comparing an incoming record with the stored one.
|
||||||
@@ -59,9 +59,9 @@ pub fn decide_store(existing: Option<(u64, bool, u64)>, incoming: &StoredRecord)
|
|||||||
/// Validates an incoming `StoreRecord` request.
|
/// Validates an incoming `StoreRecord` request.
|
||||||
///
|
///
|
||||||
/// Checks the size limits, that the record is internally consistent, that the
|
/// Checks the size limits, that the record is internally consistent, that the
|
||||||
/// key actually corresponds to the record's name or one of its tokens, and
|
/// key actually corresponds to the record's name, or one of the item's/artist's
|
||||||
/// clamps the expiry to the maximum TTL allowed for the record type. Returns
|
/// search tokens, and clamps the expiry to the maximum TTL allowed for the
|
||||||
/// the record with a possibly clamped `expires_at_ms`.
|
/// record type. Returns the record with a possibly clamped `expires_at_ms`.
|
||||||
pub(crate) fn validate_store(
|
pub(crate) fn validate_store(
|
||||||
request: StoreRecordRequest,
|
request: StoreRecordRequest,
|
||||||
network_id: &NetworkId,
|
network_id: &NetworkId,
|
||||||
@@ -83,9 +83,21 @@ pub(crate) fn validate_store(
|
|||||||
if tokens.len() > MAX_TOKENS_PER_ITEM {
|
if tokens.len() > MAX_TOKENS_PER_ITEM {
|
||||||
return Err("too many tokens".into());
|
return Err("too many tokens".into());
|
||||||
}
|
}
|
||||||
|
if item.artist_names.len() > MAX_ARTISTS_PER_ITEM {
|
||||||
|
return Err("too many artists".into());
|
||||||
|
}
|
||||||
|
for artist in &item.artist_names {
|
||||||
|
if artist.len() > MAX_ITEM_NAME_BYTES {
|
||||||
|
return Err("artist name too long".into());
|
||||||
|
}
|
||||||
|
if tokenize(&normalize_name(artist)).len() > MAX_TOKENS_PER_ITEM {
|
||||||
|
return Err("artist name has too many tokens".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let key_matches = request.key == DhtKey::exact(network_id, &item.normalized_name)
|
let key_matches = request.key == DhtKey::exact(network_id, &item.normalized_name)
|
||||||
|| tokens
|
|| item
|
||||||
|
.search_tokens()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|token| request.key == DhtKey::token(network_id, token));
|
.any(|token| request.key == DhtKey::token(network_id, token));
|
||||||
if !key_matches {
|
if !key_matches {
|
||||||
@@ -138,6 +150,28 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn track_with_artist(expires_at_ms: u64) -> StoredRecord {
|
||||||
|
let owner = test_peer(1);
|
||||||
|
StoredRecord {
|
||||||
|
item: LibraryItem {
|
||||||
|
id: ItemId::from_bytes([8u8; 32]),
|
||||||
|
owner,
|
||||||
|
kind: crate::record::ItemKind::Track,
|
||||||
|
name: "Teardrop".into(),
|
||||||
|
normalized_name: "teardrop".into(),
|
||||||
|
artist_names: vec!["Massive Attack".into()],
|
||||||
|
year: Some(1998),
|
||||||
|
release_type: None,
|
||||||
|
duration_seconds: Some(330.0),
|
||||||
|
revision: 1,
|
||||||
|
deleted: false,
|
||||||
|
updated_at_ms: 0,
|
||||||
|
},
|
||||||
|
publisher: owner,
|
||||||
|
expires_at_ms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn newer_revision_replaces_older() {
|
fn newer_revision_replaces_older() {
|
||||||
let incoming = record(2, false, 1000);
|
let incoming = record(2, false, 1000);
|
||||||
@@ -273,4 +307,34 @@ mod tests {
|
|||||||
.is_err()
|
.is_err()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_accepts_artist_token_keys() {
|
||||||
|
let net = NetworkId::from_name("test");
|
||||||
|
let now = 1_000_000;
|
||||||
|
let rec = track_with_artist(now + ACTIVE_RECORD_TTL.as_millis() as u64);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
validate_store(
|
||||||
|
StoreRecordRequest {
|
||||||
|
key: DhtKey::token(&net, "massive"),
|
||||||
|
record: rec.clone(),
|
||||||
|
},
|
||||||
|
&net,
|
||||||
|
now,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
validate_store(
|
||||||
|
StoreRecordRequest {
|
||||||
|
key: DhtKey::token(&net, "attack"),
|
||||||
|
record: rec,
|
||||||
|
},
|
||||||
|
&net,
|
||||||
|
now,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user