Schema 21: person_relay.{read,write} + relay.{read,write,advertise} + a rename

This commit is contained in:
Mike Dilger 2023-02-03 21:21:19 +13:00
parent dc2131b997
commit f0d573f991
11 changed files with 135 additions and 57 deletions

View File

@ -25,7 +25,7 @@ pub enum ToOverlordMessage {
RefreshFollowedMetadata,
RankRelay(RelayUrl, u8),
SaveSettings,
SetRelayPost(RelayUrl, bool),
SetRelayReadWrite(RelayUrl, bool, bool),
SetThreadFeed(Id, Id, Option<Id>),
Shutdown,
UnlockKey(String),

View File

@ -103,7 +103,7 @@ macro_rules! apply_sql {
}
fn upgrade(db: &Connection, mut version: u16) -> Result<(), Error> {
let current_version = 20;
let current_version = 21;
if version > current_version {
panic!(
"Database version {} is newer than this binary which expects version {}.",
@ -133,6 +133,7 @@ fn upgrade(db: &Connection, mut version: u16) -> Result<(), Error> {
apply_sql!(db, version, 18, "schema18.sql");
apply_sql!(db, version, 19, "schema19.sql");
apply_sql!(db, version, 20, "schema20.sql");
apply_sql!(db, version, 21, "schema21.sql");
tracing::info!("Database is at version {}", version);
Ok(())
}

View File

@ -13,6 +13,8 @@ pub struct DbPersonRelay {
pub last_suggested_nip23: Option<u64>,
pub last_suggested_nip05: Option<u64>,
pub last_suggested_bytag: Option<u64>,
pub read: bool,
pub write: bool,
}
impl DbPersonRelay {
@ -25,10 +27,11 @@ impl DbPersonRelay {
let sql = format!(
"SELECT person, relay, person_relay.last_fetched, \
last_suggested_kind2, last_suggested_kind3, last_suggested_nip23, \
last_suggested_nip05, last_suggested_bytag \
last_suggested_nip05, last_suggested_bytag, read, write \
FROM person_relay \
INNER JOIN relay ON person_relay.relay=relay.url \
WHERE person IN ({}) ORDER BY person, relay.rank DESC, \
WHERE person IN ({}) ORDER BY person, \
person_relay.write DESC, relay.rank DESC, \
last_suggested_nip23 DESC, last_suggested_kind3 DESC, \
last_suggested_nip05 DESC, last_suggested_kind2 DESC, \
last_fetched DESC, last_suggested_bytag DESC",
@ -57,6 +60,8 @@ impl DbPersonRelay {
last_suggested_nip23: row.get(5)?,
last_suggested_nip05: row.get(6)?,
last_suggested_bytag: row.get(7)?,
read: row.get(8)?,
write: row.get(9)?,
});
}
}
@ -71,8 +76,8 @@ impl DbPersonRelay {
pub async fn insert(person_relay: DbPersonRelay) -> Result<(), Error> {
let sql = "INSERT OR IGNORE INTO person_relay (person, relay, last_fetched, \
last_suggested_kind2, last_suggested_kind3, last_suggested_nip23, \
last_suggested_nip05, last_suggested_bytag) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
last_suggested_nip05, last_suggested_bytag, read, write) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
@ -88,6 +93,8 @@ impl DbPersonRelay {
&person_relay.last_suggested_nip23,
&person_relay.last_suggested_nip05,
&person_relay.last_suggested_bytag,
&person_relay.read,
&person_relay.write,
))?;
Ok::<(), Error>(())
})
@ -229,7 +236,7 @@ impl DbPersonRelay {
pub async fn get_best_relays(pubkey: PublicKeyHex) -> Result<Vec<(RelayUrl, u64)>, Error> {
let sql = "SELECT person, relay, last_suggested_nip23, last_suggested_kind3, \
last_suggested_nip05, last_fetched, last_suggested_kind2, \
last_suggested_bytag \
last_suggested_bytag, read, write \
FROM person_relay WHERE person=?";
let ranked_relays: Result<Vec<(RelayUrl, u64)>, Error> = spawn_blocking(move || {
@ -253,6 +260,8 @@ impl DbPersonRelay {
last_suggested_nip23: row.get(5)?,
last_suggested_nip05: row.get(6)?,
last_suggested_bytag: row.get(7)?,
read: row.get(8)?,
write: row.get(9)?,
};
dbprs.push(dbpr);
}

View File

@ -11,7 +11,9 @@ pub struct DbRelay {
pub rank: u64,
pub last_connected_at: Option<u64>,
pub last_general_eose_at: Option<u64>,
pub post: bool,
pub read: bool,
pub write: bool,
pub advertise: bool,
}
impl DbRelay {
@ -23,7 +25,9 @@ impl DbRelay {
rank: 3,
last_connected_at: None,
last_general_eose_at: None,
post: false,
read: false,
write: false,
advertise: false,
}
}
@ -41,7 +45,7 @@ impl DbRelay {
pub async fn fetch(criteria: Option<&str>) -> Result<Vec<DbRelay>, Error> {
let sql = "SELECT url, success_count, failure_count, rank, last_connected_at, \
last_general_eose_at, post FROM relay"
last_general_eose_at, read, write, advertise FROM relay"
.to_owned();
let sql = match criteria {
None => sql,
@ -56,7 +60,6 @@ impl DbRelay {
let mut rows = stmt.query([])?;
let mut output: Vec<DbRelay> = Vec::new();
while let Some(row) = rows.next()? {
let postint: u32 = row.get(6)?;
let s: String = row.get(0)?;
// just skip over invalid relay URLs
if let Ok(url) = RelayUrl::try_from_str(&s) {
@ -67,7 +70,9 @@ impl DbRelay {
rank: row.get(3)?,
last_connected_at: row.get(4)?,
last_general_eose_at: row.get(5)?,
post: (postint > 0),
read: row.get(6)?,
write: row.get(7)?,
advertise: row.get(8)?,
});
}
}
@ -90,15 +95,14 @@ impl DbRelay {
pub async fn insert(relay: DbRelay) -> Result<(), Error> {
let sql = "INSERT OR IGNORE INTO relay (url, success_count, failure_count, rank, \
last_connected_at, last_general_eose_at, post) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)";
last_connected_at, last_general_eose_at, read, write, advertise) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)";
spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(sql)?;
let postint = i32::from(relay.post);
stmt.execute((
&relay.url.0,
&relay.success_count,
@ -106,7 +110,9 @@ impl DbRelay {
&relay.rank,
&relay.last_connected_at,
&relay.last_general_eose_at,
&postint,
&relay.read,
&relay.write,
&relay.advertise,
))?;
Ok::<(), Error>(())
})
@ -117,21 +123,22 @@ impl DbRelay {
pub async fn update(relay: DbRelay) -> Result<(), Error> {
let sql = "UPDATE relay SET success_count=?, failure_count=?, rank=?, \
last_connected_at=?, last_general_eose_at=?, post=? WHERE url=?";
last_connected_at=?, last_general_eose_at=?, read=?, write=?, advertise=? WHERE url=?";
spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(sql)?;
let postint = i32::from(relay.post);
stmt.execute((
&relay.success_count,
&relay.failure_count,
&relay.rank,
&relay.last_connected_at,
&relay.last_general_eose_at,
&postint,
&relay.read,
&relay.write,
&relay.advertise,
&relay.url.0,
))?;
Ok::<(), Error>(())
@ -162,13 +169,31 @@ impl DbRelay {
Ok(())
}
pub async fn update_post(url: RelayUrl, post: bool) -> Result<(), Error> {
let sql = "UPDATE relay SET post = ? WHERE url = ?";
pub async fn update_read_and_write(
url: RelayUrl,
read: bool,
write: bool,
) -> Result<(), Error> {
let sql = "UPDATE relay SET read = ?, write = ? WHERE url = ?";
spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(sql)?;
stmt.execute((&post, &url.0))?;
stmt.execute((&read, &write, &url.0))?;
Ok::<(), Error>(())
})
.await??;
Ok(())
}
pub async fn update_advertise(url: RelayUrl, advertise: bool) -> Result<(), Error> {
let sql = "UPDATE relay SET advertise = ? WHERE url = ?";
spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
let mut stmt = db.prepare(sql)?;
stmt.execute((&advertise, &url.0))?;
Ok::<(), Error>(())
})
.await??;
@ -241,10 +266,10 @@ impl DbRelay {
}
pub async fn recommended_relay_for_reply(reply_to: Id) -> Result<Option<RelayUrl>, Error> {
// Try to find a relay where the event was seen AND that I post to which
// Try to find a relay where the event was seen AND that I write to which
// has a rank>1
let sql = "SELECT url FROM relay INNER JOIN event_seen ON relay.url=event_seen.relay \
WHERE event_seen.event=? AND relay.post=1 AND relay.rank>1";
WHERE event_seen.event=? AND relay.write=1 AND relay.rank>1";
let output: Option<RelayUrl> = spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();

8
src/db/schema21.sql Normal file
View File

@ -0,0 +1,8 @@
ALTER TABLE person_relay ADD COLUMN read INTEGER NOT NULL DEFAULT 0;
ALTER TABLE person_relay ADD COLUMN write INTEGER NOT NULL DEFAULT 0;
ALTER TABLE person RENAME COLUMN contact_list_last_received TO relay_list_last_received;
ALTER TABLE person ADD COLUMN relay_list_created_at INTEGER NOT NULL DEFAULT 0;
ALTER TABLE relay RENAME COLUMN post TO write;
ALTER TABLE relay ADD COLUMN read INTEGER NOT NULL DEFAULT 0;
ALTER TABLE relay ADD COLUMN advertise INTEGER NOT NULL DEFAULT 0;

View File

@ -219,7 +219,7 @@ impl Globals {
.relays
.blocking_read()
.iter()
.filter(|(_, r)| r.post)
.filter(|(_, r)| r.write)
{
profile.relays.push(url.to_unchecked_url())
}

View File

@ -565,11 +565,12 @@ impl Overlord {
GLOBALS.settings.read().await.save().await?;
tracing::debug!("Settings saved.");
}
ToOverlordMessage::SetRelayPost(relay_url, post) => {
ToOverlordMessage::SetRelayReadWrite(relay_url, read, write) => {
if let Some(relay) = GLOBALS.relays.write().await.get_mut(&relay_url) {
relay.post = post;
relay.read = read;
relay.write = write;
}
DbRelay::update_post(relay_url, post).await?;
DbRelay::update_read_and_write(relay_url, read, write).await?;
}
ToOverlordMessage::SetThreadFeed(id, referenced_by, previous_thread_parent) => {
self.set_thread_feed(id, referenced_by, previous_thread_parent)
@ -649,6 +650,8 @@ impl Overlord {
last_suggested_nip23: None,
last_suggested_nip05: None,
last_suggested_bytag: None,
read: true,
write: true,
})
.await?;
@ -710,7 +713,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {
@ -838,7 +841,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {
@ -913,7 +916,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {
@ -948,7 +951,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {
@ -981,7 +984,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {
@ -1025,7 +1028,7 @@ impl Overlord {
.read()
.await
.iter()
.filter_map(|(_, r)| if r.post { Some(r.to_owned()) } else { None })
.filter_map(|(_, r)| if r.write { Some(r.to_owned()) } else { None })
.collect();
for relay in relays {

View File

@ -24,7 +24,8 @@ pub struct DbPerson {
pub followed: u8,
pub followed_last_updated: i64,
pub muted: u8,
pub contact_list_last_received: i64,
pub relay_list_last_received: i64,
pub relay_list_created_at: i64,
}
impl DbPerson {
@ -38,7 +39,8 @@ impl DbPerson {
followed: 0,
followed_last_updated: 0,
muted: 0,
contact_list_last_received: 0,
relay_list_last_received: 0,
relay_list_created_at: 0,
}
}
@ -126,7 +128,7 @@ impl People {
let mut output: Vec<PublicKeyHex> = Vec::new();
for person in self.people.iter().filter_map(|p| {
if p.followed == 1
&& p.contact_list_last_received < one_day_ago
&& p.relay_list_last_received < one_day_ago
&& among_these.contains(&p.pubkey)
{
Some(p)
@ -306,7 +308,8 @@ impl People {
// who are muted, so they can be found and unmuted as necessary.
let sql = "SELECT pubkey, metadata, metadata_at, nip05_valid, nip05_last_checked, \
followed, followed_last_updated, muted, contact_list_last_received \
followed, followed_last_updated, muted, relay_list_last_received, \
relay_list_created_at \
FROM person WHERE followed=1 OR muted=1"
.to_owned();
@ -333,7 +336,8 @@ impl People {
followed: row.get(5)?,
followed_last_updated: row.get(6)?,
muted: row.get(7)?,
contact_list_last_received: row.get(8)?,
relay_list_last_received: row.get(8)?,
relay_list_created_at: row.get(9)?,
});
}
Ok(output)
@ -814,22 +818,30 @@ impl People {
Ok(())
}
pub async fn update_contact_list_last_received(
pub async fn update_relay_list_stamps(
&self,
pubkeyhex: PublicKeyHex,
mut created_at: i64,
) -> Result<(), Error> {
let now = Unixtime::now().unwrap().0;
if let Some(mut person) = self.people.get_mut(&pubkeyhex) {
person.contact_list_last_received = now;
created_at = created_at.max(person.relay_list_created_at);
person.relay_list_last_received = now;
person.relay_list_created_at = created_at;
} else {
tracing::warn!("FIXME: RelayList for person we don't have. We should create them.");
return Ok(());
}
task::spawn_blocking(move || {
let maybe_db = GLOBALS.db.blocking_lock();
let db = maybe_db.as_ref().unwrap();
let mut stmt =
db.prepare("UPDATE person SET contact_list_last_received=? WHERE pubkey=?")?;
stmt.execute((&now, pubkeyhex.as_str()))?;
let mut stmt = db.prepare(
"UPDATE person SET relay_list_last_received=?, \
relay_list_created_at=? WHERE pubkey=?",
)?;
stmt.execute((&now, &created_at, pubkeyhex.as_str()))?;
Ok(())
})
.await?
@ -910,7 +922,8 @@ impl People {
let sql = "SELECT pubkey, metadata, metadata_at, \
nip05_valid, nip05_last_checked, \
followed, followed_last_updated, muted, \
contact_list_last_received FROM person"
relay_list_last_received, relay_list_created_at \
FROM person"
.to_owned();
let sql = match criteria {
None => sql,
@ -940,7 +953,8 @@ impl People {
followed: row.get(5)?,
followed_last_updated: row.get(6)?,
muted: row.get(7)?,
contact_list_last_received: row.get(8)?,
relay_list_last_received: row.get(8)?,
relay_list_created_at: row.get(9)?,
});
}
Ok(output)
@ -963,8 +977,8 @@ impl People {
async fn fetch_many(pubkeys: &[&PublicKeyHex]) -> Result<Vec<DbPerson>, Error> {
let sql = format!(
"SELECT pubkey, metadata, metadata_at, nip05_valid, nip05_last_checked, \
followed, followed_last_updated, muted, contact_list_last_received \
FROM person WHERE pubkey IN ({})",
followed, followed_last_updated, muted, relay_list_last_received, \
relay_list_created_at FROM person WHERE pubkey IN ({})",
repeat_vars(pubkeys.len())
);
@ -1000,7 +1014,8 @@ impl People {
followed: row.get(5)?,
followed_last_updated: row.get(6)?,
muted: row.get(7)?,
contact_list_last_received: row.get(8)?,
relay_list_last_received: row.get(8)?,
relay_list_created_at: row.get(9)?,
});
}

View File

@ -215,7 +215,7 @@ pub async fn process_new_event(
if event.kind == EventKind::ContactList {
GLOBALS
.people
.update_contact_list_last_received(event.pubkey.into())
.update_relay_list_stamps(event.pubkey.into(), event.created_at.0)
.await?;
if let Some(pubkey) = GLOBALS.signer.public_key() {

View File

@ -23,7 +23,7 @@ pub(super) fn posting_area(
}
ui.label(" to post.");
});
} else if !GLOBALS.relays.blocking_read().iter().any(|(_, r)| r.post) {
} else if !GLOBALS.relays.blocking_read().iter().any(|(_, r)| r.write) {
ui.horizontal_wrapped(|ui| {
ui.label("You need to ");
if ui.link("choose relays").clicked() {

View File

@ -40,7 +40,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
// TBD time how long this takes. We don't want expensive code in the UI
let mut relays = GLOBALS.relays.blocking_read().clone();
let mut relays: Vec<DbRelay> = relays.drain().map(|(_, relay)| relay).collect();
relays.sort_by(|a, b| b.post.cmp(&a.post).then(a.url.cmp(&b.url)));
relays.sort_by(|a, b| b.write.cmp(&a.write).then(a.url.cmp(&b.url)));
ui.with_layout(Layout::bottom_up(Align::Center), |ui| {
ui.add_space(18.0);
@ -62,6 +62,7 @@ fn relay_table(ui: &mut Ui, relays: &mut [DbRelay], id: &'static str) {
.column(Column::auto().resizable(true))
.column(Column::auto().resizable(true))
.column(Column::auto().resizable(true))
.column(Column::auto().resizable(true))
.column(Column::remainder())
.header(20.0, |mut header| {
header.col(|ui| {
@ -81,7 +82,12 @@ fn relay_table(ui: &mut Ui, relays: &mut [DbRelay], id: &'static str) {
.on_hover_text("This only counts events served after EOSE, as they mark where we can pick up from next time.");
});
header.col(|ui| {
ui.heading("Write");
ui.heading("Read")
.on_hover_text("Read for events with mentions of you on these relays. It is recommended to have a few." );
});
header.col(|ui| {
ui.heading("Write")
.on_hover_text("Write your events to these relays. It is recommended to have a few." );
});
header.col(|ui| {
ui.heading("Read rank")
@ -112,14 +118,25 @@ fn relay_table(ui: &mut Ui, relays: &mut [DbRelay], id: &'static str) {
}
});
row.col(|ui| {
let mut post = relay.post; // checkbox needs a mutable state variable.
if ui.checkbox(&mut post, "")
.on_hover_text("If selected, posts you create will be sent to this relay. But you have to press [SAVE CHANGES] at the bottom of this page.")
let mut read = relay.read; // checkbox needs a mutable state variable.
if ui.checkbox(&mut read, "")
.on_hover_text("If selected, we will search for posts mentioning you on this relay.")
.clicked()
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::SetRelayPost(relay.url.clone(), post));
.send(ToOverlordMessage::SetRelayReadWrite(relay.url.clone(), read, relay.write));
}
});
row.col(|ui| {
let mut write = relay.write; // checkbox needs a mutable state variable.
if ui.checkbox(&mut write, "")
.on_hover_text("If selected, posts you create will be sent to this relay.")
.clicked()
{
let _ = GLOBALS
.to_overlord
.send(ToOverlordMessage::SetRelayReadWrite(relay.url.clone(), relay.read, write));
}
});
row.col(|ui| {