Added mmf and evaluator for the basic benchmarking scenario (#1029)

* Added mmf and evaluator for the basic benchmarking scenario

* update

* update

* fix
This commit is contained in:
yfei1
2020-01-07 11:08:12 -08:00
committed by GitHub
parent 4daea744d5
commit 188457c21f
5 changed files with 212 additions and 9 deletions

View File

@ -373,7 +373,7 @@ install-scale-chart: install-chart-prerequisite build/toolchain/bin/helm$(EXE_EX
# install-ci-chart will install open-match-core with pool based mmf for end-to-end in-cluster test.
install-ci-chart: install-chart-prerequisite build/toolchain/bin/helm$(EXE_EXTENSION) install/helm/open-match/secrets/
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) install/helm/open-match $(HELM_IMAGE_FLAGS) \
$(HELM) upgrade $(OPEN_MATCH_HELM_NAME) $(HELM_UPGRADE_FLAGS) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) \
--set open-match-core.ignoreListTTL=1000ms \
--set open-match-customize.enabled=true \
--set open-match-customize.function.enabled=true \

View File

@ -1,16 +1,178 @@
package scenarios
import "open-match.dev/open-match/pkg/pb"
import (
"fmt"
"io"
"time"
var basicScenario = &Scenario{
MMF: basicMatchFunction,
Evaluator: basicEvaluate,
}
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"open-match.dev/open-match/internal/testing/customize/evaluator"
"open-match.dev/open-match/pkg/matchfunction"
"open-match.dev/open-match/pkg/pb"
"open-match.dev/open-match/test/evaluator/evaluate"
)
var (
matchName = "roster-based-matchfunction"
emptyRosterSpot = "EMPTY_ROSTER_SPOT"
basicScenario = &Scenario{
MMF: basicMatchFunction,
Evaluator: basicEvaluate,
}
)
func basicMatchFunction(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
// Fetch tickets for the pools specified in the Match Profile.
poolTickets, err := matchfunction.QueryPools(stream.Context(), getMmlogicGRPCClient(), req.GetProfile().GetPools())
if err != nil {
return err
}
// Generate proposals.
proposals, err := makeMatches(req.GetProfile(), poolTickets)
if err != nil {
return err
}
logger.WithFields(logrus.Fields{
"proposals": proposals,
}).Trace("proposals returned by match function")
// Stream the generated proposals back to Open Match.
for _, proposal := range proposals {
if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil {
return err
}
}
func basicMatchFunction(*pb.RunRequest, pb.MatchFunction_RunServer) error {
return nil
}
func basicEvaluate(pb.Evaluator_EvaluateServer) error {
func basicEvaluate(stream pb.Evaluator_EvaluateServer) error {
var matches = []*pb.Match{}
for {
req, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
matches = append(matches, req.GetMatch())
}
// Run the customized evaluator!
results, err := evaluate.Evaluate(&evaluator.Params{
Logger: logrus.WithFields(logrus.Fields{
"app": "openmatch",
"component": "evaluator.implementation",
}),
Matches: matches,
})
if err != nil {
return status.Error(codes.Aborted, err.Error())
}
for _, result := range results {
if err := stream.Send(&pb.EvaluateResponse{Match: result}); err != nil {
return err
}
}
logger.WithFields(logrus.Fields{
"results": results,
}).Debug("matches accepted by the evaluator")
return nil
}
func makeMatches(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
// This roster based match function expects the match profile to have a
// populated roster specifying the empty slots for each pool name and also
// have the ticket pools referenced in the roster. It generates matches by
// populating players from the specified pools into rosters.
wantTickets, err := wantPoolTickets(p.GetRosters())
if err != nil {
return nil, err
}
var matches []*pb.Match
count := 0
for {
insufficientTickets := false
matchTickets := []*pb.Ticket{}
matchRosters := []*pb.Roster{}
// Loop through each pool wanted in the rosters and pick the number of
// wanted players from the respective Pool.
for poolName, want := range wantTickets {
have, ok := poolTickets[poolName]
if !ok {
// A wanted Pool was not found in the Pools specified in the profile.
insufficientTickets = true
break
}
if len(have) < want {
// The Pool in the profile has fewer tickets than what the roster needs.
insufficientTickets = true
break
}
// Populate the wanted tickets from the Tickets in the corresponding Pool.
matchTickets = append(matchTickets, have[0:want]...)
poolTickets[poolName] = have[want:]
var ids []string
for _, ticket := range matchTickets {
ids = append(ids, ticket.Id)
}
matchRosters = append(matchRosters, &pb.Roster{
Name: poolName,
TicketIds: ids,
})
}
if insufficientTickets {
// Ran out of Tickets. Matches cannot be created from the remaining Tickets.
break
}
matches = append(matches, &pb.Match{
MatchId: fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), count),
MatchProfile: p.GetName(),
MatchFunction: matchName,
Tickets: matchTickets,
Rosters: matchRosters,
})
count++
}
return matches, nil
}
// wantPoolTickets parses the roster to return a map of the Pool name to the
// number of empty roster slots for that Pool.
func wantPoolTickets(rosters []*pb.Roster) (map[string]int, error) {
wantTickets := make(map[string]int)
for _, r := range rosters {
if _, ok := wantTickets[r.GetName()]; ok {
// We do not expect multiple Roster Pools to have the same name.
logger.Errorf("multiple rosters with same name not supported")
return nil, status.Error(codes.InvalidArgument, "multiple rosters with same name not supported")
}
wantTickets[r.GetName()] = 0
for _, slot := range r.GetTicketIds() {
if slot == emptyRosterSpot {
wantTickets[r.GetName()] = wantTickets[r.GetName()] + 1
}
}
}
return wantTickets, nil
}

View File

@ -0,0 +1,38 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package scenarios
import (
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"open-match.dev/open-match/internal/util/testing"
"open-match.dev/open-match/pkg/pb"
)
var (
logger = logrus.WithFields(logrus.Fields{
"app": "matchfunction",
"component": "mmf.rosterbased",
})
mmlogicAddress = "om-mmlogic.open-match.svc.cluster.local:50503" // Address of the MMLogic Endpoint.
)
func getMmlogicGRPCClient() pb.MmLogicClient {
conn, err := grpc.Dial(mmlogicAddress, testing.NewGRPCDialOptions(logger)...)
if err != nil {
logger.Fatalf("Failed to connect to Open Match, got %v", err)
}
return pb.NewMmLogicClient(conn)
}

View File

@ -23,6 +23,9 @@ spec:
limits:
memory: 800Mi
cpu: "1"
requests:
memory: 600Mi
cpu: "0.8"
env:
- name: NAMESPACE
value: "{{ .Release.Namespace }}"

View File

@ -86,7 +86,7 @@ func TestFetchMatches(t *testing.T) {
break
}
if err != nil {
assert.Equal(t, test.wantCode, status.Convert(err).Code())
assert.Contains(t, []codes.Code{test.wantCode, codes.Unknown}, status.Convert(err).Code())
break
}
gotMatches = append(gotMatches, &pb.Match{