open-match.dev/open-match@v1.8.1/examples/scale/backend/backend.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 backend
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"math/rand"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/sirupsen/logrus"
    26  	"go.opencensus.io/trace"
    27  	"open-match.dev/open-match/examples/scale/scenarios"
    28  	"open-match.dev/open-match/internal/appmain"
    29  	"open-match.dev/open-match/internal/config"
    30  	"open-match.dev/open-match/internal/rpc"
    31  	"open-match.dev/open-match/internal/telemetry"
    32  	"open-match.dev/open-match/pkg/pb"
    33  )
    34  
    35  var (
    36  	logger = logrus.WithFields(logrus.Fields{
    37  		"app":       "openmatch",
    38  		"component": "scale.backend",
    39  	})
    40  
    41  	activeScenario = scenarios.ActiveScenario
    42  
    43  	mIterations            = telemetry.Counter("scale_backend_iterations", "fetch match iterations")
    44  	mFetchMatchCalls       = telemetry.Counter("scale_backend_fetch_match_calls", "fetch match calls")
    45  	mFetchMatchSuccesses   = telemetry.Counter("scale_backend_fetch_match_successes", "fetch match successes")
    46  	mFetchMatchErrors      = telemetry.Counter("scale_backend_fetch_match_errors", "fetch match errors")
    47  	mMatchesReturned       = telemetry.Counter("scale_backend_matches_returned", "matches returned")
    48  	mSumTicketsReturned    = telemetry.Counter("scale_backend_sum_tickets_returned", "tickets in matches returned")
    49  	mMatchesAssigned       = telemetry.Counter("scale_backend_matches_assigned", "matches assigned")
    50  	mMatchAssignsFailed    = telemetry.Counter("scale_backend_match_assigns_failed", "match assigns failed")
    51  	mBackfillsDeleted      = telemetry.Counter("scale_backend_backfills_deleted", "backfills deleted")
    52  	mBackfillDeletesFailed = telemetry.Counter("scale_backend_backfill_deletes_failed", "backfill deletes failed")
    53  )
    54  
    55  // Run triggers execution of functions that continuously fetch, assign and
    56  // delete matches.
    57  func BindService(p *appmain.Params, b *appmain.Bindings) error {
    58  	go run(p.Config())
    59  	return nil
    60  }
    61  
    62  func run(cfg config.View) {
    63  	beConn, err := rpc.GRPCClientFromConfig(cfg, "api.backend")
    64  	if err != nil {
    65  		logger.Fatalf("failed to connect to Open Match Backend, got %v", err)
    66  	}
    67  
    68  	defer beConn.Close()
    69  	be := pb.NewBackendServiceClient(beConn)
    70  
    71  	feConn, err := rpc.GRPCClientFromConfig(cfg, "api.frontend")
    72  	if err != nil {
    73  		logger.Fatalf("failed to connect to Open Match Frontend, got %v", err)
    74  	}
    75  
    76  	defer feConn.Close()
    77  	fe := pb.NewFrontendServiceClient(feConn)
    78  
    79  	w := logger.Writer()
    80  	defer w.Close()
    81  
    82  	matchesToAssign := make(chan *pb.Match, 30000)
    83  
    84  	if activeScenario.BackendAssignsTickets {
    85  		for i := 0; i < 100; i++ {
    86  			go runAssignments(be, matchesToAssign)
    87  		}
    88  	}
    89  
    90  	backfillsToDelete := make(chan *pb.Backfill, 30000)
    91  
    92  	if activeScenario.BackendDeletesBackfills {
    93  		for i := 0; i < 100; i++ {
    94  			go runDeleteBackfills(fe, backfillsToDelete)
    95  		}
    96  	}
    97  
    98  	matchesToAcknowledge := make(chan *pb.Match, 30000)
    99  
   100  	if activeScenario.BackendAcknowledgesBackfills {
   101  		for i := 0; i < 100; i++ {
   102  			go runAcknowledgeBackfills(fe, matchesToAcknowledge, backfillsToDelete)
   103  		}
   104  	}
   105  
   106  	// Don't go faster than this, as it likely means that FetchMatches is throwing
   107  	// errors, and will continue doing so if queried very quickly.
   108  	for range time.Tick(time.Millisecond * 250) {
   109  		// Keep pulling matches from Open Match backend
   110  		profiles := activeScenario.Profiles()
   111  		var wg sync.WaitGroup
   112  
   113  		for _, p := range profiles {
   114  			wg.Add(1)
   115  			go func(wg *sync.WaitGroup, p *pb.MatchProfile) {
   116  				defer wg.Done()
   117  				runFetchMatches(be, p, matchesToAssign, matchesToAcknowledge)
   118  			}(&wg, p)
   119  		}
   120  
   121  		// Wait for all profiles to complete before proceeding.
   122  		wg.Wait()
   123  		telemetry.RecordUnitMeasurement(context.Background(), mIterations)
   124  	}
   125  }
   126  
   127  func runFetchMatches(be pb.BackendServiceClient, p *pb.MatchProfile, matchesToAssign chan<- *pb.Match, matchesToAcknowledge chan<- *pb.Match) {
   128  	ctx, span := trace.StartSpan(context.Background(), "scale.backend/FetchMatches")
   129  	defer span.End()
   130  
   131  	req := &pb.FetchMatchesRequest{
   132  		Config: &pb.FunctionConfig{
   133  			Host: "open-match-function",
   134  			Port: 50502,
   135  			Type: pb.FunctionConfig_GRPC,
   136  		},
   137  		Profile: p,
   138  	}
   139  
   140  	telemetry.RecordUnitMeasurement(ctx, mFetchMatchCalls)
   141  	stream, err := be.FetchMatches(ctx, req)
   142  	if err != nil {
   143  		telemetry.RecordUnitMeasurement(ctx, mFetchMatchErrors)
   144  		logger.WithError(err).Error("failed to get available stream client")
   145  		return
   146  	}
   147  
   148  	for {
   149  		// Pull the Match
   150  		resp, err := stream.Recv()
   151  		if err == io.EOF {
   152  			telemetry.RecordUnitMeasurement(ctx, mFetchMatchSuccesses)
   153  			return
   154  		}
   155  
   156  		if err != nil {
   157  			telemetry.RecordUnitMeasurement(ctx, mFetchMatchErrors)
   158  			logger.WithError(err).Error("failed to get matches from stream client")
   159  			return
   160  		}
   161  
   162  		telemetry.RecordNUnitMeasurement(ctx, mSumTicketsReturned, int64(len(resp.GetMatch().Tickets)))
   163  		telemetry.RecordUnitMeasurement(ctx, mMatchesReturned)
   164  
   165  		if activeScenario.BackendAssignsTickets {
   166  			matchesToAssign <- resp.GetMatch()
   167  		}
   168  
   169  		if activeScenario.BackendAcknowledgesBackfills {
   170  			matchesToAcknowledge <- resp.GetMatch()
   171  		}
   172  	}
   173  }
   174  
   175  func runDeleteBackfills(fe pb.FrontendServiceClient, backfillsToDelete <-chan *pb.Backfill) {
   176  	for b := range backfillsToDelete {
   177  		if !activeScenario.BackfillDeleteCond(b) {
   178  			continue
   179  		}
   180  
   181  		ctx := context.Background()
   182  		_, err := fe.DeleteBackfill(ctx, &pb.DeleteBackfillRequest{BackfillId: b.Id})
   183  		if err != nil {
   184  			logger.WithError(err).Errorf("failed to delete backfill: %s", b.Id)
   185  			telemetry.RecordUnitMeasurement(ctx, mBackfillDeletesFailed)
   186  		} else {
   187  			telemetry.RecordUnitMeasurement(ctx, mBackfillsDeleted)
   188  		}
   189  	}
   190  }
   191  
   192  func runAcknowledgeBackfills(fe pb.FrontendServiceClient, matchesToAcknowledge <-chan *pb.Match, backfillsToDelete chan<- *pb.Backfill) {
   193  	for m := range matchesToAcknowledge {
   194  		backfillId := m.Backfill.GetId()
   195  		if backfillId == "" {
   196  			continue
   197  		}
   198  
   199  		err := acknowledgeBackfill(fe, backfillId)
   200  		if err != nil {
   201  			logger.WithError(err).Errorf("failed to acknowledge backfill: %s", backfillId)
   202  			continue
   203  		}
   204  
   205  		if activeScenario.BackendDeletesBackfills {
   206  			backfillsToDelete <- m.Backfill
   207  		}
   208  	}
   209  }
   210  
   211  func acknowledgeBackfill(fe pb.FrontendServiceClient, backfillId string) error {
   212  	ctx, span := trace.StartSpan(context.Background(), "scale.frontend/AcknowledgeBackfill")
   213  	defer span.End()
   214  
   215  	_, err := fe.AcknowledgeBackfill(ctx, &pb.AcknowledgeBackfillRequest{
   216  		BackfillId: backfillId,
   217  		Assignment: &pb.Assignment{
   218  			Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
   219  		},
   220  	})
   221  	return err
   222  }
   223  
   224  func runAssignments(be pb.BackendServiceClient, matchesToAssign <-chan *pb.Match) {
   225  	ctx := context.Background()
   226  
   227  	for m := range matchesToAssign {
   228  		ids := []string{}
   229  		for _, t := range m.Tickets {
   230  			ids = append(ids, t.GetId())
   231  		}
   232  
   233  		_, err := be.AssignTickets(context.Background(), &pb.AssignTicketsRequest{
   234  			Assignments: []*pb.AssignmentGroup{
   235  				{
   236  					TicketIds: ids,
   237  					Assignment: &pb.Assignment{
   238  						Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
   239  					},
   240  				},
   241  			},
   242  		})
   243  		if err != nil {
   244  			telemetry.RecordUnitMeasurement(ctx, mMatchAssignsFailed)
   245  			logger.WithError(err).Error("failed to assign tickets")
   246  			continue
   247  		}
   248  
   249  		telemetry.RecordUnitMeasurement(ctx, mMatchesAssigned)
   250  	}
   251  }