Skip to content

Commit

Permalink
Remember multiple recent transaction IDs for deduplication
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Aug 30, 2021
1 parent 9970efd commit 5a2fad3
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 4 deletions.
3 changes: 2 additions & 1 deletion appservice/appservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func Create() *AppService {
StateStore: NewBasicStateStore(),
Router: mux.NewRouter(),
UserAgent: mautrix.DefaultUserAgent,
txnIDC: NewTransactionIDCache(128),
Live: true,
Ready: false,
}
Expand Down Expand Up @@ -90,7 +91,7 @@ type AppService struct {
Registration *Registration `yaml:"-"`
Log maulogger.Logger `yaml:"-"`

lastProcessedTransaction string
txnIDC *TransactionIDCache

Events chan *event.Event `yaml:"-"`
DeviceLists chan *mautrix.DeviceLists `yaml:"-"`
Expand Down
4 changes: 2 additions & 2 deletions appservice/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (as *AppService) PutTransaction(w http.ResponseWriter, r *http.Request) {
}.Write(w)
return
}
if as.lastProcessedTransaction == txnID {
if as.txnIDC.IsProcessed(txnID) {
// Duplicate transaction ID: no-op
WriteBlankOK(w)
return
Expand All @@ -134,8 +134,8 @@ func (as *AppService) PutTransaction(w http.ResponseWriter, r *http.Request) {
} else {
as.handleTransaction(&txn)
WriteBlankOK(w)
as.txnIDC.MarkProcessed(txnID)
}
as.lastProcessedTransaction = txnID
}

func (as *AppService) handleTransaction(txn *Transaction) {
Expand Down
43 changes: 43 additions & 0 deletions appservice/txnid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package appservice

import "sync"

type TransactionIDCache struct {
array []string
arrayPtr int
hash map[string]struct{}
lock sync.RWMutex
}

func NewTransactionIDCache(size int) *TransactionIDCache {
return &TransactionIDCache{
array: make([]string, size),
hash: make(map[string]struct{}),
}
}

func (txnIDC *TransactionIDCache) IsProcessed(txnID string) bool {
txnIDC.lock.RLock()
_, exists := txnIDC.hash[txnID]
txnIDC.lock.RUnlock()
return exists
}

func (txnIDC *TransactionIDCache) MarkProcessed(txnID string) {
txnIDC.lock.Lock()
txnIDC.hash[txnID] = struct{}{}
if txnIDC.array[txnIDC.arrayPtr] != "" {
for i := 0; i < len(txnIDC.array)/8; i++ {
delete(txnIDC.hash, txnIDC.array[txnIDC.arrayPtr+i])
txnIDC.array[txnIDC.arrayPtr+i] = ""
}
}
txnIDC.array[txnIDC.arrayPtr] = txnID
txnIDC.lock.Unlock()
}
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package mautrix

const Version = "v0.9.21"
const Version = "v0.9.22"

var DefaultUserAgent = "mautrix-go/" + Version

0 comments on commit 5a2fad3

Please sign in to comment.