mirror of
https://github.com/matrix-org/sliding-sync.git
synced 2025-03-10 13:37:11 +00:00
PendingTransactionIDs: defer mutex unlock
This commit is contained in:
parent
6c81134056
commit
210e95b0d8
@ -35,7 +35,7 @@ type loaderFunc func(userID string) (deviceIDs []string)
|
|||||||
// To avoid the map growing without bound, we use a ttlcache and drop entries
|
// To avoid the map growing without bound, we use a ttlcache and drop entries
|
||||||
// after a short period of time.
|
// after a short period of time.
|
||||||
type PendingTransactionIDs struct {
|
type PendingTransactionIDs struct {
|
||||||
// mu guards the pending field.
|
// mu guards the pending field. See MissingTxnID for rationale.
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pending *ttlcache.Cache
|
pending *ttlcache.Cache
|
||||||
// loader should provide the list of device IDs
|
// loader should provide the list of device IDs
|
||||||
@ -57,6 +57,24 @@ func NewPendingTransactionIDs(loader loaderFunc) *PendingTransactionIDs {
|
|||||||
// transaction ID for this event ID. Returns true if this is the first time we know
|
// transaction ID for this event ID. Returns true if this is the first time we know
|
||||||
// for sure that we'll never see a txn ID for this event.
|
// for sure that we'll never see a txn ID for this event.
|
||||||
func (c *PendingTransactionIDs) MissingTxnID(eventID, userID, myDeviceID string) (bool, error) {
|
func (c *PendingTransactionIDs) MissingTxnID(eventID, userID, myDeviceID string) (bool, error) {
|
||||||
|
// While ttlcache is threadsafe, it does not provide a way to atomically update
|
||||||
|
// (get+set) a value, which means we are still open to races. For example:
|
||||||
|
//
|
||||||
|
// - We have three pollers A, B, C.
|
||||||
|
// - Poller A sees an event without txn id and calls MissingTxnID.
|
||||||
|
// - `c.pending.Get()` fails, so we load up all device IDs: [A, B, C].
|
||||||
|
// - Then `c.pending.Set()` with [B, C].
|
||||||
|
// - Poller B sees the same event, also missing txn ID and calls MissingTxnID.
|
||||||
|
// - Poller C does the same concurrently.
|
||||||
|
//
|
||||||
|
// If the Get+Set isn't atomic, then we might do e.g.
|
||||||
|
// - B gets [B, C] and prepares to write [C].
|
||||||
|
// - C gets [B, C] and prepares to write [B].
|
||||||
|
// - Last writer wins. Either way, we never write [] and so never return true
|
||||||
|
// (the all-clear signal.)
|
||||||
|
//
|
||||||
|
// This wouldn't be the end of the world (the API process has a maximum delay, and
|
||||||
|
// the ttlcache will expire the entry), but it would still be nice to avoid it.
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
@ -86,7 +104,7 @@ func (c *PendingTransactionIDs) MissingTxnID(eventID, userID, myDeviceID string)
|
|||||||
// for this event.
|
// for this event.
|
||||||
func (c *PendingTransactionIDs) SeenTxnID(eventID string) error {
|
func (c *PendingTransactionIDs) SeenTxnID(eventID string) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
return c.pending.Set(eventID, []string{})
|
return c.pending.Set(eventID, []string{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user