Files
proton-bridge/internal/services/imapservice/shared_cache.go
Leander Beernaert 41c125f65e feat(GODT-2803): Gluon IMAP State access
Update to latest Gluon to allow access to the database for bridge. The
cache is placed in a `SharedCache` type to ensure that we respect calls
to `Connector.Close`.

In theory, this should never trigger an error, but this way we can catch
it if it happens.

https://github.com/ProtonMail/gluon/pull/391
2023-08-14 09:51:49 +02:00

88 lines
2.3 KiB
Go

// Copyright (c) 2023 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package imapservice
import (
"context"
"errors"
"sync"
"github.com/ProtonMail/gluon/connector"
)
type CacheAccessor interface {
connector.IMAPState
Close()
}
// SharedCache is meant to protect access to the database and guarantee it's always valid. There may be some corner
// cases where the Gluon connector can get closed while we are processing events in parallel. If for some reason
// Gluon closes the database, the instance is invalidated and any attempts to access this state will return
// `ErrCacheNotAvailable`.
type SharedCache struct {
cache connector.IMAPState
lock sync.RWMutex
}
func NewSharedCached() *SharedCache {
return &SharedCache{}
}
var ErrCacheNotAvailable = errors.New("cache no longer available")
func (s *SharedCache) Set(cache connector.IMAPState) {
s.lock.Lock()
defer s.lock.Unlock()
s.cache = cache
}
func (s *SharedCache) Close() {
s.lock.Lock()
defer s.lock.Unlock()
s.cache = nil
}
func (s *SharedCache) Acquire() (CacheAccessor, error) {
s.lock.RLock()
if s.cache == nil {
s.lock.RUnlock()
return nil, ErrCacheNotAvailable
}
return &cacheAccessor{sharedCache: s}, nil
}
type cacheAccessor struct {
sharedCache *SharedCache
}
func (c cacheAccessor) Read(ctx context.Context, f func(context.Context, connector.IMAPStateRead) error) error {
return c.sharedCache.cache.Read(ctx, f)
}
func (c cacheAccessor) Write(ctx context.Context, f func(context.Context, connector.IMAPStateWrite) error) error {
return c.sharedCache.cache.Write(ctx, f)
}
func (c cacheAccessor) Close() {
c.sharedCache.lock.RUnlock()
}