diff --git a/service/adapters/relay_source.go b/service/adapters/relay_source.go index 7c366cf..0905f96 100644 --- a/service/adapters/relay_source.go +++ b/service/adapters/relay_source.go @@ -2,6 +2,8 @@ package adapters import ( "context" + "sync" + "time" "github.com/boreq/errors" "github.com/planetary-social/nos-crossposting-service/internal" @@ -9,6 +11,8 @@ import ( "github.com/planetary-social/nos-crossposting-service/service/domain" ) +const refreshPurplePagesAfter = 30 * time.Minute + var hardcodedRelayAddresses = []domain.RelayAddress{ domain.MustNewRelayAddress("wss://relay.damus.io"), domain.MustNewRelayAddress("wss://nos.lol"), @@ -18,10 +22,15 @@ var hardcodedRelayAddresses = []domain.RelayAddress{ type RelaySource struct { logger logging.Logger purplePages *PurplePages + cache *RelayAddressCache } func NewRelaySource(logger logging.Logger, purplePages *PurplePages) *RelaySource { - return &RelaySource{logger: logger, purplePages: purplePages} + return &RelaySource{ + logger: logger, + purplePages: purplePages, + cache: NewRelayAddressCache(), + } } func (p RelaySource) GetRelays(ctx context.Context, publicKey domain.PublicKey) ([]domain.RelayAddress, error) { @@ -31,18 +40,71 @@ func (p RelaySource) GetRelays(ctx context.Context, publicKey domain.PublicKey) result.Put(relayAddress) } + relayAddressesFromPurplePages, err := p.getRelaysFromPurplePages(ctx, publicKey) + if err != nil { + return nil, errors.Wrap(err, "error getting relays from purple pages") + } + + for _, relayAddress := range relayAddressesFromPurplePages { + result.Put(relayAddress) + } + + return result.List(), nil +} + +func (p RelaySource) getRelaysFromPurplePages(ctx context.Context, publicKey domain.PublicKey) ([]domain.RelayAddress, error) { + var previousEntries []domain.RelayAddress + + entry, ok := p.cache.Get(publicKey) + if ok { + previousEntries = entry.Addresses + if time.Since(entry.T) < refreshPurplePagesAfter { + return previousEntries, nil + } + } + relayAddressesFromPurplePages, err := p.purplePages.GetRelays(ctx, publicKey) if err != nil { if errors.Is(err, ErrRelayListNotFoundInPurplePages) || errors.Is(err, ErrPurplePagesTimeout) { - return result.List(), nil + return previousEntries, nil } return nil, errors.Wrap(err, "error querying purple pages") } - for _, relayAddress := range relayAddressesFromPurplePages { - result.Put(relayAddress) + p.cache.Set(publicKey, relayAddressesFromPurplePages) + + return relayAddressesFromPurplePages, nil +} + +type RelayAddressCache struct { + m map[domain.PublicKey]Entry + lock sync.Mutex +} + +func NewRelayAddressCache() *RelayAddressCache { + return &RelayAddressCache{m: make(map[domain.PublicKey]Entry)} +} + +func (c *RelayAddressCache) Set(publicKey domain.PublicKey, addresses []domain.RelayAddress) { + c.lock.Lock() + defer c.lock.Unlock() + + c.m[publicKey] = Entry{ + T: time.Now(), + Addresses: addresses, } +} - return result.List(), nil +func (c *RelayAddressCache) Get(publicKey domain.PublicKey) (Entry, bool) { + c.lock.Lock() + defer c.lock.Unlock() + + v, ok := c.m[publicKey] + return v, ok +} + +type Entry struct { + T time.Time + Addresses []domain.RelayAddress } diff --git a/service/app/downloader.go b/service/app/downloader.go index e133cec..43428aa 100644 --- a/service/app/downloader.go +++ b/service/app/downloader.go @@ -16,7 +16,7 @@ const ( howFarIntoThePastToLook = 24 * time.Hour storeMetricsEvery = 30 * time.Second refreshDownloaderPublicKeysEvery = 1 * time.Minute - refreshPublicKeyDownloaderRelaysEvery = 60 * time.Minute + refreshPublicKeyDownloaderRelaysEvery = 1 * time.Minute ) type ReceivedEventPublisher interface {