storage: Migrate Relationships, added some, renamed, and rebuild them

This commit is contained in:
Mike Dilger 2024-04-26 09:56:49 +12:00
parent 8ca7dfeb06
commit b271be11a8
12 changed files with 491 additions and 192 deletions

View File

@ -42,7 +42,7 @@ pub async fn process_new_event(
let mut maxtime = now;
maxtime.0 += GLOBALS.storage.read_setting_future_allowance_secs() as i64;
if let Err(e) = event.verify(Some(maxtime)) {
tracing::error!("{}: VERIFY ERROR: {}", e, serde_json::to_string(&event)?);
tracing::warn!("{}: VERIFY ERROR: {}", e, serde_json::to_string(&event)?);
return Ok(());
}
}
@ -121,7 +121,7 @@ pub async fn process_new_event(
// Ignore if the event is already deleted (by id)
for (_id, relbyid) in GLOBALS.storage.find_relationships_by_id(event.id)? {
if let RelationshipById::Deletion { by, reason: _ } = relbyid {
if let RelationshipById::Deletes { by, reason: _ } = relbyid {
if event.delete_author_allowed(by) {
tracing::trace!(
"{}: Deleted Event: {} {:?} @{}",
@ -144,7 +144,7 @@ pub async fn process_new_event(
author: event.pubkey,
};
for (_id, relbyaddr) in GLOBALS.storage.find_relationships_by_addr(&ea)? {
if let RelationshipByAddr::Deletion { by, reason: _ } = relbyaddr {
if let RelationshipByAddr::Deletes { by, reason: _ } = relbyaddr {
if by == event.pubkey {
tracing::trace!(
"{}: Deleted Event: {} {:?} @{}",
@ -483,25 +483,39 @@ pub(crate) fn process_relationships_of_event<'a>(
let mut invalidate: Vec<Id> = Vec::new();
let mut f = |txn: &mut RwTxn<'a>| -> Result<(), Error> {
// replies to
match event.replies_to() {
Some(EventReference::Id { id, .. }) => {
// Reposts
if event.kind == EventKind::Repost {
if let Ok(inner_event) = serde_json::from_str::<Event>(&event.content) {
GLOBALS.storage.write_relationship_by_id(
id,
inner_event.id,
event.id,
RelationshipById::Reply,
RelationshipById::Reposts,
Some(txn),
)?;
} else {
for eref in event.mentions().iter() {
if let EventReference::Id { id, .. } = eref {
GLOBALS.storage.write_relationship_by_id(
*id,
event.id,
RelationshipById::Reposts,
Some(txn),
)?;
}
}
}
}
// Quotes
for eref in event.quotes().iter() {
if let EventReference::Id { id, .. } = eref {
GLOBALS.storage.write_relationship_by_id(
*id,
event.id,
RelationshipById::Quotes,
Some(txn),
)?;
}
Some(EventReference::Addr(ea)) => {
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::Reply,
Some(txn),
)?;
}
None => (),
}
// timestamps
@ -511,7 +525,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::Timestamp,
RelationshipById::Timestamps,
Some(txn),
)?;
}
@ -543,7 +557,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
*id,
event.id,
RelationshipById::Deletion {
RelationshipById::Deletes {
by: event.pubkey,
reason: reason.clone(),
},
@ -574,7 +588,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea.clone(),
event.id,
RelationshipByAddr::Deletion {
RelationshipByAddr::Deletes {
by: event.pubkey,
reason: reason.clone(),
},
@ -591,7 +605,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
reacted_to_id, // event reacted to
event.id, // the reaction event id
RelationshipById::Reaction {
RelationshipById::ReactsTo {
by: event.pubkey,
reaction,
},
@ -627,6 +641,16 @@ pub(crate) fn process_relationships_of_event<'a>(
},
Some(txn),
)?;
} else if let Ok((ea, _marker)) = tag.parse_address() {
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::Labels {
label: label.to_owned(),
namespace: namespace.to_owned(),
},
Some(txn),
)?;
}
}
}
@ -638,7 +662,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::ListMutesThread,
RelationshipById::Mutes,
Some(txn),
)?;
}
@ -652,7 +676,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::ListPins,
RelationshipById::Pins,
Some(txn),
)?;
}
@ -666,7 +690,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::ListBookmarks,
RelationshipById::Bookmarks,
Some(txn),
)?;
}
@ -675,7 +699,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::ListBookmarks,
RelationshipByAddr::Bookmarks,
Some(txn),
)?;
}
@ -689,7 +713,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::ListBookmarks,
RelationshipById::Bookmarks,
Some(txn),
)?;
}
@ -698,7 +722,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::ListBookmarks,
RelationshipByAddr::Bookmarks,
Some(txn),
)?;
}
@ -712,7 +736,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::Curation,
RelationshipById::Curates,
Some(txn),
)?;
}
@ -720,7 +744,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::Curation,
RelationshipByAddr::Curates,
Some(txn),
)?;
}
@ -733,7 +757,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::LiveChatMessage,
RelationshipByAddr::ChatsWithin,
Some(txn),
)?;
}
@ -746,7 +770,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::BadgeAward,
RelationshipByAddr::AwardsBadge,
Some(txn),
)?;
}
@ -759,7 +783,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_addr(
ea,
event.id,
RelationshipByAddr::HandlerRecommendation,
RelationshipByAddr::RecommendsHandler,
Some(txn),
)?;
}
@ -786,7 +810,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
zapdata.id,
event.id,
RelationshipById::ZapReceipt {
RelationshipById::Zaps {
by: event.pubkey,
amount: zapdata.amount,
},
@ -795,7 +819,7 @@ pub(crate) fn process_relationships_of_event<'a>(
invalidate.push(zapdata.id);
}
Err(e) => tracing::error!("Invalid zap receipt: {}", e),
Err(e) => tracing::warn!("Invalid zap receipt: {}", e),
_ => {}
}
@ -806,7 +830,7 @@ pub(crate) fn process_relationships_of_event<'a>(
GLOBALS.storage.write_relationship_by_id(
id,
event.id,
RelationshipById::JobResult,
RelationshipById::SuppliesJobResult,
Some(txn),
)?;
}

View File

@ -1,5 +1,5 @@
/// Relationship type by Id, aliased to the latest version
pub type RelationshipById = crate::storage::types::RelationshipById1;
pub type RelationshipById = crate::storage::types::RelationshipById2;
/// Relationship type by EventAddr, aliased to the latest version
pub type RelationshipByAddr = crate::storage::types::RelationshipByAddr1;
pub type RelationshipByAddr = crate::storage::types::RelationshipByAddr2;

View File

@ -0,0 +1,38 @@
use crate::error::Error;
use crate::storage::Storage;
use heed::RwTxn;
impl Storage {
pub(super) fn m32_trigger(&self) -> Result<(), Error> {
let _ = self.db_relationships_by_id1()?;
let _ = self.db_relationships_by_id2()?;
let _ = self.db_relationships_by_addr1()?;
let _ = self.db_relationships_by_addr2()?;
Ok(())
}
pub(super) fn m32_migrate<'a>(
&'a self,
prefix: &str,
txn: &mut RwTxn<'a>,
) -> Result<(), Error> {
// Info message
tracing::info!("{prefix}: Migrating relationship data to new type...");
// Migrate
self.m32_migrate_relationship_data(txn)?;
Ok(())
}
fn m32_migrate_relationship_data<'a>(&'a self, txn: &mut RwTxn<'a>) -> Result<(), Error> {
// Clear the old relationships data
self.db_relationships_by_id1()?.clear(txn)?;
self.db_relationships_by_addr1()?.clear(txn)?;
// Rebuild relationships
self.set_flag_rebuild_relationships_needed(true, Some(txn))?;
Ok(())
}
}

View File

@ -25,6 +25,7 @@ mod m29;
mod m3;
mod m30;
mod m31;
mod m32;
mod m4;
mod m5;
mod m6;
@ -37,7 +38,7 @@ use crate::error::{Error, ErrorKind};
use heed::RwTxn;
impl Storage {
const MAX_MIGRATION_LEVEL: u32 = 31;
const MAX_MIGRATION_LEVEL: u32 = 32;
/// Initialize the database from empty
pub(super) fn init_from_empty(&self) -> Result<(), Error> {
@ -123,6 +124,7 @@ impl Storage {
29 => self.m29_trigger()?,
30 => self.m30_trigger()?,
31 => self.m31_trigger()?,
32 => self.m32_trigger()?,
_ => panic!("Unreachable migration level"),
}
@ -163,6 +165,7 @@ impl Storage {
29 => self.m29_migrate(&prefix, txn)?,
30 => self.m30_migrate(&prefix, txn)?,
31 => self.m31_migrate(&prefix, txn)?,
32 => self.m32_migrate(&prefix, txn)?,
_ => panic!("Unreachable migration level"),
};

View File

@ -42,7 +42,9 @@ mod person_lists_metadata3;
mod person_relays1;
mod relationships1;
mod relationships_by_addr1;
mod relationships_by_addr2;
mod relationships_by_id1;
mod relationships_by_id2;
mod relays1;
mod relays2;
mod reprel1;
@ -323,12 +325,12 @@ impl Storage {
#[inline]
pub(crate) fn db_relationships_by_addr(&self) -> Result<RawDatabase, Error> {
self.db_relationships_by_addr1()
self.db_relationships_by_addr2()
}
#[inline]
pub(crate) fn db_relationships_by_id(&self) -> Result<RawDatabase, Error> {
self.db_relationships_by_id1()
self.db_relationships_by_id2()
}
#[inline]
@ -1892,7 +1894,7 @@ impl Storage {
relationship_by_id: RelationshipById,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
self.write_relationship_by_id1(id, related, relationship_by_id, rw_txn)
self.write_relationship_by_id2(id, related, relationship_by_id, rw_txn)
}
/// Find relationships belonging to the given event
@ -1901,7 +1903,7 @@ impl Storage {
/// e.g. result id replies to id, or result id deletes id
#[inline]
pub fn find_relationships_by_id(&self, id: Id) -> Result<Vec<(Id, RelationshipById)>, Error> {
self.find_relationships_by_id1(id)
self.find_relationships_by_id2(id)
}
/// Write a relationship between an event and an EventAddr (replaceable)
@ -1913,7 +1915,7 @@ impl Storage {
relationship_by_addr: RelationshipByAddr,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
self.write_relationship_by_addr1(addr, related, relationship_by_addr, rw_txn)
self.write_relationship_by_addr2(addr, related, relationship_by_addr, rw_txn)
}
/// Find relationships belonging to the given event to replaceable events
@ -1922,7 +1924,7 @@ impl Storage {
&self,
addr: &EventAddr,
) -> Result<Vec<(Id, RelationshipByAddr)>, Error> {
self.find_relationships_by_addr1(addr)
self.find_relationships_by_addr2(addr)
}
/// Get replies to the given event
@ -1942,7 +1944,7 @@ impl Storage {
.find_relationships_by_id(id)?
.iter()
.filter_map(|(id, rel)| {
if *rel == RelationshipById::Reply {
if *rel == RelationshipById::RepliesTo {
Some(*id)
} else {
None
@ -1956,7 +1958,7 @@ impl Storage {
.find_relationships_by_addr(addr)?
.iter()
.filter_map(|(id, rel)| {
if *rel == RelationshipByAddr::Reply {
if *rel == RelationshipByAddr::RepliesTo {
Some(*id)
} else {
None
@ -1976,7 +1978,7 @@ impl Storage {
// Collect up to one reaction per pubkey
let mut phase1: HashMap<PublicKey, char> = HashMap::new();
for (_, rel) in self.find_relationships_by_id(id)? {
if let RelationshipById::Reaction { by, reaction } = rel {
if let RelationshipById::ReactsTo { by, reaction } = rel {
if let Some(target_event) = &maybe_target_event {
if target_event.pubkey == by {
// Do not let people like their own post
@ -2013,7 +2015,7 @@ impl Storage {
pub fn get_zap_total(&self, id: Id) -> Result<MilliSatoshi, Error> {
let mut total = MilliSatoshi(0);
for (_, rel) in self.find_relationships_by_id(id)? {
if let RelationshipById::ZapReceipt { by: _, amount } = rel {
if let RelationshipById::Zaps { by: _, amount } = rel {
total = total + amount;
}
}
@ -2025,7 +2027,7 @@ impl Storage {
let mut reasons: Vec<String> = Vec::new();
for (deleting_id, rel) in self.find_relationships_by_id(maybe_deleted_event.id)? {
if let RelationshipById::Deletion { by, reason } = rel {
if let RelationshipById::Deletes { by, reason } = rel {
if maybe_deleted_event.delete_author_allowed(by) {
// We must have the deletion event to check it
if let Some(deleting_event) = self.read_event(deleting_id)? {
@ -2048,7 +2050,7 @@ impl Storage {
};
for (deleting_id, rel) in self.find_relationships_by_addr(&addr)? {
// Must be a deletion relationship
if let RelationshipByAddr::Deletion { by, reason } = rel {
if let RelationshipByAddr::Deletes { by, reason } = rel {
if maybe_deleted_event.delete_author_allowed(by) {
// We must have the deletion event to check it
if let Some(deleting_event) = self.read_event(deleting_id)? {

View File

@ -1,10 +1,6 @@
use crate::error::Error;
use crate::storage::types::RelationshipByAddr1;
use crate::storage::{RawDatabase, Storage};
use heed::RwTxn;
use heed::{types::UnalignedSlice, DatabaseFlags};
use nostr_types::{EventAddr, Id};
use speedy::{Readable, Writable};
use std::sync::Mutex;
// Kind:Pubkey:d-tag -> RelationshipByAddr1:Id
@ -43,91 +39,4 @@ impl Storage {
}
}
}
pub(crate) fn write_relationship_by_addr1<'a>(
&'a self,
addr: EventAddr,
related: Id,
relationship_by_addr: RelationshipByAddr1,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
let key = relationships_by_addr1_into_key(&addr);
let value = relationships_by_addr1_into_value(relationship_by_addr, related)?;
let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> {
self.db_relationships_by_addr1()?.put(txn, &key, &value)?;
Ok(())
};
match rw_txn {
Some(txn) => f(txn)?,
None => {
let mut txn = self.env.write_txn()?;
f(&mut txn)?;
txn.commit()?;
}
};
Ok(())
}
pub(crate) fn find_relationships_by_addr1(
&self,
addr: &EventAddr,
) -> Result<Vec<(Id, RelationshipByAddr1)>, Error> {
let key = relationships_by_addr1_into_key(addr);
let txn = self.env.read_txn()?;
let iter = match self
.db_relationships_by_addr1()?
.get_duplicates(&txn, &key)?
{
Some(iter) => iter,
None => return Ok(vec![]),
};
let mut output: Vec<(Id, RelationshipByAddr1)> = Vec::new();
for result in iter {
let (_key, val) = result?;
let (rel, id) = relationships_by_addr1_from_value(val)?;
output.push((id, rel));
}
Ok(output)
}
}
fn relationships_by_addr1_into_key(ea: &EventAddr) -> Vec<u8> {
let u: u32 = ea.kind.into();
let mut key: Vec<u8> = u.to_be_bytes().as_slice().to_owned();
key.extend(ea.author.as_bytes());
key.extend(ea.d.as_bytes());
key
}
/*
fn relationships_by_addr1_from_key(key: &[u8]) -> Result<EventAddr, Error> {
let u = u32::from_be_bytes(key[..4].try_into().unwrap());
let kind: EventKind = u.into();
let pubkey: PublicKey = PublicKey::from_bytes(&key[4..4+32], true)?;
let d: String = String::from_utf8_lossy(&key[4+32..]).to_string();
Ok(EventAddr {
d,
relays: vec![],
kind,
author: pubkey
})
}
*/
fn relationships_by_addr1_into_value(
relationship_by_addr: RelationshipByAddr1,
id: Id,
) -> Result<Vec<u8>, Error> {
let mut value: Vec<u8> = relationship_by_addr.write_to_vec()?;
value.extend(id.as_slice());
Ok(value)
}
fn relationships_by_addr1_from_value(value: &[u8]) -> Result<(RelationshipByAddr1, Id), Error> {
let (result, len) = RelationshipByAddr1::read_with_length_from_buffer(value);
let relationship_by_addr = result?;
let id = Id(value[len..len + 32].try_into().unwrap());
Ok((relationship_by_addr, id))
}

View File

@ -0,0 +1,133 @@
use crate::error::Error;
use crate::storage::types::RelationshipByAddr2;
use crate::storage::{RawDatabase, Storage};
use heed::RwTxn;
use heed::{types::UnalignedSlice, DatabaseFlags};
use nostr_types::{EventAddr, Id};
use speedy::{Readable, Writable};
use std::sync::Mutex;
// Kind:Pubkey:d-tag -> RelationshipByAddr2:Id
// (has dups)
static RELATIONSHIPS_BY_ADDR2_DB_CREATE_LOCK: Mutex<()> = Mutex::new(());
static mut RELATIONSHIPS_BY_ADDR2_DB: Option<RawDatabase> = None;
impl Storage {
pub(super) fn db_relationships_by_addr2(&self) -> Result<RawDatabase, Error> {
unsafe {
if let Some(db) = RELATIONSHIPS_BY_ADDR2_DB {
Ok(db)
} else {
// Lock. This drops when anything returns.
let _lock = RELATIONSHIPS_BY_ADDR2_DB_CREATE_LOCK.lock();
// In case of a race, check again
if let Some(db) = RELATIONSHIPS_BY_ADDR2_DB {
return Ok(db);
}
// Create it. We know that nobody else is doing this and that
// it cannot happen twice.
let mut txn = self.env.write_txn()?;
let db = self
.env
.database_options()
.types::<UnalignedSlice<u8>, UnalignedSlice<u8>>()
.flags(DatabaseFlags::DUP_SORT) // NOT FIXED, RelationshipByAddr2 serialized isn't.
.name("relationships_by_addr2")
.create(&mut txn)?;
txn.commit()?;
RELATIONSHIPS_BY_ADDR2_DB = Some(db);
Ok(db)
}
}
}
pub(crate) fn write_relationship_by_addr2<'a>(
&'a self,
addr: EventAddr,
related: Id,
relationship_by_addr: RelationshipByAddr2,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
let key = relationships_by_addr2_into_key(&addr);
let value = relationships_by_addr2_into_value(relationship_by_addr, related)?;
let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> {
self.db_relationships_by_addr2()?.put(txn, &key, &value)?;
Ok(())
};
match rw_txn {
Some(txn) => f(txn)?,
None => {
let mut txn = self.env.write_txn()?;
f(&mut txn)?;
txn.commit()?;
}
};
Ok(())
}
pub(crate) fn find_relationships_by_addr2(
&self,
addr: &EventAddr,
) -> Result<Vec<(Id, RelationshipByAddr2)>, Error> {
let key = relationships_by_addr2_into_key(addr);
let txn = self.env.read_txn()?;
let iter = match self
.db_relationships_by_addr2()?
.get_duplicates(&txn, &key)?
{
Some(iter) => iter,
None => return Ok(vec![]),
};
let mut output: Vec<(Id, RelationshipByAddr2)> = Vec::new();
for result in iter {
let (_key, val) = result?;
let (rel, id) = relationships_by_addr2_from_value(val)?;
output.push((id, rel));
}
Ok(output)
}
}
fn relationships_by_addr2_into_key(ea: &EventAddr) -> Vec<u8> {
let u: u32 = ea.kind.into();
let mut key: Vec<u8> = u.to_be_bytes().as_slice().to_owned();
key.extend(ea.author.as_bytes());
key.extend(ea.d.as_bytes());
key
}
/*
fn relationships_by_addr2_from_key(key: &[u8]) -> Result<EventAddr, Error> {
let u = u32::from_be_bytes(key[..4].try_into().unwrap());
let kind: EventKind = u.into();
let pubkey: PublicKey = PublicKey::from_bytes(&key[4..4+32], true)?;
let d: String = String::from_utf8_lossy(&key[4+32..]).to_string();
Ok(EventAddr {
d,
relays: vec![],
kind,
author: pubkey
})
}
*/
fn relationships_by_addr2_into_value(
relationship_by_addr: RelationshipByAddr2,
id: Id,
) -> Result<Vec<u8>, Error> {
let mut value: Vec<u8> = relationship_by_addr.write_to_vec()?;
value.extend(id.as_slice());
Ok(value)
}
fn relationships_by_addr2_from_value(value: &[u8]) -> Result<(RelationshipByAddr2, Id), Error> {
let (result, len) = RelationshipByAddr2::read_with_length_from_buffer(value);
let relationship_by_addr = result?;
let id = Id(value[len..len + 32].try_into().unwrap());
Ok((relationship_by_addr, id))
}

View File

@ -1,10 +1,6 @@
use crate::error::Error;
use crate::storage::types::RelationshipById1;
use crate::storage::{RawDatabase, Storage};
use heed::types::UnalignedSlice;
use heed::RwTxn;
use nostr_types::Id;
use speedy::{Readable, Writable};
use std::sync::Mutex;
// Id:Id -> RelationshipById1
@ -50,51 +46,4 @@ impl Storage {
}
}
}
pub(crate) fn write_relationship_by_id1<'a>(
&'a self,
id: Id,
related: Id,
relationship_by_id: RelationshipById1,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
let mut key = id.as_ref().as_slice().to_owned();
key.extend(related.as_ref());
let value = relationship_by_id.write_to_vec()?;
let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> {
self.db_relationships_by_id1()?.put(txn, &key, &value)?;
Ok(())
};
match rw_txn {
Some(txn) => f(txn)?,
None => {
let mut txn = self.env.write_txn()?;
f(&mut txn)?;
txn.commit()?;
}
};
Ok(())
}
pub(crate) fn find_relationships_by_id1(
&self,
id: Id,
) -> Result<Vec<(Id, RelationshipById1)>, Error> {
let start_key = id.as_slice();
let txn = self.env.read_txn()?;
let iter = self
.db_relationships_by_id1()?
.prefix_iter(&txn, start_key)?;
let mut output: Vec<(Id, RelationshipById1)> = Vec::new();
for result in iter {
let (key, val) = result?;
let id2 = Id(key[32..64].try_into().unwrap());
let relationship_by_id = RelationshipById1::read_from_buffer(val)?;
output.push((id2, relationship_by_id));
}
Ok(output)
}
}

View File

@ -0,0 +1,100 @@
use crate::error::Error;
use crate::storage::types::RelationshipById2;
use crate::storage::{RawDatabase, Storage};
use heed::types::UnalignedSlice;
use heed::RwTxn;
use nostr_types::Id;
use speedy::{Readable, Writable};
use std::sync::Mutex;
// Id:Id -> RelationshipById2
// key: id.as_slice(), id.as_slice() | Id(val[32..64].try_into()?)
// val: relationship_by_id.write_to_vec() | RelationshipById2::read_from_buffer(val)
// NOTE: this means the SECOND Id relates to the FIRST Id, e.g.
// id2 replies to id1
// id2 reacts to id1
// id2 deletes id1
// id2 is a zap receipt on id1
static RELATIONSHIPS_BY_ID2_DB_CREATE_LOCK: Mutex<()> = Mutex::new(());
static mut RELATIONSHIPS_BY_ID2_DB: Option<RawDatabase> = None;
impl Storage {
pub(super) fn db_relationships_by_id2(&self) -> Result<RawDatabase, Error> {
unsafe {
if let Some(db) = RELATIONSHIPS_BY_ID2_DB {
Ok(db)
} else {
// Lock. This drops when anything returns.
let _lock = RELATIONSHIPS_BY_ID2_DB_CREATE_LOCK.lock();
// In case of a race, check again
if let Some(db) = RELATIONSHIPS_BY_ID2_DB {
return Ok(db);
}
// Create it. We know that nobody else is doing this and that
// it cannot happen twice.
let mut txn = self.env.write_txn()?;
let db = self
.env
.database_options()
.types::<UnalignedSlice<u8>, UnalignedSlice<u8>>()
// no .flags needed?
.name("relationships_by_id2")
.create(&mut txn)?;
txn.commit()?;
RELATIONSHIPS_BY_ID2_DB = Some(db);
Ok(db)
}
}
}
pub(crate) fn write_relationship_by_id2<'a>(
&'a self,
id: Id,
related: Id,
relationship_by_id: RelationshipById2,
rw_txn: Option<&mut RwTxn<'a>>,
) -> Result<(), Error> {
let mut key = id.as_ref().as_slice().to_owned();
key.extend(related.as_ref());
let value = relationship_by_id.write_to_vec()?;
let f = |txn: &mut RwTxn<'a>| -> Result<(), Error> {
self.db_relationships_by_id2()?.put(txn, &key, &value)?;
Ok(())
};
match rw_txn {
Some(txn) => f(txn)?,
None => {
let mut txn = self.env.write_txn()?;
f(&mut txn)?;
txn.commit()?;
}
};
Ok(())
}
pub(crate) fn find_relationships_by_id2(
&self,
id: Id,
) -> Result<Vec<(Id, RelationshipById2)>, Error> {
let start_key = id.as_slice();
let txn = self.env.read_txn()?;
let iter = self
.db_relationships_by_id2()?
.prefix_iter(&txn, start_key)?;
let mut output: Vec<(Id, RelationshipById2)> = Vec::new();
for result in iter {
let (key, val) = result?;
let id2 = Id(key[32..64].try_into().unwrap());
let relationship_by_id = RelationshipById2::read_from_buffer(val)?;
output.push((id2, relationship_by_id));
}
Ok(output)
}
}

View File

@ -25,9 +25,15 @@ pub use relationship1::Relationship1;
mod relationship_by_addr1;
pub use relationship_by_addr1::RelationshipByAddr1;
mod relationship_by_addr2;
pub use relationship_by_addr2::RelationshipByAddr2;
mod relationship_by_id1;
pub use relationship_by_id1::RelationshipById1;
mod relationship_by_id2;
pub use relationship_by_id2::RelationshipById2;
mod relay1;
pub use relay1::Relay1;

View File

@ -0,0 +1,57 @@
use super::RelationshipByAddr1;
use nostr_types::PublicKey;
use speedy::{Readable, Writable};
/// A relationship between events by Address and Id
#[derive(Clone, Debug, PartialEq, Eq, Readable, Writable)]
pub enum RelationshipByAddr2 {
// NIP-01, NIP-10 replies
RepliesTo,
// Annotation
Annotates,
// NIP-09 Event Deletion
Deletes { by: PublicKey, reason: String },
// NIP-32 Labeling
Labels { label: String, namespace: String },
// NIP-51 Lists
Bookmarks,
// NIP-51 Lists
Curates,
// communities
// interests
// emojis
// NIP-53
ChatsWithin,
// NIP-58
AwardsBadge,
// NIP-72 Moderated Communities (Reddit-style)
// CommunityPostsWithin,
// NIP-89 Recommended Application Handlers
RecommendsHandler,
}
impl From<RelationshipByAddr1> for RelationshipByAddr2 {
fn from(one: RelationshipByAddr1) -> RelationshipByAddr2 {
match one {
RelationshipByAddr1::Reply => RelationshipByAddr2::RepliesTo,
RelationshipByAddr1::Deletion { by, reason } => {
RelationshipByAddr2::Deletes { by, reason }
}
RelationshipByAddr1::ListBookmarks => RelationshipByAddr2::Bookmarks,
RelationshipByAddr1::Curation => RelationshipByAddr2::Curates,
RelationshipByAddr1::LiveChatMessage => RelationshipByAddr2::ChatsWithin,
RelationshipByAddr1::BadgeAward => RelationshipByAddr2::AwardsBadge,
RelationshipByAddr1::HandlerRecommendation => RelationshipByAddr2::RecommendsHandler,
}
}
}

View File

@ -0,0 +1,78 @@
use super::RelationshipById1;
use nostr_types::{MilliSatoshi, PublicKey};
use speedy::{Readable, Writable};
/// A relationship between events by Ids
#[derive(Clone, Debug, PartialEq, Eq, Readable, Writable)]
pub enum RelationshipById2 {
// NIP-01, NIP-10 replies
RepliesTo,
// Annotation
Annotates,
// NIP-18 Reposts
Reposts,
// NIP-18 Quotes
Quotes,
// NIP-03 OpenTimestamps Attestations for Events
Timestamps,
// NIP-09 Event Deletion
Deletes { by: PublicKey, reason: String },
// NIP-25 Reactions
ReactsTo { by: PublicKey, reaction: String },
// NIP-32 Labeling
Labels { label: String, namespace: String },
// NIP-51 Lists
Mutes,
// NIP-51 Lists
Pins,
// NIP-51 Lists
Bookmarks,
// NIP-51 Lists
Curates,
// NIP-56 Reporting
Reports(String),
// NIP-57 Lightning Zaps
Zaps { by: PublicKey, amount: MilliSatoshi },
// NIP-72 Moderated Communities (Reddit-style)
// Approves { in_community: EventAddr },
// NIP-90 Data Vending Machines
SuppliesJobResult,
}
impl From<RelationshipById1> for RelationshipById2 {
fn from(one: RelationshipById1) -> RelationshipById2 {
match one {
RelationshipById1::Reply => RelationshipById2::RepliesTo,
RelationshipById1::Timestamp => RelationshipById2::Timestamps,
RelationshipById1::Deletion { by, reason } => RelationshipById2::Deletes { by, reason },
RelationshipById1::Reaction { by, reaction } => {
RelationshipById2::ReactsTo { by, reaction }
}
RelationshipById1::Labels { label, namespace } => {
RelationshipById2::Labels { label, namespace }
}
RelationshipById1::ListMutesThread => RelationshipById2::Mutes,
RelationshipById1::ListPins => RelationshipById2::Pins,
RelationshipById1::ListBookmarks => RelationshipById2::Bookmarks,
RelationshipById1::Curation => RelationshipById2::Curates,
RelationshipById1::Reports(s) => RelationshipById2::Reports(s),
RelationshipById1::ZapReceipt { by, amount } => RelationshipById2::Zaps { by, amount },
RelationshipById1::JobResult => RelationshipById2::SuppliesJobResult,
}
}
}