-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix V2Typing send multiple times #214
Changes from 12 commits
2840d0d
ca263b2
e566158
8dc8d48
3a2001f
7c83d7e
86c4f18
d55880f
1b1d00d
a283715
d2cf57d
d357aa2
5846873
2ced198
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
package handler2_test | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"os" | ||
"reflect" | ||
"sync" | ||
|
@@ -97,12 +99,17 @@ func (p *mockPub) WaitForPayloadType(t string) chan struct{} { | |
return ch | ||
} | ||
|
||
func (p *mockPub) DoWait(t *testing.T, errMsg string, ch chan struct{}) { | ||
func (p *mockPub) DoWait(t *testing.T, errMsg string, ch chan struct{}, wantTimeOut bool) { | ||
select { | ||
case <-ch: | ||
if wantTimeOut { | ||
t.Fatalf("expected to timeout, but received on channel") | ||
} | ||
return | ||
case <-time.After(time.Second): | ||
t.Fatalf("DoWait: timed out waiting: %s", errMsg) | ||
if !wantTimeOut { | ||
t.Fatalf("DoWait: timed out waiting: %s", errMsg) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -160,7 +167,7 @@ func TestHandlerFreshEnsurePolling(t *testing.T) { | |
DeviceID: deviceID, | ||
AccessTokenHash: tok.AccessTokenHash, | ||
}) | ||
pub.DoWait(t, "didn't see V2InitialSyncComplete", ch) | ||
pub.DoWait(t, "didn't see V2InitialSyncComplete", ch, false) | ||
|
||
// make sure we polled with the token i.e it did a db hit | ||
pMap.assertCallExists(t, pollInfo{ | ||
|
@@ -174,3 +181,44 @@ func TestHandlerFreshEnsurePolling(t *testing.T) { | |
}) | ||
|
||
} | ||
|
||
func TestSetTypingConcurrently(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't what I'm interested in testing. The case here should be passing without your change. The case I'm interested in is when you have 2 pollers receiving delayed typing notifs. For example. if alice starts typing then stops typing (so [A] then []) the problem is that 1 poller may be "behind" the other, such that it has yet to see [A] whilst the other "ahead" poller has already seen [A] and []. In this scenario, we flicker with 4 operations instead of 2, as we go [A], [], [A], [], which this test is not testing, nor does the code fix. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It doesn't always pass. With luck it does, yea, if the machine is slow enough to execute both calls.
which also means that |
||
store := state.NewStorage(postgresURI) | ||
v2Store := sync2.NewStore(postgresURI, "secret") | ||
pMap := &mockPollerMap{} | ||
pub := newMockPub() | ||
sub := &mockSub{} | ||
h, err := handler2.NewHandler(pMap, v2Store, store, pub, sub, false, time.Minute) | ||
assertNoError(t, err) | ||
ctx := context.Background() | ||
|
||
roomID := "!typing:localhost" | ||
|
||
typingType := pubsub.V2Typing{} | ||
|
||
// startSignal is used to synchronize calling SetTyping | ||
startSignal := make(chan struct{}) | ||
// Call SetTyping twice, this may happen with pollers for the same user | ||
go func() { | ||
<-startSignal | ||
h.SetTyping(ctx, sync2.PollerID{UserID: "@alice", DeviceID: "aliceDevice"}, roomID, json.RawMessage(`{"content":{"user_ids":["@alice:localhost"]}}`)) | ||
}() | ||
go func() { | ||
<-startSignal | ||
h.SetTyping(ctx, sync2.PollerID{UserID: "@bob", DeviceID: "bobDevice"}, roomID, json.RawMessage(`{"content":{"user_ids":["@alice:localhost"]}}`)) | ||
}() | ||
|
||
close(startSignal) | ||
|
||
// Wait for the event to be published | ||
ch := pub.WaitForPayloadType(typingType.Type()) | ||
pub.DoWait(t, "didn't see V2Typing", ch, false) | ||
ch = pub.WaitForPayloadType(typingType.Type()) | ||
// Wait again, but this time we expect to timeout. | ||
pub.DoWait(t, "saw unexpected V2Typing", ch, true) | ||
|
||
// We expect only one call to Notify, as the hashes should match | ||
if gotCalls := len(pub.calls); gotCalls != 1 { | ||
t.Fatalf("expected only one call to notify, got %d", gotCalls) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though I couldn't get the test fail in CI, this was causing a race condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the bits which touch
typingMap
ortypingDeviceHandler
should be protected.