nostrdb: add profiles to nostrdb

This adds profiles to nostrdb

- Remove in-memory Profiles caches, nostrdb is as fast as an in-memory cache
- Remove ProfileDatabase and just use nostrdb directly

Changelog-Changed: Use nostrdb for profiles
This commit is contained in:
William Casarin
2023-08-28 07:52:59 -07:00
parent 8586eed635
commit bb4fd75576
42 changed files with 362 additions and 705 deletions

View File

@@ -15,72 +15,52 @@ class ValidationModel: ObservableObject {
}
}
class ProfileDataModel: ObservableObject {
@Published var profile: TimestampedProfile?
init() {
self.profile = nil
}
}
class ProfileData {
var status: UserStatusModel
var profile_model: ProfileDataModel
var validation_model: ValidationModel
var zapper: Pubkey?
init() {
status = .init()
profile_model = .init()
validation_model = .init()
zapper = nil
}
}
class Profiles {
static let db_freshness_threshold: TimeInterval = 24 * 60 * 60
/// This queue is used to synchronize access to the profiles dictionary, which
/// prevents data races from crashing the app.
private var profiles_queue = DispatchQueue(label: "io.damus.profiles",
qos: .userInteractive,
attributes: .concurrent)
private var ndb: Ndb
private var validated_queue = DispatchQueue(label: "io.damus.profiles.validated",
qos: .userInteractive,
attributes: .concurrent)
static let db_freshness_threshold: TimeInterval = 24 * 60 * 60
@MainActor
private var profiles: [Pubkey: ProfileData] = [:]
@MainActor
var nip05_pubkey: [String: Pubkey] = [:]
private let database = ProfileDatabase()
let user_search_cache: UserSearchCache
init(user_search_cache: UserSearchCache) {
init(user_search_cache: UserSearchCache, ndb: Ndb) {
self.user_search_cache = user_search_cache
self.ndb = ndb
}
@MainActor
func is_validated(_ pk: Pubkey) -> NIP05? {
validated_queue.sync {
self.profile_data(pk).validation_model.validated
}
self.profile_data(pk).validation_model.validated
}
@MainActor
func invalidate_nip05(_ pk: Pubkey) {
validated_queue.async(flags: .barrier) {
self.profile_data(pk).validation_model.validated = nil
}
self.profile_data(pk).validation_model.validated = nil
}
@MainActor
func set_validated(_ pk: Pubkey, nip05: NIP05?) {
validated_queue.async(flags: .barrier) {
self.profile_data(pk).validation_model.validated = nip05
}
self.profile_data(pk).validation_model.validated = nip05
}
@MainActor
func profile_data(_ pubkey: Pubkey) -> ProfileData {
guard let data = profiles[pubkey] else {
let data = ProfileData()
@@ -91,60 +71,28 @@ class Profiles {
return data
}
@MainActor
func lookup_zapper(pubkey: Pubkey) -> Pubkey? {
profile_data(pubkey).zapper
}
func add(id: Pubkey, profile: TimestampedProfile) {
profiles_queue.async(flags: .barrier) {
let old_timestamped_profile = self.profile_data(id).profile_model.profile
self.profile_data(id).profile_model.profile = profile
self.user_search_cache.updateProfile(id: id, profiles: self, oldProfile: old_timestamped_profile?.profile, newProfile: profile.profile)
}
Task {
do {
try await database.upsert(id: id, profile: profile.profile, last_update: Date(timeIntervalSince1970: TimeInterval(profile.timestamp)))
} catch {
print("⚠️ Warning: Profiles failed to save a profile: \(error)")
}
}
func lookup_with_timestamp(_ pubkey: Pubkey) -> ProfileRecord? {
return ndb.lookup_profile(pubkey)
}
func lookup(id: Pubkey) -> Profile? {
var profile: Profile?
profiles_queue.sync {
profile = self.profile_data(id).profile_model.profile?.profile
}
return profile ?? database.get(id: id)
return ndb.lookup_profile(id)?.profile
}
func lookup_with_timestamp(id: Pubkey) -> TimestampedProfile? {
profiles_queue.sync {
return self.profile_data(id).profile_model.profile
}
}
func has_fresh_profile(id: Pubkey) -> Bool {
var profile: Profile?
profiles_queue.sync {
profile = self.profile_data(id).profile_model.profile?.profile
}
if profile != nil {
return true
}
// check memory first
return false
// then disk
guard let pull_date = database.get_network_pull_date(id: id) else {
return false
}
return Date.now.timeIntervalSince(pull_date) < Profiles.db_freshness_threshold
guard let profile = lookup_with_timestamp(id) else { return false }
return Date.now.timeIntervalSince(Date(timeIntervalSince1970: Double(profile.receivedAt))) < Profiles.db_freshness_threshold
}
}
@MainActor
func invalidate_zapper_cache(pubkey: Pubkey, profiles: Profiles, lnurl: LNUrls) {
profiles.profile_data(pubkey).zapper = nil
lnurl.endpoints.removeValue(forKey: pubkey)