open-match.dev/open-match@v1.8.1/examples/scale/scenarios/scenarios.go (about)

     1  // Copyright 2019 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package scenarios
    16  
    17  import (
    18  	"sync"
    19  
    20  	"github.com/sirupsen/logrus"
    21  	"google.golang.org/grpc"
    22  	"open-match.dev/open-match/examples/scale/scenarios/backfill"
    23  	"open-match.dev/open-match/examples/scale/scenarios/firstmatch"
    24  	"open-match.dev/open-match/internal/util/testing"
    25  	"open-match.dev/open-match/pkg/matchfunction"
    26  	"open-match.dev/open-match/pkg/pb"
    27  )
    28  
    29  var (
    30  	queryServiceAddress = "open-match-query.open-match.svc.cluster.local:50503" // Address of the QueryService Endpoint.
    31  
    32  	logger = logrus.WithFields(logrus.Fields{
    33  		"app": "scale",
    34  	})
    35  )
    36  
    37  // GameScenario defines what tickets look like, and how they should be matched.
    38  type GameScenario interface {
    39  	// Ticket creates a new ticket, with randomized parameters.
    40  	Ticket() *pb.Ticket
    41  
    42  	// Backfill creates a new backfill, with randomized parameters.
    43  	Backfill() *pb.Backfill
    44  
    45  	// Profiles lists all of the profiles that should run.
    46  	Profiles() []*pb.MatchProfile
    47  
    48  	// MatchFunction is the custom logic implementation of the match function.
    49  	MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error)
    50  
    51  	// Evaluate is the custom logic implementation of the evaluator.
    52  	Evaluate(stream pb.Evaluator_EvaluateServer) error
    53  }
    54  
    55  // ActiveScenario sets the scenario with preset parameters that we want to use for current Open Match benchmark run.
    56  var ActiveScenario = func() *Scenario {
    57  	var gs GameScenario = firstmatch.Scenario()
    58  
    59  	// TODO: Select which scenario to use based on some configuration or choice,
    60  	// so it's easier to run different scenarios without changing code.
    61  	//gs = battleroyal.Scenario()
    62  	//gs = teamshooter.Scenario()
    63  	s := backfill.Scenario()
    64  	gs = s
    65  
    66  	return &Scenario{
    67  		FrontendTotalTicketsToCreate:    -1,
    68  		FrontendTicketCreatedQPS:        100,
    69  		FrontendCreatesBackfillsOnStart: true,
    70  		FrontendTotalBackfillsToCreate:  1000,
    71  		FrontendDeletesTickets:          true,
    72  
    73  		BackendAssignsTickets:        false,
    74  		BackendAcknowledgesBackfills: true,
    75  		BackendDeletesBackfills:      true,
    76  
    77  		Ticket:             gs.Ticket,
    78  		Backfill:           gs.Backfill,
    79  		BackfillDeleteCond: s.BackfillDeleteCond,
    80  		Profiles:           gs.Profiles,
    81  
    82  		MMF:       queryPoolsWrapper(gs.MatchFunction),
    83  		Evaluator: gs.Evaluate,
    84  	}
    85  }()
    86  
    87  // Scenario defines the controllable fields for Open Match benchmark scenarios
    88  type Scenario struct {
    89  	// TODO: supports the following controllable parameters
    90  
    91  	// MatchFunction Configs
    92  	// MatchOverlapRatio          float32
    93  	// TicketSearchFieldsUnitSize int
    94  	// TicketSearchFieldsNumber   int
    95  
    96  	// GameFrontend Configs
    97  	// TicketExtensionSize       int
    98  	// PendingTicketNumber       int
    99  	// MatchExtensionSize        int
   100  	FrontendTicketCreatedQPS        uint32
   101  	FrontendTotalTicketsToCreate    int // TotalTicketsToCreate = -1 let scale-frontend create tickets forever
   102  	FrontendTotalBackfillsToCreate  int
   103  	FrontendCreatesBackfillsOnStart bool
   104  	FrontendDeletesTickets          bool
   105  
   106  	// GameBackend Configs
   107  	// ProfileNumber      int
   108  	// FilterNumber       int
   109  	BackendAssignsTickets        bool
   110  	BackendAcknowledgesBackfills bool
   111  	BackendDeletesBackfills      bool
   112  
   113  	Ticket             func() *pb.Ticket
   114  	Backfill           func() *pb.Backfill
   115  	BackfillDeleteCond func(*pb.Backfill) bool
   116  	Profiles           func() []*pb.MatchProfile
   117  
   118  	MMF       matchFunction
   119  	Evaluator evaluatorFunction
   120  }
   121  
   122  type matchFunction func(*pb.RunRequest, pb.MatchFunction_RunServer) error
   123  type evaluatorFunction func(pb.Evaluator_EvaluateServer) error
   124  
   125  func (mmf matchFunction) Run(req *pb.RunRequest, srv pb.MatchFunction_RunServer) error {
   126  	return mmf(req, srv)
   127  }
   128  
   129  func (eval evaluatorFunction) Evaluate(srv pb.Evaluator_EvaluateServer) error {
   130  	return eval(srv)
   131  }
   132  
   133  func getQueryServiceGRPCClient() pb.QueryServiceClient {
   134  	conn, err := grpc.Dial(queryServiceAddress, testing.NewGRPCDialOptions(logger)...)
   135  	if err != nil {
   136  		logger.Fatalf("Failed to connect to Open Match, got %v", err)
   137  	}
   138  	return pb.NewQueryServiceClient(conn)
   139  }
   140  
   141  func queryPoolsWrapper(mmf func(req *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error)) matchFunction {
   142  	var q pb.QueryServiceClient
   143  	var startQ sync.Once
   144  
   145  	return func(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
   146  		startQ.Do(func() {
   147  			q = getQueryServiceGRPCClient()
   148  		})
   149  
   150  		poolTickets, err := matchfunction.QueryPools(stream.Context(), q, req.GetProfile().GetPools())
   151  		if err != nil {
   152  			return err
   153  		}
   154  
   155  		poolBackfills, err := matchfunction.QueryBackfillPools(stream.Context(), q, req.GetProfile().GetPools())
   156  		if err != nil {
   157  			return err
   158  		}
   159  
   160  		proposals, err := mmf(req.GetProfile(), poolBackfills, poolTickets)
   161  		if err != nil {
   162  			return err
   163  		}
   164  
   165  		logger.WithFields(logrus.Fields{
   166  			"proposals": proposals,
   167  		}).Trace("proposals returned by match function")
   168  
   169  		for _, proposal := range proposals {
   170  			if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil {
   171  				return err
   172  			}
   173  		}
   174  
   175  		return nil
   176  	}
   177  }