Unverified Commit 7429c3ee authored by Andrei Mihu's avatar Andrei Mihu Committed by GitHub
Browse files

Add matchmaker option to enforce a multiple of resulting matched count. (#811)

parent c475ac80
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Add Groups page and associated endpoints to the developer console.
- Add NotificationSendAll function to the runtimes, for sending a notification to all users.
- Log a warning when client IP address cannot be resolved.
- Add matchmaker option to enforce a multiple of resulting matched count.
- Add tagged Prometheus stats containing RPC function identifiers.

### Changed
+109 −31
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ import (
	"context"
	"fmt"
	"math"
	"sort"
	"sync"
	"time"

@@ -98,14 +99,57 @@ type MatchmakerIndex struct {
	// Parameters used for correctly processing various matchmaker operations, but not indexed for searching.
	Query         string              `json:"-"`
	Count         int                 `json:"-"`
	CountMultiple int                 `json:"-"`
	SessionID     string              `json:"-"`
	Intervals     int                 `json:"-"`
	SessionIDs    map[string]struct{} `json:"-"`
}

type MatchmakerIndexGroup struct {
	indexes      []*MatchmakerIndex
	avgCreatedAt int64
}

func groupIndexes(indexes []*MatchmakerIndex, required int) []*MatchmakerIndexGroup {
	if len(indexes) == 0 || required == 0 {
		return nil
	}

	var results []*MatchmakerIndexGroup
	for i := 0; i < len(indexes); i++ {
		// Grab index combination not including the current index.
		current, before, after := indexes[i], indexes[:i], indexes[i+1:]
		others := make([]*MatchmakerIndex, len(before)+len(after))
		copy(others, before)
		copy(others[len(before):], after)

		if current.Count == required {
			// 1. The current index by itself satisfies the requirement.
			results = append(results, &MatchmakerIndexGroup{
				indexes:      []*MatchmakerIndex{current},
				avgCreatedAt: current.CreatedAt,
			})
		} else {
			// 2. The current index plus some combination(s) of the others.
			fillResults := groupIndexes(others, required-current.Count)
			for _, fillResult := range fillResults {
				indexesCount := int64(len(fillResult.indexes))
				fillResult.avgCreatedAt = (fillResult.avgCreatedAt*indexesCount + current.CreatedAt) / (indexesCount + 1)
				fillResult.indexes = append(fillResult.indexes, current)
				results = append(results, fillResult)
			}
		}

		// 3. Other combinations not including the current index.
		results = append(results, groupIndexes(others, required)...)
	}

	return results
}

type Matchmaker interface {
	Stop()
	Add(presences []*MatchmakerPresence, sessionID, partyId, query string, minCount, maxCount int, stringProperties map[string]string, numericProperties map[string]float64) (string, error)
	Add(presences []*MatchmakerPresence, sessionID, partyId, query string, minCount, maxCount, countMultiple int, stringProperties map[string]string, numericProperties map[string]float64) (string, error)
	RemoveSession(sessionID, ticket string) error
	RemoveSessionAll(sessionID string) error
	RemoveParty(partyID, ticket string) error
@@ -260,6 +304,7 @@ func (m *LocalMatchmaker) process(batch *index.Batch) {

		// Form possible combinations, in case multiple matches might be suitable.
		entryCombos := make([][]*MatchmakerEntry, 0, 5)
		lastHitCounter := len(blugeMatches.Hits) - 1
		for hitCounter, hit := range blugeMatches.Hits {
			if hit.ID == ticket {
				// Skip the current ticket.
@@ -324,10 +369,10 @@ func (m *LocalMatchmaker) process(batch *index.Batch) {
							break
						} else if !entryMatchesSearchHitQuery {
							mutualMatchConflict = true
							// this search hit is not a mutual match with the outer ticket
							// This search hit is not a mutual match with the outer ticket.
							break
						}
						// MatchmakerEntry's do not have the query, have to dig it back out of indexes
						// MatchmakerEntry does not have the query,read it out of indexes.
						if entriesIndexEntry, ok := m.indexes[entry.Ticket]; ok {
							searchHitMatchesEntryQuery, err := validateMatch(m.ctx, indexReader, entriesIndexEntry.Query, hit.ID)
							if err != nil {
@@ -336,7 +381,7 @@ func (m *LocalMatchmaker) process(batch *index.Batch) {
								break
							} else if !searchHitMatchesEntryQuery {
								mutualMatchConflict = true
								// this search hit is not a mutual match with the outer ticket
								// This search hit is not a mutual match with the outer ticket.
								break
							}
						} else {
@@ -369,29 +414,61 @@ func (m *LocalMatchmaker) process(batch *index.Batch) {
			// * It is the last interval for this active index.
			// * The combo at least satisfies the min count.
			// * The combo does not exceed the max count.
			// * There are no further hits that may further fill the found combo, so we get as close as possible to the max count.
			if l := len(foundCombo) + index.Count; l == index.MaxCount || (lastInterval && l >= index.MinCount && l <= index.MaxCount && hitCounter >= len(blugeMatches.Hits)-1) {
				// Check that the minimum count that satisfies the current index is also good enough for all matched entries.
				var minCountFailed bool
			// * There are no more hits that may further fill the found combo, so we get as close as possible to the max count.
			if l := len(foundCombo) + index.Count; l == index.MaxCount || (lastInterval && l >= index.MinCount && l <= index.MaxCount && hitCounter >= lastHitCounter) {
				if rem := l % index.CountMultiple; rem != 0 {
					// The size of the combination being considered does not satisfy the count multiple.
					// Attempt to adjust the combo by removing the smallest possible number of entries.
					// Prefer keeping entries that have been in the matchmaker the longest, if possible.
					eligibleIndexes := make([]*MatchmakerIndex, 0, len(foundCombo))
					for _, e := range foundCombo {
					if foundIndex, ok := m.indexes[e.Ticket]; ok && foundIndex.MinCount > l {
						minCountFailed = true
						// Only tickets individually less <= the removable size are considered.
						// For example removing a party of 3 when we're only looking to remove 2 is not allowed.
						if foundIndex, ok := m.indexes[e.Ticket]; ok && foundIndex.Count <= rem {
							eligibleIndexes = append(eligibleIndexes, foundIndex)
						}
					}

					eligibleGroups := groupIndexes(eligibleIndexes, rem)
					if len(eligibleGroups) <= 0 {
						// No possible combination to remove, unlikely but guard.
						continue
					}
					// Sort to ensure we keep as many of the longest-waiting tickets as possible.
					sort.Slice(eligibleGroups, func(i, j int) bool {
						return eligibleGroups[i].avgCreatedAt < eligibleGroups[j].avgCreatedAt
					})
					// The most eligible group is removed from the combo.
					for _, egIndex := range eligibleGroups[0].indexes {
						for i := 0; i < len(foundCombo); i++ {
							if egIndex.Ticket == foundCombo[i].Ticket {
								foundCombo[i] = foundCombo[len(foundCombo)-1]
								foundCombo[len(foundCombo)-1] = nil
								foundCombo = foundCombo[:len(foundCombo)-1]
								break
							}
						}
				if minCountFailed {
					}

					if (len(foundCombo)+index.Count)%index.CountMultiple != 0 {
						// Removal was insufficient, the combo is still not valid for the required multiple.
						continue
					}
				}

				// Check that the maximum count that satisfies the current index is also good enough for all matched entries.
				var maxCountFailed bool
				// Check that ALL of these conditions are true for ALL matched entries:
				// * The found combo size satisfies the minimum count.
				// * The found combo size satisfies the maximum count.
				// * The found combo size satisfies the count multiple.
				// For any condition failures it does not matter which specific condition is not met.
				var conditionFailed bool
				for _, e := range foundCombo {
					if foundIndex, ok := m.indexes[e.Ticket]; ok && foundIndex.MaxCount < l {
						maxCountFailed = true
					if foundIndex, ok := m.indexes[e.Ticket]; ok && (foundIndex.MinCount > l || foundIndex.MaxCount < l || l%foundIndex.CountMultiple != 0) {
						conditionFailed = true
						break
					}
				}
				if maxCountFailed {
				if conditionFailed {
					continue
				}

@@ -507,7 +584,7 @@ func (m *LocalMatchmaker) process(batch *index.Batch) {
	}
}

func (m *LocalMatchmaker) Add(presences []*MatchmakerPresence, sessionID, partyId, query string, minCount, maxCount int, stringProperties map[string]string, numericProperties map[string]float64) (string, error) {
func (m *LocalMatchmaker) Add(presences []*MatchmakerPresence, sessionID, partyId, query string, minCount, maxCount, countMultiple int, stringProperties map[string]string, numericProperties map[string]float64) (string, error) {
	// Check if the matchmaker has been stopped.
	if m.stopped.Load() {
		return "", runtime.ErrMatchmakerNotAvailable
@@ -552,6 +629,7 @@ func (m *LocalMatchmaker) Add(presences []*MatchmakerPresence, sessionID, partyI

		Query:         query,
		Count:         len(presences),
		CountMultiple: countMultiple,
		SessionID:     sessionID,
		Intervals:     0,
		SessionIDs:    sessionIDs,
+68 −58
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ func TestMatchmakerAddOnly(t *testing.T) {
			Node:      "a",
			SessionID: sessionID,
		},
	}, sessionID.String(), "", "properties.a1:foo", 2, 2, map[string]string{
	}, sessionID.String(), "", "properties.a1:foo", 2, 2, 1, map[string]string{
		"a1": "bar",
	}, map[string]float64{})
	if err != nil {
@@ -77,7 +77,7 @@ func TestMatchmakerAddAndRemove(t *testing.T) {
			Node:      "a",
			SessionID: sessionID,
		},
	}, sessionID.String(), "", "properties.a1:foo", 2, 2, map[string]string{
	}, sessionID.String(), "", "properties.a1:foo", 2, 2, 1, map[string]string{
		"a1": "bar",
	}, map[string]float64{})
	if err != nil {
@@ -117,7 +117,7 @@ func TestMatchmakerAddWithBasicMatch(t *testing.T) {
			Node:      "a",
			SessionID: sessionID,
		},
	}, sessionID.String(), "", "properties.a3:bar", 2, 2, map[string]string{
	}, sessionID.String(), "", "properties.a3:bar", 2, 2, 1, map[string]string{
		"a3": "baz",
	}, map[string]float64{})
	if err != nil {
@@ -136,7 +136,7 @@ func TestMatchmakerAddWithBasicMatch(t *testing.T) {
			Node:      "b",
			SessionID: sessionID2,
		},
	}, sessionID2.String(), "", "properties.a3:baz", 2, 2, map[string]string{
	}, sessionID2.String(), "", "properties.a3:baz", 2, 2, 1, map[string]string{
		"a3": "bar",
	}, map[string]float64{})
	if err != nil {
@@ -161,7 +161,7 @@ func TestMatchmakerAddWithBasicMatch(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -189,7 +189,7 @@ func TestMatchmakerAddWithBasicMatch(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -231,7 +231,7 @@ func TestMatchmakerAddWithMatchOnStar(t *testing.T) {
		},
	}, sessionID.String(), "",
		"*",
		2, 2, map[string]string{},
		2, 2, 1, map[string]string{},
		map[string]float64{
			"b1": 15,
		})
@@ -250,7 +250,7 @@ func TestMatchmakerAddWithMatchOnStar(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"*",
		2, 2, map[string]string{},
		2, 2, 1, map[string]string{},
		map[string]float64{
			"b1": 15,
		})
@@ -279,7 +279,7 @@ func TestMatchmakerAddWithMatchOnStar(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -307,7 +307,7 @@ func TestMatchmakerAddWithMatchOnStar(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -349,7 +349,7 @@ func TestMatchmakerAddWithMatchOnRange(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.b1:>=10 +properties.b1:<=20",
		2, 2, map[string]string{},
		2, 2, 1, map[string]string{},
		map[string]float64{
			"b1": 15,
		})
@@ -368,7 +368,7 @@ func TestMatchmakerAddWithMatchOnRange(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.b1:>=10 +properties.b1:<=20",
		2, 2, map[string]string{},
		2, 2, 1, map[string]string{},
		map[string]float64{
			"b1": 15,
		})
@@ -397,7 +397,7 @@ func TestMatchmakerAddWithMatchOnRange(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -425,7 +425,7 @@ func TestMatchmakerAddWithMatchOnRange(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -467,7 +467,7 @@ func TestMatchmakerAddWithMatchOnRangeAndValue(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.b1:>=10 +properties.b1:<=20",
		2, 2,
		2, 2, 1,
		map[string]string{
			"c2": "foo",
		},
@@ -492,7 +492,7 @@ func TestMatchmakerAddWithMatchOnRangeAndValue(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.c1:>=10 +properties.c1:<=20 +properties.c2:foo",
		2, 2,
		2, 2, 1,
		map[string]string{
			"c2": "foo",
		},
@@ -521,7 +521,7 @@ func TestMatchmakerAddWithMatchOnRangeAndValue(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -549,7 +549,7 @@ func TestMatchmakerAddWithMatchOnRangeAndValue(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -594,7 +594,7 @@ func TestMatchmakerAddRemoveNotMatch(t *testing.T) {
		},
	}, sessionID.String(), "",
		"properties.a3:bar",
		2, 2, map[string]string{
		2, 2, 1, map[string]string{
			"a3": "baz",
		}, map[string]float64{})
	if err != nil {
@@ -642,7 +642,7 @@ func TestMatchmakerAddButNotMatch(t *testing.T) {
		},
	}, sessionID.String(), "",
		"properties.a5:bar",
		2, 2,
		2, 2, 1,
		map[string]string{
			"a5": "baz",
		},
@@ -665,7 +665,7 @@ func TestMatchmakerAddButNotMatch(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"properties.a5:bar",
		2, 2,
		2, 2, 1,
		map[string]string{
			"a5": "baz",
		},
@@ -715,7 +715,7 @@ func TestMatchmakerAddButNotMatchOnRange(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.b2:>=10 +properties.b2:<=20 +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
		},
@@ -740,7 +740,7 @@ func TestMatchmakerAddButNotMatchOnRange(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.b2:>=10 +properties.b2:<=20 +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
		},
@@ -792,7 +792,7 @@ func TestMatchmakerAddButNotMatchOnRangeAndValue(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.c3:>=10 +properties.c3:<=20 +properties.c4:foo +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"c4": "foo",
@@ -818,7 +818,7 @@ func TestMatchmakerAddButNotMatchOnRangeAndValue(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.c3:>=10 +properties.c3:<=20 +properties.c4:foo +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"c4": "foo",
@@ -868,7 +868,7 @@ func TestMatchmakerAddMultipleAndSomeMatch(t *testing.T) {
		},
	}, sessionID.String(), "",
		"properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -892,7 +892,7 @@ func TestMatchmakerAddMultipleAndSomeMatch(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -916,7 +916,7 @@ func TestMatchmakerAddMultipleAndSomeMatch(t *testing.T) {
		},
	}, sessionID3.String(), "",
		"properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -971,7 +971,7 @@ func TestMatchmakerAddMultipleAndSomeMatchWithBoost(t *testing.T) {
		},
	}, sessionID.String(), "",
		"properties.n1:<10^10 properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -997,7 +997,7 @@ func TestMatchmakerAddMultipleAndSomeMatchWithBoost(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"properties.n1:>10^10 properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -1023,7 +1023,7 @@ func TestMatchmakerAddMultipleAndSomeMatchWithBoost(t *testing.T) {
		},
	}, sessionID3.String(), "",
		"properties.n1:<10^10 properties.a6:bar +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -1090,7 +1090,7 @@ func TestMatchmakerAddMultipleAndSomeMatchOptionalTextAlteringScore(t *testing.T
		},
	}, sessionID.String(), "",
		"properties.a6:bar properties.a6:foo +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -1114,7 +1114,7 @@ func TestMatchmakerAddMultipleAndSomeMatchOptionalTextAlteringScore(t *testing.T
		},
	}, sessionID2.String(), "",
		"properties.a6:bar properties.a6:foo +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "foo",
@@ -1138,7 +1138,7 @@ func TestMatchmakerAddMultipleAndSomeMatchOptionalTextAlteringScore(t *testing.T
		},
	}, sessionID3.String(), "",
		"properties.a6:bar properties.a6:foo +properties.id:"+testID.String(),
		2, 2,
		2, 2, 1,
		map[string]string{
			"id": testID.String(),
			"a6": "bar",
@@ -1191,7 +1191,8 @@ func TestMatchmakerAddAndMatchAuthoritative(t *testing.T) {
		},
	}, sessionID.String(), "",
		"properties.d1:foo",
		2, 2, map[string]string{
		2, 2, 1,
		map[string]string{
			"d1":   "foo",
			"mode": "authoritative",
		}, map[string]float64{})
@@ -1213,7 +1214,8 @@ func TestMatchmakerAddAndMatchAuthoritative(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"properties.d1:foo",
		2, 2, map[string]string{
		2, 2, 1,
		map[string]string{
			"d1":   "foo",
			"mode": "authoritative",
		}, map[string]float64{})
@@ -1239,7 +1241,7 @@ func TestMatchmakerAddAndMatchAuthoritative(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -1267,7 +1269,7 @@ func TestMatchmakerAddAndMatchAuthoritative(t *testing.T) {
		}
		self := mm.GetSelf()
		if self == nil {
			t.Fatal("expectd self to not be nil")
			t.Fatal("expected self to not be nil")
		}
		if self.Presence.GetSessionId() == "" {
			t.Fatalf("expected session id not to be empty")
@@ -1391,7 +1393,8 @@ func TestMatchmakerRequireMutualMatch(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.b1:>=10 +properties.b1:<=20",
		2, 2, map[string]string{},
		2, 2, 1,
		map[string]string{},
		map[string]float64{
			"b1": 5,
		})
@@ -1410,7 +1413,8 @@ func TestMatchmakerRequireMutualMatch(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.b1:>=10 +properties.b1:<=20",
		2, 2, map[string]string{},
		2, 2, 1,
		map[string]string{},
		map[string]float64{
			"b1": 15,
		})
@@ -1472,7 +1476,8 @@ func TestMatchmakerRequireMutualMatchLarger(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.foo:bar properties.b1:10^10",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1493,7 +1498,8 @@ func TestMatchmakerRequireMutualMatchLarger(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.foo:bar properties.b1:20^10",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1514,7 +1520,8 @@ func TestMatchmakerRequireMutualMatchLarger(t *testing.T) {
		},
	}, sessionID3.String(), "",
		"+properties.foo:bar +properties.b1:<10",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1572,7 +1579,8 @@ func TestMatchmakerRequireMutualMatchLargerReversed(t *testing.T) {
		},
	}, sessionID.String(), "",
		"+properties.foo:bar properties.b1:10^10",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1593,7 +1601,8 @@ func TestMatchmakerRequireMutualMatchLargerReversed(t *testing.T) {
		},
	}, sessionID2.String(), "",
		"+properties.foo:bar +properties.b1:<10 properties.b1:20^10",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1614,7 +1623,8 @@ func TestMatchmakerRequireMutualMatchLargerReversed(t *testing.T) {
		},
	}, sessionID3.String(), "",
		"+properties.foo:bar",
		3, 3, map[string]string{
		3, 3, 1,
		map[string]string{
			"foo": "bar",
		},
		map[string]float64{
@@ -1650,7 +1660,7 @@ func isModeAuthoritative(props map[string]interface{}) bool {
// - min/max count 2
// - all items are a mutual match
func BenchmarkMatchmakerSmallProcessAllMutual(b *testing.B) {
	benchmarkMatchmakerHelper(b, 2, 2, 2,
	benchmarkMatchmakerHelper(b, 2, 2, 2, 1,
		func(i int) (string, map[string]string) {
			return benchmarkMatchQueryAny, benchmarkPropsAny
		})
@@ -1662,7 +1672,7 @@ func BenchmarkMatchmakerSmallProcessAllMutual(b *testing.B) {
// - min/max count 2
// - approx 50% items are a mutual match
func BenchmarkMatchmakerSmallProcessSomeNotMutual(b *testing.B) {
	benchmarkMatchmakerHelper(b, 2, 2, 2,
	benchmarkMatchmakerHelper(b, 2, 2, 2, 1,
		func(i int) (string, map[string]string) {
			matchQuery := benchmarkMatchQueryAny
			props := benchmarkPropsAny
@@ -1680,7 +1690,7 @@ func BenchmarkMatchmakerSmallProcessSomeNotMutual(b *testing.B) {
// - min/max count 2
// - all items are a mutual match
func BenchmarkMatchmakerMediumProcessAllMutual(b *testing.B) {
	benchmarkMatchmakerHelper(b, 100, 2, 2,
	benchmarkMatchmakerHelper(b, 100, 2, 2, 1,
		func(i int) (string, map[string]string) {
			return benchmarkMatchQueryAny, benchmarkPropsAny
		})
@@ -1692,7 +1702,7 @@ func BenchmarkMatchmakerMediumProcessAllMutual(b *testing.B) {
// - min/max count 2
// - approx 50% items are a mutual match
func BenchmarkMatchmakerMediumProcessSomeNonMutual(b *testing.B) {
	benchmarkMatchmakerHelper(b, 100, 2, 2,
	benchmarkMatchmakerHelper(b, 100, 2, 2, 1,
		func(i int) (string, map[string]string) {
			matchQuery := benchmarkMatchQueryAny
			props := benchmarkPropsAny
@@ -1710,7 +1720,7 @@ func BenchmarkMatchmakerMediumProcessSomeNonMutual(b *testing.B) {
// - min/max count 6
// - approx 50% items are a mutual match
func BenchmarkMatchmakerProcessMediumSomeNonMutualBiggerGroup(b *testing.B) {
	benchmarkMatchmakerHelper(b, 100, 6, 6,
	benchmarkMatchmakerHelper(b, 100, 6, 6, 1,
		func(i int) (string, map[string]string) {
			matchQuery := benchmarkMatchQueryAny
			props := benchmarkPropsAny
@@ -1729,7 +1739,7 @@ func BenchmarkMatchmakerProcessMediumSomeNonMutualBiggerGroup(b *testing.B) {
// - docs are now in a 50/40/10 distribution
// - 50% match all, 40% match some, and 10% match few
func BenchmarkMatchmakerProcessMediumSomeNonMutualBiggerGroupAndDifficultMatch(b *testing.B) {
	benchmarkMatchmakerHelper(b, 100, 6, 6,
	benchmarkMatchmakerHelper(b, 100, 6, 6, 1,
		func(i int) (string, map[string]string) {
			matchQuery := benchmarkMatchQueryAny
			props := benchmarkPropsAny
@@ -1744,7 +1754,7 @@ func BenchmarkMatchmakerProcessMediumSomeNonMutualBiggerGroupAndDifficultMatch(b
		})
}

func benchmarkMatchmakerHelper(b *testing.B, activeCount, minCount, maxCount int,
func benchmarkMatchmakerHelper(b *testing.B, activeCount, minCount, maxCount, countMultiple int,
	withQueryAndProps func(i int) (string, map[string]string)) {
	consoleLogger := loggerForBenchmark(b)
	matchMaker, cleanup, err := createTestMatchmaker(b, consoleLogger, nil)
@@ -1772,7 +1782,7 @@ func benchmarkMatchmakerHelper(b *testing.B, activeCount, minCount, maxCount int
				},
			}, sessionID.String(), "",
				matchQuery,
				minCount, maxCount,
				minCount, maxCount, countMultiple,
				props,
				map[string]float64{})
			if err != nil {
@@ -1828,7 +1838,7 @@ func TestMatchmakerMaxPartyTracking(t *testing.T) {
			},
		}, sessionID.String(), party,
			"properties.a5:bar",
			2, 2,
			2, 2, 1,
			map[string]string{
				"a5": "bar",
			},
@@ -1909,7 +1919,7 @@ func TestMatchmakerMaxSessionTracking(t *testing.T) {
			},
		}, sessionID.String(), "",
			"properties.a5:bar",
			2, 2,
			2, 2, 1,
			map[string]string{
				"a5": "bar",
			},
+2 −2
Original line number Diff line number Diff line
@@ -509,7 +509,7 @@ func (p *PartyHandler) JoinRequestList(sessionID, node string) ([]*rtapi.UserPre
	return joinRequestUserPresences, nil
}

func (p *PartyHandler) MatchmakerAdd(sessionID, node, query string, minCount, maxCount int, stringProperties map[string]string, numericProperties map[string]float64) (string, []*PresenceID, error) {
func (p *PartyHandler) MatchmakerAdd(sessionID, node, query string, minCount, maxCount, countMultiple int, stringProperties map[string]string, numericProperties map[string]float64) (string, []*PresenceID, error) {
	p.RLock()
	if p.stopped {
		p.RUnlock()
@@ -542,7 +542,7 @@ func (p *PartyHandler) MatchmakerAdd(sessionID, node, query string, minCount, ma

	p.RUnlock()

	ticket, err := p.matchmaker.Add(presences, "", p.IDStr, query, minCount, maxCount, stringProperties, numericProperties)
	ticket, err := p.matchmaker.Add(presences, "", p.IDStr, query, minCount, maxCount, countMultiple, stringProperties, numericProperties)
	if err != nil {
		return "", nil, err
	}
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ func TestPartyMatchmakerAddAndRemove(t *testing.T) {
		},
	}})

	ticket, _, err := partyHandler.MatchmakerAdd(sessionID.String(), node, "", 1, 1, nil, nil)
	ticket, _, err := partyHandler.MatchmakerAdd(sessionID.String(), node, "", 1, 1, 1, nil, nil)
	if err != nil {
		t.Fatalf("MatchmakerAdd error %s", err)
	}
Loading