mirror of
https://github.com/googleforgames/open-match.git
synced 2025-03-22 19:08:31 +00:00
Compare commits
109 Commits
release-0.
...
v1.3.0
Author | SHA1 | Date | |
---|---|---|---|
626e00d2c5 | |||
8bf5e67708 | |||
eca40e3298 | |||
902c9d69b4 | |||
67767cf1cd | |||
6f46731b15 | |||
0d1a77c5de | |||
f2a23f5ba1 | |||
3fa588c1f8 | |||
cc08f39205 | |||
ec9cf00bcf | |||
8b8617f68d | |||
ce9b989e58 | |||
5c00395c78 | |||
faf3eded1f | |||
250d44aefd | |||
13fdf5960f | |||
aa5a1f9da1 | |||
ad1ca16218 | |||
7d849f3f04 | |||
05c8c8aa76 | |||
f50c9eec80 | |||
c6f23f01ca | |||
21efdb6691 | |||
81a1dc38b6 | |||
d0ddf22658 | |||
ee247c6c1a | |||
a17eb3bc72 | |||
3d194f541e | |||
3a0cd7611b | |||
c13b461795 | |||
b9e55fc727 | |||
dd1386a55b | |||
defac9065b | |||
f203384fbf | |||
7ef9c052bd | |||
ea744b8b51 | |||
1a8fc62833 | |||
1d5574b8a3 | |||
75a3d43477 | |||
252fc8090d | |||
2c617f2cb6 | |||
fcd590eca6 | |||
4b3147511b | |||
c85af44567 | |||
688262111d | |||
26d1aa236a | |||
fff37cd82c | |||
98a227b515 | |||
88cd95fe57 | |||
248494c04c | |||
aa4398e786 | |||
fc5c3629e8 | |||
8d86709632 | |||
0a273674b9 | |||
e2247a7f53 | |||
b269896c23 | |||
a210185098 | |||
4df95deb54 | |||
a9b8eec9e0 | |||
afa59327a4 | |||
d86b6c5121 | |||
2eb2921914 | |||
80d882b7c7 | |||
0f34e31778 | |||
d45eb74510 | |||
1765ab7b7e | |||
6f05e526fb | |||
496d156faa | |||
3a3d618c43 | |||
e1cbd855f5 | |||
10b36705f0 | |||
a6fc4724bc | |||
511337088a | |||
5f67bb36a6 | |||
94d2105809 | |||
d85f1f4bc7 | |||
79e9afeca7 | |||
3334f7f74a | |||
85ce954eb9 | |||
679cfb5839 | |||
c53a5b7c88 | |||
cfb316169a | |||
a9365b5333 | |||
93df53201c | |||
eb86841423 | |||
771f706317 | |||
a9f9a2f2e6 | |||
068632285e | |||
113461114e | |||
0ac7ae13ac | |||
29a2dbcf99 | |||
48d3b5c0ee | |||
a5fa651106 | |||
cd84d74ff9 | |||
8c2aa1ea81 | |||
493ff8e520 | |||
8363bc5fc9 | |||
144f646b7f | |||
b518b5cc1b | |||
af0b9fd5f7 | |||
5f4b522ecd | |||
12625d7f53 | |||
3248c8c4ad | |||
10c0c59997 | |||
c17e3e62c0 | |||
8e91be6201 | |||
f6c837d6cd | |||
3c8908aae0 |
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @laremere @aLekSer @HazWard @calebatwd @syntxerror @sawagh @andrewgrundy
|
3
.github/ISSUE_TEMPLATE/release.md
vendored
3
.github/ISSUE_TEMPLATE/release.md
vendored
@ -114,7 +114,6 @@ git push origin release-0.5
|
||||
- [ ] There might be additional references to the old version but be careful not to change it for places that have it for historical purposes.
|
||||
- [ ] Run `make release`
|
||||
- [ ] Run `make api/api.md` in open-match repo to update the auto-generated API references in open-match-docs repo.
|
||||
- [ ] Use the files under the `build/release/` directory for the Open Match installation guide. Make sure the artifacts work as expected - these are the artifacts that will be published to the GCS bucket and used in our release assets.
|
||||
- [ ] Create a PR with the changes, include the release candidate name, and point it to the release branch.
|
||||
- [ ] Go to [open-match-build](https://pantheon.corp.google.com/cloud-build/triggers?project=open-match-build) and update all *post submit* triggers' `_GCB_LATEST_VERSION` value to the `X.Y` of the release. This value should only increase as it's used to determine the latest stable version.
|
||||
- [ ] Merge your changes once the PR is approved.
|
||||
@ -152,6 +151,7 @@ only required once.**
|
||||
- [ ] Go to the History section and find the "Post Submit" build of the merged commit that's running. Wait for it to go Green. If it's red, fix error repeat this section. Take note of the docker image version tag for next step. Example: 0.5.0-a4706cb.
|
||||
- [ ] Run `./docs/governance/templates/release.sh {source version tag} {version}` to copy the images to open-match-public-images.
|
||||
- [ ] If this is a new minor version in the newest major version then run `./docs/governance/templates/release.sh {source version tag} latest`.
|
||||
- [ ] Use the files under the `build/release/` directory for the Open Match installation guide. Make sure the artifacts work as expected - these are the artifacts that will be published to the GCS bucket and used in our release assets.
|
||||
- [ ] Copy the files from `build/release/` generated from `make release` to the release draft you created. You can drag and drop the files using the Github UI.
|
||||
- [ ] Update [Slack invitation link](https://slack.com/help/articles/201330256-invite-new-members-to-your-workspace#share-an-invite-link) in [open-match.dev](https://open-match.dev/site/docs/contribute/#get-involved).
|
||||
- [ ] Test Open Match installation under GKE and Minikube enviroment using YAML files and Helm. Follow the [First Match](https://development.open-match.dev/site/docs/getting-started/first_match/) guide, run `make proxy-demo`, and open `localhost:51507` to make sure everything works.
|
||||
@ -165,6 +165,7 @@ only required once.**
|
||||
- [ ] Save the release as a draft.
|
||||
- [ ] Circulate the draft release to active contributors. Where reasonable, get everyone's ok on the release notes before continuing.
|
||||
- [ ] Publish the [Release](om-release) in Github. This will notify repository watchers.
|
||||
- [ ] Publish the [Release](om-release) on Open Match [Blog](https://open-match.dev/site/blog/).
|
||||
|
||||
## Announce
|
||||
|
||||
|
16
.github/pull_request_template.md
vendored
Normal file
16
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<!-- Thanks for sending a pull request! Here are some tips for you:
|
||||
If this is your first time, please read our contributor guidelines: https://github.com/googleforgames/open-match/blob/master/CONTRIBUTING.md and developer guide https://github.com/googleforgames/open-match/blob/master/docs/development.md
|
||||
-->
|
||||
|
||||
**What this PR does / Why we need it**:
|
||||
|
||||
**Which issue(s) this PR fixes**:
|
||||
<!--
|
||||
*Automatically closes linked issue when PR is merged.
|
||||
Usage: `Closes #<issue number>`, or `Closes (paste link of issue)`.
|
||||
-->
|
||||
Closes #
|
||||
|
||||
**Special notes for your reviewer**:
|
||||
|
||||
|
311
Makefile
311
Makefile
@ -15,44 +15,45 @@
|
||||
## Open Match Make Help
|
||||
## ====================
|
||||
##
|
||||
## Create a GKE Cluster (requires gcloud installed and initialized, https://cloud.google.com/sdk/docs/quickstarts)
|
||||
## # Create a GKE Cluster (requires gcloud installed and initialized, https://cloud.google.com/sdk/docs/quickstarts)
|
||||
## make activate-gcp-apis
|
||||
## make create-gke-cluster push-helm
|
||||
##
|
||||
## Create a Minikube Cluster (requires VirtualBox)
|
||||
## # Create a Minikube Cluster (requires VirtualBox)
|
||||
## make create-mini-cluster push-helm
|
||||
##
|
||||
## Create a KinD Cluster (Follow instructions to run command before pushing helm.)
|
||||
## # Create a KinD Cluster (Follow instructions to run command before pushing helm.)
|
||||
## make create-kind-cluster get-kind-kubeconfig
|
||||
## Finish KinD setup by installing helm:
|
||||
##
|
||||
## # Finish KinD setup by installing helm:
|
||||
## make push-helm
|
||||
##
|
||||
## Deploy Open Match
|
||||
## # Deploy Open Match
|
||||
## make push-images -j$(nproc)
|
||||
## make install-chart
|
||||
##
|
||||
## Build and Test
|
||||
## # Build and Test
|
||||
## make all -j$(nproc)
|
||||
## make test
|
||||
##
|
||||
## Access telemetry
|
||||
## # Access telemetry
|
||||
## make proxy-prometheus
|
||||
## make proxy-grafana
|
||||
## make proxy-ui
|
||||
##
|
||||
## Teardown
|
||||
## # Teardown
|
||||
## make delete-mini-cluster
|
||||
## make delete-gke-cluster
|
||||
## make delete-kind-cluster && export KUBECONFIG=""
|
||||
##
|
||||
## Prepare a Pull Request
|
||||
## # Prepare a Pull Request
|
||||
## make presubmit
|
||||
##
|
||||
|
||||
# If you want information on how to edit this file checkout,
|
||||
# http://makefiletutorial.com/
|
||||
|
||||
BASE_VERSION = 0.0.0-dev
|
||||
BASE_VERSION = 1.3.0
|
||||
SHORT_SHA = $(shell git rev-parse --short=7 HEAD | tr -d [:punct:])
|
||||
BRANCH_NAME = $(shell git rev-parse --abbrev-ref HEAD | tr -d [:punct:])
|
||||
VERSION = $(BASE_VERSION)-$(SHORT_SHA)
|
||||
@ -67,6 +68,8 @@ MINIKUBE_VERSION = latest
|
||||
GOLANGCI_VERSION = 1.18.0
|
||||
KIND_VERSION = 0.5.1
|
||||
SWAGGERUI_VERSION = 3.24.2
|
||||
GOOGLE_APIS_VERSION = aba342359b6743353195ca53f944fe71e6fb6cd4
|
||||
GRPC_GATEWAY_VERSION = 2.3.0
|
||||
TERRAFORM_VERSION = 0.12.13
|
||||
CHART_TESTING_VERSION = 2.4.0
|
||||
|
||||
@ -77,7 +80,6 @@ ENABLE_SECURITY_HARDENING = 0
|
||||
GO = GO111MODULE=on go
|
||||
# Defines the absolute local directory of the open-match project
|
||||
REPOSITORY_ROOT := $(patsubst %/,%,$(dir $(abspath $(MAKEFILE_LIST))))
|
||||
GO_BUILD_COMMAND = CGO_ENABLED=0 $(GO) build -a -installsuffix cgo .
|
||||
BUILD_DIR = $(REPOSITORY_ROOT)/build
|
||||
TOOLCHAIN_DIR = $(BUILD_DIR)/toolchain
|
||||
TOOLCHAIN_BIN = $(TOOLCHAIN_DIR)/bin
|
||||
@ -122,7 +124,7 @@ GCLOUD = gcloud --quiet
|
||||
OPEN_MATCH_HELM_NAME = open-match
|
||||
OPEN_MATCH_KUBERNETES_NAMESPACE = open-match
|
||||
OPEN_MATCH_SECRETS_DIR = $(REPOSITORY_ROOT)/install/helm/open-match/secrets
|
||||
GCLOUD_ACCOUNT_EMAIL = $(shell gcloud auth list --format yaml | grep account: | cut -c 10-)
|
||||
GCLOUD_ACCOUNT_EMAIL = $(shell gcloud auth list --format yaml | grep ACTIVE -a2 | grep account: | cut -c 10-)
|
||||
_GCB_POST_SUBMIT ?= 0
|
||||
# Latest version triggers builds of :latest images.
|
||||
_GCB_LATEST_VERSION ?= undefined
|
||||
@ -186,7 +188,7 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
GOLANG_PROTOS = pkg/pb/backend.pb.go pkg/pb/frontend.pb.go pkg/pb/matchfunction.pb.go pkg/pb/query.pb.go pkg/pb/messages.pb.go pkg/pb/extensions.pb.go pkg/pb/evaluator.pb.go internal/ipb/synchronizer.pb.go pkg/pb/backend.pb.gw.go pkg/pb/frontend.pb.gw.go pkg/pb/matchfunction.pb.gw.go pkg/pb/query.pb.gw.go pkg/pb/evaluator.pb.gw.go
|
||||
GOLANG_PROTOS = pkg/pb/backend.pb.go pkg/pb/frontend.pb.go pkg/pb/matchfunction.pb.go pkg/pb/query.pb.go pkg/pb/messages.pb.go pkg/pb/extensions.pb.go pkg/pb/evaluator.pb.go internal/ipb/synchronizer.pb.go internal/ipb/messages.pb.go pkg/pb/backend.pb.gw.go pkg/pb/frontend.pb.gw.go pkg/pb/matchfunction.pb.gw.go pkg/pb/query.pb.gw.go pkg/pb/evaluator.pb.gw.go
|
||||
|
||||
SWAGGER_JSON_DOCS = api/frontend.swagger.json api/backend.swagger.json api/query.swagger.json api/matchfunction.swagger.json api/evaluator.swagger.json
|
||||
|
||||
@ -196,7 +198,7 @@ ALL_PROTOS = $(GOLANG_PROTOS) $(SWAGGER_JSON_DOCS)
|
||||
CMDS = $(notdir $(wildcard cmd/*))
|
||||
|
||||
# Names of the individual images, ommiting the openmatch prefix.
|
||||
IMAGES = $(CMDS) mmf-go-soloduel mmf-go-pool base-build
|
||||
IMAGES = $(CMDS) mmf-go-soloduel mmf-go-backfill base-build
|
||||
|
||||
help:
|
||||
@cat Makefile | grep ^\#\# | grep -v ^\#\#\# |cut -c 4-
|
||||
@ -208,14 +210,18 @@ local-cloud-build: gcloud
|
||||
################################################################################
|
||||
## #############################################################################
|
||||
## Image commands:
|
||||
## These commands are auto-generated based on a complete list of images. All
|
||||
## folders in cmd/ are turned into an image using Dockerfile.cmd. Additional
|
||||
## images are specified by the IMAGES variable. Image commands ommit the
|
||||
## "openmatch-" prefix on the image name and tags.
|
||||
## These commands are auto-generated based on a complete list of images.
|
||||
## All folders in cmd/ are turned into an image using Dockerfile.cmd.
|
||||
## Additional images are specified by the IMAGES variable.
|
||||
## Image commands ommit the "openmatch-" prefix on the image name and tags.
|
||||
##
|
||||
|
||||
list-images:
|
||||
@echo $(IMAGES)
|
||||
|
||||
#######################################
|
||||
## build-images / build-<image name>-image: builds images locally
|
||||
## # Builds images locally
|
||||
## build-images / build-<image name>-image
|
||||
##
|
||||
build-images: $(foreach IMAGE,$(IMAGES),build-$(IMAGE)-image)
|
||||
|
||||
@ -236,12 +242,12 @@ $(foreach CMD,$(CMDS),build-$(CMD)-image): build-%-image: docker build-base-buil
|
||||
build-mmf-go-soloduel-image: docker build-base-build-image
|
||||
docker build -f examples/functions/golang/soloduel/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-soloduel:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-soloduel:$(ALTERNATE_TAG) .
|
||||
|
||||
build-mmf-go-pool-image: docker build-base-build-image
|
||||
docker build -f test/matchfunction/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-pool:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-pool:$(ALTERNATE_TAG) .
|
||||
build-mmf-go-backfill-image: docker build-base-build-image
|
||||
docker build -f examples/functions/golang/backfill/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-backfill:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-backfill:$(ALTERNATE_TAG) .
|
||||
|
||||
#######################################
|
||||
## push-images / push-<image name>-image: builds and pushes images to your
|
||||
## container registry.
|
||||
## # Builds and pushes images to your container registry.
|
||||
## push-images / push-<image name>-image
|
||||
##
|
||||
push-images: $(foreach IMAGE,$(IMAGES),push-$(IMAGE)-image)
|
||||
|
||||
@ -260,8 +266,9 @@ endif
|
||||
endif
|
||||
|
||||
#######################################
|
||||
## retag-images / retag-<image name>-image: publishes images on the public
|
||||
## container registry. Used for publishing releases.
|
||||
## # Publishes images on the public container registry.
|
||||
## # Used for publishing releases.
|
||||
## retag-images / retag-<image name>-image
|
||||
##
|
||||
retag-images: $(foreach IMAGE,$(IMAGES),retag-$(IMAGE)-image)
|
||||
|
||||
@ -274,7 +281,8 @@ $(foreach IMAGE,$(IMAGES),retag-$(IMAGE)-image): retag-%-image: docker
|
||||
docker push $(TARGET_REGISTRY)/openmatch-$*:$(TAG)
|
||||
|
||||
#######################################
|
||||
## clean-images / clean-<image name>-image: removes images from local docker
|
||||
## # Removes images from local docker
|
||||
## clean-images / clean-<image name>-image
|
||||
##
|
||||
clean-images: docker $(foreach IMAGE,$(IMAGES),clean-$(IMAGE)-image)
|
||||
-docker rmi -f open-match-base-build
|
||||
@ -284,7 +292,7 @@ $(foreach IMAGE,$(IMAGES),clean-$(IMAGE)-image): clean-%-image:
|
||||
|
||||
#####################################################################################################################
|
||||
update-chart-deps: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/helm/open-match; $(HELM) repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com; $(HELM) dependency update)
|
||||
(cd $(REPOSITORY_ROOT)/install/helm/open-match; $(HELM) repo add incubator https://charts.helm.sh/incubator; $(HELM) repo add bitnami https://charts.bitnami.com/bitnami;$(HELM) dependency update)
|
||||
|
||||
lint-chart: build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/ct$(EXE_EXTENSION)
|
||||
(cd $(REPOSITORY_ROOT)/install/helm; $(HELM) lint $(OPEN_MATCH_HELM_NAME))
|
||||
@ -297,8 +305,8 @@ build/chart/open-match-$(BASE_VERSION).tgz: build/toolchain/bin/helm$(EXE_EXTENS
|
||||
|
||||
build/chart/index.yaml: build/toolchain/bin/helm$(EXE_EXTENSION) gcloud build/chart/open-match-$(BASE_VERSION).tgz
|
||||
mkdir -p $(BUILD_DIR)/chart-index/
|
||||
-gsutil cp gs://open-match-chart/chart/index.yaml $(BUILD_DIR)/chart-index/
|
||||
-gsutil -m cp gs://open-match-chart/chart/open-match-* $(BUILD_DIR)/chart-index/
|
||||
-gsutil cp $(_CHARTS_BUCKET)/chart/index.yaml $(BUILD_DIR)/chart-index/
|
||||
-gsutil -m cp $(_CHARTS_BUCKET)/chart/open-match-* $(BUILD_DIR)/chart-index/
|
||||
$(HELM) repo index $(BUILD_DIR)/chart-index/
|
||||
$(HELM) repo index --merge $(BUILD_DIR)/chart-index/index.yaml $(BUILD_DIR)/chart/
|
||||
|
||||
@ -312,7 +320,7 @@ install-chart-prerequisite: build/toolchain/bin/kubectl$(EXE_EXTENSION) update-c
|
||||
$(KUBECTL) apply -f install/gke-metadata-server-workaround.yaml
|
||||
|
||||
# Used for Open Match development. Install om-configmap-override.yaml by default.
|
||||
HELM_UPGRADE_FLAGS = --cleanup-on-fail -i --no-hooks --debug --timeout=600s --namespace=$(OPEN_MATCH_KUBERNETES_NAMESPACE) --set global.gcpProjectId=$(GCP_PROJECT_ID) --set open-match-override.enabled=true --set redis.password=$(REDIS_DEV_PASSWORD)
|
||||
HELM_UPGRADE_FLAGS = --cleanup-on-fail -i --no-hooks --debug --timeout=600s --namespace=$(OPEN_MATCH_KUBERNETES_NAMESPACE) --set global.gcpProjectId=$(GCP_PROJECT_ID) --set open-match-override.enabled=true --set redis.password=$(REDIS_DEV_PASSWORD) --set redis.usePassword=false --set redis.sentinel.usePassword=false
|
||||
# Used for generate static yamls. Install om-configmap-override.yaml as needed.
|
||||
HELM_TEMPLATE_FLAGS = --no-hooks --namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE) --set usingHelmTemplate=true
|
||||
HELM_IMAGE_FLAGS = --set global.image.registry=$(REGISTRY) --set global.image.tag=$(TAG)
|
||||
@ -356,17 +364,24 @@ install-scale-chart: install-chart-prerequisite build/toolchain/bin/helm$(EXE_EX
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set global.telemetry.prometheus.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set open-match-scale.enabled=true | $(KUBECTL) apply -f -
|
||||
--set global.kubernetes.serviceAccount=$(OPEN_MATCH_HELM_NAME)-unprivileged-service \
|
||||
--set open-match-scale.enabled=true \
|
||||
--set open-match-scale.configs.default.configName="\{\{ printf \"$(OPEN_MATCH_HELM_NAME)-configmap-default\" \}\}" \
|
||||
--set open-match-scale.configs.override.configName="\{\{ printf \"$(OPEN_MATCH_HELM_NAME)-configmap-override\" \}\}" | $(KUBECTL) apply -f -
|
||||
|
||||
# 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) --atomic install/helm/open-match $(HELM_IMAGE_FLAGS) \
|
||||
--set open-match-core.ignoreListTTL=500ms \
|
||||
--set open-match-customize.enabled=true \
|
||||
--set open-match-customize.function.enabled=true \
|
||||
--set open-match-customize.evaluator.enabled=true \
|
||||
--set open-match-customize.function.image=openmatch-mmf-go-pool \
|
||||
--set query.replicas=1,frontend.replicas=1,backend.replicas=1,open-match-customize.evaluator.replicas=1,open-match-customize.function.replicas=1 \
|
||||
--set query.replicas=1,frontend.replicas=1,backend.replicas=1 \
|
||||
--set evaluator.hostName=open-match-test \
|
||||
--set evaluator.grpcPort=50509 \
|
||||
--set evaluator.httpPort=51509 \
|
||||
--set open-match-core.registrationInterval=200ms \
|
||||
--set open-match-core.proposalCollectionInterval=200ms \
|
||||
--set open-match-core.assignedDeleteTimeout=200ms \
|
||||
--set open-match-core.pendingReleaseTimeout=1s \
|
||||
--set open-match-core.queryPageSize=10 \
|
||||
--set global.gcpProjectId=intentionally-invalid-value \
|
||||
--set redis.master.resources.requests.cpu=0.6,redis.master.resources.requests.memory=300Mi \
|
||||
--set ci=true
|
||||
|
||||
@ -378,11 +393,18 @@ delete-chart: build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/kubec
|
||||
-$(KUBECTL) delete namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)
|
||||
-$(KUBECTL) delete namespace $(OPEN_MATCH_KUBERNETES_NAMESPACE)-demo
|
||||
|
||||
ifneq ($(BASE_VERSION), 0.0.0-dev)
|
||||
install/yaml/: REGISTRY = gcr.io/$(OPEN_MATCH_PUBLIC_IMAGES_PROJECT_ID)
|
||||
install/yaml/: TAG = $(BASE_VERSION)
|
||||
endif
|
||||
install/yaml/: update-chart-deps install/yaml/install.yaml install/yaml/01-open-match-core.yaml install/yaml/02-open-match-demo.yaml install/yaml/03-prometheus-chart.yaml install/yaml/04-grafana-chart.yaml install/yaml/05-jaeger-chart.yaml install/yaml/06-open-match-override-configmap.yaml install/yaml/07-open-match-default-evaluator.yaml
|
||||
|
||||
# We have to hard-code the Jaeger endpoints as we are excluding Jaeger, so Helm cannot determine the endpoints from the Jaeger subchart
|
||||
install/yaml/01-open-match-core.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
--set-string global.telemetry.jaeger.agentEndpoint="$(OPEN_MATCH_HELM_NAME)-jaeger-agent:6831" \
|
||||
--set-string global.telemetry.jaeger.collectorEndpoint="http://$(OPEN_MATCH_HELM_NAME)-jaeger-collector:14268/api/traces" \
|
||||
install/helm/open-match > install/yaml/01-open-match-core.yaml
|
||||
|
||||
install/yaml/02-open-match-demo.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
@ -400,6 +422,7 @@ install/yaml/03-prometheus-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
--set global.telemetry.prometheus.enabled=true \
|
||||
install/helm/open-match > install/yaml/03-prometheus-chart.yaml
|
||||
|
||||
# We have to hard-code the Prometheus Server URL as we are excluding Prometheus, so Helm cannot determine the URL from the Prometheus subchart
|
||||
install/yaml/04-grafana-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
mkdir -p install/yaml/
|
||||
$(HELM) template $(OPEN_MATCH_HELM_NAME) $(HELM_TEMPLATE_FLAGS) $(HELM_IMAGE_FLAGS) \
|
||||
@ -407,6 +430,7 @@ install/yaml/04-grafana-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
--set open-match-core.redis.enabled=false \
|
||||
--set open-match-telemetry.enabled=true \
|
||||
--set global.telemetry.grafana.enabled=true \
|
||||
--set-string global.telemetry.grafana.prometheusServer="http://$(OPEN_MATCH_HELM_NAME)-prometheus-server.$(OPEN_MATCH_KUBERNETES_NAMESPACE).svc.cluster.local:80/" \
|
||||
install/helm/open-match > install/yaml/04-grafana-chart.yaml
|
||||
|
||||
install/yaml/05-jaeger-chart.yaml: build/toolchain/bin/helm$(EXE_EXTENSION)
|
||||
@ -453,11 +477,29 @@ set-redis-password:
|
||||
read REDIS_PASSWORD; \
|
||||
stty echo; \
|
||||
printf "\n"; \
|
||||
$(KUBECTL) create secret generic om-redis -n $(OPEN_MATCH_KUBERNETES_NAMESPACE) --from-literal=redis-password=$$REDIS_PASSWORD --dry-run -o yaml | $(KUBECTL) replace -f - --force
|
||||
$(KUBECTL) create secret generic open-match-redis -n $(OPEN_MATCH_KUBERNETES_NAMESPACE) --from-literal=redis-password=$$REDIS_PASSWORD --dry-run -o yaml | $(KUBECTL) replace -f - --force
|
||||
## ####################################
|
||||
## # Tool installation helpers
|
||||
##
|
||||
|
||||
## # Install toolchain. Short for installing K8s, protoc and OpenMatch tools.
|
||||
## make install-toolchain
|
||||
##
|
||||
install-toolchain: install-kubernetes-tools install-protoc-tools install-openmatch-tools
|
||||
|
||||
## # Install Kubernetes tools
|
||||
## make install-kubernetes-tools
|
||||
##
|
||||
install-kubernetes-tools: build/toolchain/bin/kubectl$(EXE_EXTENSION) build/toolchain/bin/helm$(EXE_EXTENSION) build/toolchain/bin/minikube$(EXE_EXTENSION) build/toolchain/bin/terraform$(EXE_EXTENSION)
|
||||
install-protoc-tools: build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION)
|
||||
|
||||
## # Install protoc tools
|
||||
## make install-protoc-tools
|
||||
##
|
||||
install-protoc-tools: build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-openapiv2$(EXE_EXTENSION)
|
||||
|
||||
## # Install OpenMatch tools
|
||||
## make install-openmatch-tools
|
||||
##
|
||||
install-openmatch-tools: build/toolchain/bin/certgen$(EXE_EXTENSION) build/toolchain/bin/reaper$(EXE_EXTENSION)
|
||||
|
||||
build/toolchain/bin/helm$(EXE_EXTENSION):
|
||||
@ -529,19 +571,19 @@ build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION):
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION):
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
|
||||
|
||||
build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION):
|
||||
build/toolchain/bin/protoc-gen-openapiv2$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build -i -pkgdir . github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
|
||||
|
||||
build/toolchain/bin/certgen$(EXE_EXTENSION): tools/certgen/certgen$(EXE_EXTENSION)
|
||||
build/toolchain/bin/certgen$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cp -f $(REPOSITORY_ROOT)/tools/certgen/certgen$(EXE_EXTENSION) $(CERTGEN)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build $(REPOSITORY_ROOT)/tools/certgen/
|
||||
|
||||
build/toolchain/bin/reaper$(EXE_EXTENSION): tools/reaper/reaper$(EXE_EXTENSION)
|
||||
build/toolchain/bin/reaper$(EXE_EXTENSION):
|
||||
mkdir -p $(TOOLCHAIN_BIN)
|
||||
cp -f $(REPOSITORY_ROOT)/tools/reaper/reaper$(EXE_EXTENSION) $(TOOLCHAIN_BIN)/reaper$(EXE_EXTENSION)
|
||||
cd $(TOOLCHAIN_BIN) && $(GO) build $(REPOSITORY_ROOT)/tools/reaper/
|
||||
|
||||
# Fake target for docker
|
||||
docker: no-sudo
|
||||
@ -591,16 +633,21 @@ get-kind-kubeconfig: build/toolchain/bin/kind$(EXE_EXTENSION)
|
||||
delete-kind-cluster: build/toolchain/bin/kind$(EXE_EXTENSION) build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
-$(KIND) delete cluster
|
||||
|
||||
create-gke-cluster: GKE_VERSION = 1.14.8-gke.17 # gcloud beta container get-server-config --zone us-west1-a
|
||||
create-gke-cluster: GKE_CLUSTER_SHAPE_FLAGS = --machine-type n1-standard-4 --enable-autoscaling --min-nodes 1 --num-nodes 2 --max-nodes 10 --disk-size 50
|
||||
create-cluster-role-binding:
|
||||
$(KUBECTL) create clusterrolebinding myname-cluster-admin-binding --clusterrole=cluster-admin --user=$(GCLOUD_ACCOUNT_EMAIL)
|
||||
|
||||
create-gke-cluster: GKE_VERSION = 1.20.8-gke.900 # gcloud beta container get-server-config --zone us-west1-a
|
||||
create-gke-cluster: GKE_CLUSTER_SHAPE_FLAGS = --machine-type n1-standard-8 --enable-autoscaling --min-nodes 1 --num-nodes 6 --max-nodes 10 --disk-size 50
|
||||
create-gke-cluster: GKE_FUTURE_COMPAT_FLAGS = --no-enable-basic-auth --no-issue-client-certificate --enable-ip-alias --metadata disable-legacy-endpoints=true --enable-autoupgrade
|
||||
create-gke-cluster: build/toolchain/bin/kubectl$(EXE_EXTENSION) gcloud
|
||||
$(GCLOUD) beta $(GCP_PROJECT_FLAG) container clusters create $(GKE_CLUSTER_NAME) $(GCP_LOCATION_FLAG) $(GKE_CLUSTER_SHAPE_FLAGS) $(GKE_FUTURE_COMPAT_FLAGS) $(GKE_CLUSTER_FLAGS) \
|
||||
--enable-pod-security-policy \
|
||||
--cluster-version $(GKE_VERSION) \
|
||||
--image-type cos_containerd \
|
||||
--tags open-match
|
||||
$(KUBECTL) create clusterrolebinding myname-cluster-admin-binding --clusterrole=cluster-admin --user=$(GCLOUD_ACCOUNT_EMAIL)
|
||||
--tags open-match \
|
||||
--workload-pool $(PROJECT_ID).svc.id.goog
|
||||
$(MAKE) create-cluster-role-binding
|
||||
|
||||
|
||||
delete-gke-cluster: gcloud
|
||||
-$(GCLOUD) $(GCP_PROJECT_FLAG) container clusters delete $(GKE_CLUSTER_NAME) $(GCP_LOCATION_FLAG) $(GCLOUD_EXTRA_FLAGS)
|
||||
@ -614,12 +661,19 @@ delete-mini-cluster: build/toolchain/bin/minikube$(EXE_EXTENSION)
|
||||
gcp-apply-binauthz-policy: build/policies/binauthz.yaml
|
||||
$(GCLOUD) beta $(GCP_PROJECT_FLAG) container binauthz policy import build/policies/binauthz.yaml
|
||||
|
||||
## ####################################
|
||||
## # Protobuf
|
||||
##
|
||||
|
||||
## # Build all protobuf definitions.
|
||||
## make all-protos
|
||||
##
|
||||
all-protos: $(ALL_PROTOS)
|
||||
|
||||
# The proto generator really wants to be run from the $GOPATH root, and doesn't
|
||||
# support methods for directing it to the correct location that's not the proto
|
||||
# file's location. So instead put it in a tempororary directory, then move it
|
||||
# out.
|
||||
# file's location.
|
||||
# So, instead, put it in a tempororary directory, then move it out.
|
||||
pkg/pb/%.pb.go: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-go$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-grpc-gateway$(EXE_EXTENSION)
|
||||
mkdir -p $(REPOSITORY_ROOT)/build/prototmp $(REPOSITORY_ROOT)/pkg/pb
|
||||
$(PROTOC) $< \
|
||||
@ -641,25 +695,24 @@ pkg/pb/%.pb.gw.go: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTE
|
||||
--grpc-gateway_out=logtostderr=true,allow_delete_body=true:$(REPOSITORY_ROOT)/build/prototmp
|
||||
mv $(REPOSITORY_ROOT)/build/prototmp/open-match.dev/open-match/$@ $@
|
||||
|
||||
api/%.swagger.json: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-swagger$(EXE_EXTENSION)
|
||||
api/%.swagger.json: api/%.proto third_party/ build/toolchain/bin/protoc$(EXE_EXTENSION) build/toolchain/bin/protoc-gen-openapiv2$(EXE_EXTENSION)
|
||||
$(PROTOC) $< \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--swagger_out=logtostderr=true,allow_delete_body=true:$(REPOSITORY_ROOT)
|
||||
--openapiv2_out=json_names_for_fields=false,logtostderr=true,allow_delete_body=true:$(REPOSITORY_ROOT)
|
||||
|
||||
|
||||
## # Build API reference in markdown. Needs open-match-docs repo at the same level as this one.
|
||||
## make api/api.md
|
||||
##
|
||||
api/api.md: third_party/ build/toolchain/bin/protoc-gen-doc$(EXE_EXTENSION)
|
||||
$(PROTOC) api/*.proto \
|
||||
-I $(REPOSITORY_ROOT) -I $(PROTOC_INCLUDES) \
|
||||
--doc_out=. \
|
||||
--doc_opt=markdown,api.md
|
||||
--doc_opt=markdown,api_temp.md
|
||||
# Crazy hack that insert hugo link reference to this API doc -)
|
||||
$(SED_REPLACE) '1 i\---\
|
||||
title: "Open Match API References" \
|
||||
linkTitle: "Open Match API References" \
|
||||
weight: 2 \
|
||||
description: \
|
||||
This document provides API references for Open Match services. \
|
||||
--- \
|
||||
' ./api.md && mv ./api.md $(REPOSITORY_ROOT)/../open-match-docs/site/content/en/docs/Reference/
|
||||
cat ./docs/hugo_apiheader.txt ./api_temp.md >> api.md
|
||||
mv ./api.md $(REPOSITORY_ROOT)/../open-match-docs/site/content/en/docs/Reference/
|
||||
rm ./api_temp.md
|
||||
|
||||
# Include structure of the protos needs to be called out do the dependency chain is run through properly.
|
||||
pkg/pb/backend.pb.go: pkg/pb/messages.pb.go
|
||||
@ -668,14 +721,47 @@ pkg/pb/matchfunction.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/query.pb.go: pkg/pb/messages.pb.go
|
||||
pkg/pb/evaluator.pb.go: pkg/pb/messages.pb.go
|
||||
internal/ipb/synchronizer.pb.go: pkg/pb/messages.pb.go
|
||||
internal/ipb/messages.pb.go: pkg/pb/messages.pb.go
|
||||
|
||||
## ####################################
|
||||
## # Go tasks
|
||||
##
|
||||
|
||||
## # Build assets and binaries
|
||||
## make build
|
||||
##
|
||||
build: assets
|
||||
$(GO) build ./...
|
||||
$(GO) build -tags e2ecluster ./...
|
||||
|
||||
define test_folder
|
||||
$(if $(wildcard $(1)/go.mod), \
|
||||
cd $(1) && \
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -race ./... && \
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -run IgnoreRace$$ ./... \
|
||||
)
|
||||
$(foreach dir, $(wildcard $(1)/*/.), $(call test_folder, $(dir)))
|
||||
endef
|
||||
|
||||
define fast_test_folder
|
||||
$(if $(wildcard $(1)/go.mod), \
|
||||
cd $(1) && \
|
||||
$(GO) test ./... \
|
||||
)
|
||||
$(foreach dir, $(wildcard $(1)/*/.), $(call fast_test_folder, $(dir)))
|
||||
endef
|
||||
|
||||
## # Run go tests
|
||||
## make test
|
||||
##
|
||||
test: $(ALL_PROTOS) tls-certs third_party/
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -race ./...
|
||||
$(GO) test -cover -test.count $(GOLANG_TEST_COUNT) -run IgnoreRace$$ ./...
|
||||
$(call test_folder,.)
|
||||
|
||||
## # Run go tests more quickly, but with worse flake and race detection
|
||||
## make fasttest
|
||||
##
|
||||
fasttest: $(ALL_PROTOS) tls-certs third_party/
|
||||
$(call fast_test_folder,.)
|
||||
|
||||
test-e2e-cluster: all-protos tls-certs third_party/
|
||||
$(HELM) test --timeout 7m30s -v 0 --logs -n $(OPEN_MATCH_KUBERNETES_NAMESPACE) $(OPEN_MATCH_HELM_NAME)
|
||||
@ -690,6 +776,9 @@ vet:
|
||||
golangci: build/toolchain/bin/golangci-lint$(EXE_EXTENSION)
|
||||
GO111MODULE=on $(GOLANGCI) run --config=$(REPOSITORY_ROOT)/.golangci.yaml
|
||||
|
||||
## # Run linter on Go code, charts and terraform
|
||||
## make lint
|
||||
##
|
||||
lint: fmt vet golangci lint-chart terraform-lint
|
||||
|
||||
assets: $(ALL_PROTOS) tls-certs third_party/ build/chart/
|
||||
@ -720,57 +809,6 @@ build/cmd/demo-%/COPY_PHONY:
|
||||
mkdir -p $(BUILD_DIR)/cmd/demo-$*/
|
||||
cp -r examples/demo/static $(BUILD_DIR)/cmd/demo-$*/static
|
||||
|
||||
all: service-binaries example-binaries tools-binaries
|
||||
|
||||
service-binaries: cmd/minimatch/minimatch$(EXE_EXTENSION) cmd/swaggerui/swaggerui$(EXE_EXTENSION)
|
||||
service-binaries: cmd/backend/backend$(EXE_EXTENSION) cmd/frontend/frontend$(EXE_EXTENSION)
|
||||
service-binaries: cmd/query/query$(EXE_EXTENSION) cmd/synchronizer/synchronizer$(EXE_EXTENSION)
|
||||
|
||||
example-binaries: example-mmf-binaries
|
||||
example-mmf-binaries: examples/functions/golang/soloduel/soloduel$(EXE_EXTENSION)
|
||||
|
||||
examples/functions/golang/soloduel/soloduel$(EXE_EXTENSION): pkg/pb/query.pb.go pkg/pb/query.pb.gw.go api/query.swagger.json pkg/pb/matchfunction.pb.go pkg/pb/matchfunction.pb.gw.go api/matchfunction.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/examples/functions/golang/soloduel; $(GO_BUILD_COMMAND)
|
||||
|
||||
test/matchfunction/matchfunction$(EXE_EXTENSION): pkg/pb/query.pb.go pkg/pb/query.pb.gw.go api/query.swagger.json pkg/pb/matchfunction.pb.go pkg/pb/matchfunction.pb.gw.go api/matchfunction.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/test/matchfunction; $(GO_BUILD_COMMAND)
|
||||
|
||||
tools-binaries: tools/certgen/certgen$(EXE_EXTENSION) tools/reaper/reaper$(EXE_EXTENSION)
|
||||
|
||||
cmd/backend/backend$(EXE_EXTENSION): pkg/pb/backend.pb.go pkg/pb/backend.pb.gw.go api/backend.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/cmd/backend; $(GO_BUILD_COMMAND)
|
||||
|
||||
cmd/frontend/frontend$(EXE_EXTENSION): pkg/pb/frontend.pb.go pkg/pb/frontend.pb.gw.go api/frontend.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/cmd/frontend; $(GO_BUILD_COMMAND)
|
||||
|
||||
cmd/query/query$(EXE_EXTENSION): pkg/pb/query.pb.go pkg/pb/query.pb.gw.go api/query.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/cmd/query; $(GO_BUILD_COMMAND)
|
||||
|
||||
cmd/default-evaluator/default-evaluator$(EXE_EXTENSION): pkg/pb/evaluator.pb.go pkg/pb/evaluator.pb.gw.go api/evaluator.swagger.json
|
||||
cd $(REPOSITORY_ROOT)/cmd/evaluator; $(GO_BUILD_COMMAND)
|
||||
|
||||
cmd/synchronizer/synchronizer$(EXE_EXTENSION): internal/ipb/synchronizer.pb.go
|
||||
cd $(REPOSITORY_ROOT)/cmd/synchronizer; $(GO_BUILD_COMMAND)
|
||||
|
||||
# Note: This list of dependencies is long but only add file references here. If you add a .PHONY dependency make will always rebuild it.
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/backend.pb.go pkg/pb/backend.pb.gw.go api/backend.swagger.json
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/frontend.pb.go pkg/pb/frontend.pb.gw.go api/frontend.swagger.json
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/query.pb.go pkg/pb/query.pb.gw.go api/query.swagger.json
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/evaluator.pb.go pkg/pb/evaluator.pb.gw.go api/evaluator.swagger.json
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/matchfunction.pb.go pkg/pb/matchfunction.pb.gw.go api/matchfunction.swagger.json
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): pkg/pb/messages.pb.go
|
||||
cmd/minimatch/minimatch$(EXE_EXTENSION): internal/ipb/synchronizer.pb.go
|
||||
cd $(REPOSITORY_ROOT)/cmd/minimatch; $(GO_BUILD_COMMAND)
|
||||
|
||||
cmd/swaggerui/swaggerui$(EXE_EXTENSION): third_party/swaggerui/
|
||||
cd $(REPOSITORY_ROOT)/cmd/swaggerui; $(GO_BUILD_COMMAND)
|
||||
|
||||
tools/certgen/certgen$(EXE_EXTENSION):
|
||||
cd $(REPOSITORY_ROOT)/tools/certgen/ && $(GO_BUILD_COMMAND)
|
||||
|
||||
tools/reaper/reaper$(EXE_EXTENSION):
|
||||
cd $(REPOSITORY_ROOT)/tools/reaper/ && $(GO_BUILD_COMMAND)
|
||||
|
||||
build/policies/binauthz.yaml: install/policies/binauthz.yaml
|
||||
mkdir -p $(BUILD_DIR)/policies
|
||||
cp -f $(REPOSITORY_ROOT)/install/policies/binauthz.yaml $(BUILD_DIR)/policies/binauthz.yaml
|
||||
@ -811,13 +849,13 @@ md-test: docker
|
||||
|
||||
ci-deploy-artifacts: install/yaml/ $(SWAGGER_JSON_DOCS) build/chart/ gcloud
|
||||
ifeq ($(_GCB_POST_SUBMIT),1)
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/install/yaml/* gs://open-match-chart/install/v$(BASE_VERSION)/yaml/
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/api/*.json gs://open-match-chart/api/v$(BASE_VERSION)/
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/install/yaml/* $(_CHARTS_BUCKET)/install/v$(BASE_VERSION)/yaml/
|
||||
gsutil cp -a public-read $(REPOSITORY_ROOT)/api/*.json $(_CHARTS_BUCKET)/api/v$(BASE_VERSION)/
|
||||
# Deploy Helm Chart
|
||||
# Since each build will refresh just it's version we can allow this for every post submit.
|
||||
# Copy the files into multiple locations to keep a backup.
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* gs://open-match-chart/chart/by-hash/$(VERSION)/
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* gs://open-match-chart/chart/
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* $(_CHARTS_BUCKET)/chart/by-hash/$(VERSION)/
|
||||
gsutil cp -a public-read $(BUILD_DIR)/chart/*.* $(_CHARTS_BUCKET)/chart/
|
||||
else
|
||||
@echo "Not deploying build artifacts to open-match.dev because this is not a post commit change."
|
||||
endif
|
||||
@ -827,7 +865,7 @@ ci-reap-namespaces: build/toolchain/bin/reaper$(EXE_EXTENSION)
|
||||
|
||||
# For presubmit we want to update the protobuf generated files and verify that tests are good.
|
||||
presubmit: GOLANG_TEST_COUNT = 5
|
||||
presubmit: clean third_party/ update-chart-deps assets update-deps lint build install-toolchain test md-test terraform-test
|
||||
presubmit: clean third_party/ update-chart-deps assets update-deps lint build test md-test terraform-test
|
||||
|
||||
build/release/: presubmit clean-install-yaml install/yaml/
|
||||
mkdir -p $(BUILD_DIR)/release/
|
||||
@ -861,19 +899,6 @@ clean-protos:
|
||||
rm -rf $(REPOSITORY_ROOT)/pkg/pb/
|
||||
rm -rf $(REPOSITORY_ROOT)/internal/ipb/
|
||||
|
||||
clean-binaries:
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/backend/backend$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/synchronizer/synchronizer$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/frontend/frontend$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/query/query$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/minimatch/minimatch$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/examples/functions/golang/soloduel/soloduel$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/test/matchfunction/matchfunction$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/test/evaluator/evaluator$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/cmd/swaggerui/swaggerui$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/tools/certgen/certgen$(EXE_EXTENSION)
|
||||
rm -rf $(REPOSITORY_ROOT)/tools/reaper/reaper$(EXE_EXTENSION)
|
||||
|
||||
clean-terraform:
|
||||
rm -rf $(REPOSITORY_ROOT)/install/terraform/.terraform/
|
||||
|
||||
@ -898,7 +923,7 @@ clean-swagger-docs:
|
||||
clean-third-party:
|
||||
rm -rf $(REPOSITORY_ROOT)/third_party/
|
||||
|
||||
clean: clean-images clean-binaries clean-build clean-install-yaml clean-secrets clean-terraform clean-third-party clean-protos clean-swagger-docs
|
||||
clean: clean-images clean-build clean-install-yaml clean-secrets clean-terraform clean-third-party clean-protos clean-swagger-docs
|
||||
|
||||
proxy-frontend: build/toolchain/bin/kubectl$(EXE_EXTENSION)
|
||||
@echo "Frontend Health: http://localhost:$(FRONTEND_PORT)/healthz"
|
||||
@ -959,24 +984,24 @@ proxy:
|
||||
update-deps:
|
||||
$(GO) mod tidy
|
||||
|
||||
third_party/: third_party/google/api third_party/protoc-gen-swagger/options third_party/swaggerui/
|
||||
third_party/: third_party/google/api third_party/protoc-gen-openapiv2/options third_party/swaggerui/
|
||||
|
||||
third_party/google/api:
|
||||
mkdir -p $(TOOLCHAIN_DIR)/googleapis-temp/
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/google/api
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/google/rpc
|
||||
curl -o $(TOOLCHAIN_DIR)/googleapis-temp/googleapis.zip -L https://github.com/googleapis/googleapis/archive/master.zip
|
||||
curl -o $(TOOLCHAIN_DIR)/googleapis-temp/googleapis.zip -L https://github.com/googleapis/googleapis/archive/$(GOOGLE_APIS_VERSION).zip
|
||||
(cd $(TOOLCHAIN_DIR)/googleapis-temp/; unzip -q -o googleapis.zip)
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-master/google/api/*.proto $(REPOSITORY_ROOT)/third_party/google/api/
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-master/google/rpc/*.proto $(REPOSITORY_ROOT)/third_party/google/rpc/
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-$(GOOGLE_APIS_VERSION)/google/api/*.proto $(REPOSITORY_ROOT)/third_party/google/api/
|
||||
cp -f $(TOOLCHAIN_DIR)/googleapis-temp/googleapis-$(GOOGLE_APIS_VERSION)/google/rpc/*.proto $(REPOSITORY_ROOT)/third_party/google/rpc/
|
||||
rm -rf $(TOOLCHAIN_DIR)/googleapis-temp
|
||||
|
||||
third_party/protoc-gen-swagger/options:
|
||||
third_party/protoc-gen-openapiv2/options:
|
||||
mkdir -p $(TOOLCHAIN_DIR)/grpc-gateway-temp/
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/protoc-gen-swagger/options
|
||||
curl -o $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway.zip -L https://github.com/grpc-ecosystem/grpc-gateway/archive/master.zip
|
||||
mkdir -p $(REPOSITORY_ROOT)/third_party/protoc-gen-openapiv2/options
|
||||
curl -o $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway.zip -L https://github.com/grpc-ecosystem/grpc-gateway/archive/v$(GRPC_GATEWAY_VERSION).zip
|
||||
(cd $(TOOLCHAIN_DIR)/grpc-gateway-temp/; unzip -q -o grpc-gateway.zip)
|
||||
cp -f $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway-master/protoc-gen-swagger/options/*.proto $(REPOSITORY_ROOT)/third_party/protoc-gen-swagger/options/
|
||||
cp -f $(TOOLCHAIN_DIR)/grpc-gateway-temp/grpc-gateway-$(GRPC_GATEWAY_VERSION)/protoc-gen-openapiv2/options/*.proto $(REPOSITORY_ROOT)/third_party/protoc-gen-openapiv2/options/
|
||||
rm -rf $(TOOLCHAIN_DIR)/grpc-gateway-temp
|
||||
|
||||
third_party/swaggerui/:
|
||||
|
@ -24,10 +24,6 @@ The [Open Match Development guide](docs/development.md) has detailed instruction
|
||||
on getting the source code, making changes, testing and submitting a pull request
|
||||
to Open Match.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software is currently alpha, and subject to change.
|
||||
|
||||
## Support
|
||||
|
||||
* [Slack Channel](https://open-match.slack.com/) ([Signup](https://join.slack.com/t/open-match/shared_invite/enQtNDM1NjcxNTY4MTgzLTM5ZWQxNjc1YWI3MzJmN2RiMWJmYWI0ZjFiNzNkZmNkMWQ3YWU5OGVkNzA5Yzc4OGVkOGU5MTc0OTA5ZTA5NDU))
|
||||
|
@ -19,9 +19,9 @@ option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Backend"
|
||||
version: "1.0"
|
||||
@ -88,8 +88,12 @@ message ReleaseTicketsRequest{
|
||||
|
||||
message ReleaseTicketsResponse {}
|
||||
|
||||
message ReleaseAllTicketsRequest{}
|
||||
|
||||
message ReleaseAllTicketsResponse {}
|
||||
|
||||
// AssignmentGroup contains an Assignment and the Tickets to which it should be applied.
|
||||
message AssignmentGroup{
|
||||
message AssignmentGroup {
|
||||
// TicketIds is a list of strings representing Open Match generated Ids which apply to an Assignment.
|
||||
repeated string ticket_ids = 1;
|
||||
|
||||
@ -120,9 +124,11 @@ message AssignTicketsResponse {
|
||||
|
||||
// The BackendService implements APIs to generate matches and handle ticket assignments.
|
||||
service BackendService {
|
||||
// FetchMatches triggers a MatchFunction with the specified MatchProfile and returns a set of match proposals that
|
||||
// match the description of that MatchProfile.
|
||||
// FetchMatches immediately returns an error if it encounters any execution failures.
|
||||
// FetchMatches triggers a MatchFunction with the specified MatchProfile and
|
||||
// returns a set of matches generated by the Match Making Function, and
|
||||
// accepted by the evaluator.
|
||||
// Tickets in matches returned by FetchMatches are moved from active to
|
||||
// pending, and will not be returned by query.
|
||||
rpc FetchMatches(FetchMatchesRequest) returns (stream FetchMatchesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/matches:fetch"
|
||||
@ -138,10 +144,8 @@ service BackendService {
|
||||
};
|
||||
}
|
||||
|
||||
// ReleaseTickets removes the submitted tickets from the list that prevents tickets
|
||||
// that are awaiting assignment from appearing in MMF queries, effectively putting them back into
|
||||
// the matchmaking pool
|
||||
//
|
||||
// ReleaseTickets moves tickets from the pending state, to the active state.
|
||||
// This enables them to be returned by query, and find different matches.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc ReleaseTickets(ReleaseTicketsRequest) returns (ReleaseTicketsResponse) {
|
||||
@ -150,4 +154,16 @@ service BackendService {
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// ReleaseAllTickets moves all tickets from the pending state, to the active
|
||||
// state. This enables them to be returned by query, and find different
|
||||
// matches.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc ReleaseAllTickets(ReleaseAllTicketsRequest) returns (ReleaseAllTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/backendservice/tickets:releaseall"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "BackendService"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
@ -26,13 +31,22 @@
|
||||
"paths": {
|
||||
"/v1/backendservice/matches:fetch": {
|
||||
"post": {
|
||||
"summary": "FetchMatches triggers a MatchFunction with the specified MatchProfile and returns a set of match proposals that \nmatch the description of that MatchProfile.\nFetchMatches immediately returns an error if it encounters any execution failures.",
|
||||
"operationId": "FetchMatches",
|
||||
"summary": "FetchMatches triggers a MatchFunction with the specified MatchProfile and\nreturns a set of matches generated by the Match Making Function, and\naccepted by the evaluator.\nTickets in matches returned by FetchMatches are moved from active to\npending, and will not be returned by query.",
|
||||
"operationId": "BackendService_FetchMatches",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchFetchMatchesResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchFetchMatchesResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchFetchMatchesResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -41,6 +55,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -61,7 +81,7 @@
|
||||
"/v1/backendservice/tickets:assign": {
|
||||
"post": {
|
||||
"summary": "AssignTickets overwrites the Assignment field of the input TicketIds.",
|
||||
"operationId": "AssignTickets",
|
||||
"operationId": "BackendService_AssignTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@ -75,6 +95,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -94,9 +120,8 @@
|
||||
},
|
||||
"/v1/backendservice/tickets:release": {
|
||||
"post": {
|
||||
"summary": "ReleaseTickets removes the submitted tickets from the list that prevents tickets \nthat are awaiting assignment from appearing in MMF queries, effectively putting them back into\nthe matchmaking pool",
|
||||
"description": "BETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "ReleaseTickets",
|
||||
"summary": "ReleaseTickets moves tickets from the pending state, to the active state.\nThis enables them to be returned by query, and find different matches.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "BackendService_ReleaseTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@ -110,6 +135,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -126,6 +157,46 @@
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/backendservice/tickets:releaseall": {
|
||||
"post": {
|
||||
"summary": "ReleaseAllTickets moves all tickets from the pending state, to the active\nstate. This enables them to be returned by query, and find different\nmatches.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "BackendService_ReleaseAllTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseAllTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchReleaseAllTicketsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"BackendService"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@ -137,6 +208,17 @@
|
||||
],
|
||||
"default": "UNKNOWN"
|
||||
},
|
||||
"DoubleRangeFilterExclude": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NONE",
|
||||
"MIN",
|
||||
"MAX",
|
||||
"BOTH"
|
||||
],
|
||||
"default": "NONE",
|
||||
"title": "- NONE: No bounds should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c= MAX\n - MIN: Only the minimum bound should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c= MAX\n - MAX: Only the maximum bound should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c MAX\n - BOTH: Both bounds should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c MAX"
|
||||
},
|
||||
"openmatchAssignTicketsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -176,7 +258,7 @@
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket. Open\nmatch does not require or inspect any fields on assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchAssignmentFailure": {
|
||||
"type": "object",
|
||||
@ -207,6 +289,37 @@
|
||||
},
|
||||
"description": "AssignmentGroup contains an Assignment and the Tickets to which it should be applied."
|
||||
},
|
||||
"openmatchBackfill": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by\nthe Match Function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
},
|
||||
"generation": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Generation gets incremented on GameServers update operations.\nPrevents the MMF from overriding a newer version from the game server.\nDo NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs."
|
||||
}
|
||||
},
|
||||
"description": "Represents a backfill entity which is used to fill partially full matches.\n\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -223,6 +336,10 @@
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
},
|
||||
"exclude": {
|
||||
"$ref": "#/definitions/DoubleRangeFilterExclude",
|
||||
"description": "Defines the bounds to apply when filtering tickets by their search_fields.double_args value.\nBETA FEATURE WARNING: This field and the associated values are\nnot finalized and still subject to possible change or removal."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
@ -301,6 +418,14 @@
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "Backfill request which contains additional information to the match\nand contains an association to a GameServer.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"allocate_gameserver": {
|
||||
"type": "boolean",
|
||||
"description": "AllocateGameServer signalise Director that Backfill is new and it should \nallocate a GameServer, this Backfill would be assigned.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
@ -368,6 +493,12 @@
|
||||
},
|
||||
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
|
||||
},
|
||||
"openmatchReleaseAllTicketsRequest": {
|
||||
"type": "object"
|
||||
},
|
||||
"openmatchReleaseAllTicketsResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"openmatchReleaseTicketsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -442,7 +573,7 @@
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
@ -458,10 +589,10 @@
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
@ -478,44 +609,27 @@
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchFetchMatchesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchFetchMatchesResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use."
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchFetchMatchesResponse"
|
||||
"description": "The `Status` type defines a logical error model that is suitable for\ndifferent programming environments, including REST APIs and RPC APIs. It is\nused by [gRPC](https://github.com/grpc). Each `Status` message contains\nthree pieces of data: error code, error message, and error details.\n\nYou can find out more about this error model and how to work with it in the\n[API Design Guide](https://cloud.google.com/apis/design/errors)."
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
@ -19,9 +19,9 @@ option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Evaluator"
|
||||
version: "1.0"
|
||||
@ -52,7 +52,7 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message EvaluateRequest {
|
||||
|
@ -13,6 +13,11 @@
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Evaluator"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
@ -27,12 +32,21 @@
|
||||
"/v1/evaluator/matches:evaluate": {
|
||||
"post": {
|
||||
"summary": "Evaluate evaluates a list of proposed matches based on quality, collision status, and etc, then shortlist the matches and returns the final results.",
|
||||
"operationId": "Evaluate",
|
||||
"operationId": "Evaluator_Evaluate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchEvaluateResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchEvaluateResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchEvaluateResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -41,6 +55,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -76,7 +96,38 @@
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket. Open\nmatch does not require or inspect any fields on assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchBackfill": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by\nthe Match Function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
},
|
||||
"generation": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Generation gets incremented on GameServers update operations.\nPrevents the MMF from overriding a newer version from the game server.\nDo NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs."
|
||||
}
|
||||
},
|
||||
"description": "Represents a backfill entity which is used to fill partially full matches.\n\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal."
|
||||
},
|
||||
"openmatchEvaluateRequest": {
|
||||
"type": "object",
|
||||
@ -124,6 +175,14 @@
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "Backfill request which contains additional information to the match\nand contains an association to a GameServer.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"allocate_gameserver": {
|
||||
"type": "boolean",
|
||||
"description": "AllocateGameServer signalise Director that Backfill is new and it should \nallocate a GameServer, this Backfill would be assigned.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
@ -165,7 +224,7 @@
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
@ -181,10 +240,10 @@
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
@ -201,44 +260,27 @@
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchEvaluateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchEvaluateResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use."
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchEvaluateResponse"
|
||||
"description": "The `Status` type defines a logical error model that is suitable for\ndifferent programming environments, including REST APIs and RPC APIs. It is\nused by [gRPC](https://github.com/grpc). Each `Status` message contains\nthree pieces of data: error code, error message, and error details.\n\nYou can find out more about this error model and how to work with it in the\n[API Design Guide](https://cloud.google.com/apis/design/errors)."
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
@ -19,10 +19,10 @@ option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Frontend"
|
||||
version: "1.0"
|
||||
@ -53,7 +53,7 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message CreateTicketRequest {
|
||||
@ -81,6 +81,57 @@ message WatchAssignmentsResponse {
|
||||
Assignment assignment = 1;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message AcknowledgeBackfillRequest {
|
||||
// An existing ID of Backfill to acknowledge.
|
||||
string backfill_id = 1;
|
||||
|
||||
// An updated Assignment of the requested Backfill.
|
||||
Assignment assignment = 2;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message AcknowledgeBackfillResponse {
|
||||
// The Backfill that was acknowledged.
|
||||
Backfill backfill = 1;
|
||||
|
||||
// All of the Tickets that were successfully assigned
|
||||
repeated Ticket tickets = 2;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message CreateBackfillRequest {
|
||||
// An empty Backfill object.
|
||||
Backfill backfill = 1;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message DeleteBackfillRequest {
|
||||
// An existing ID of Backfill to delete.
|
||||
string backfill_id = 1;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message GetBackfillRequest {
|
||||
// An existing ID of Backfill to retrieve.
|
||||
string backfill_id = 1;
|
||||
}
|
||||
|
||||
// UpdateBackfillRequest - update searchFields, extensions and set assignment.
|
||||
//
|
||||
// BETA FEATURE WARNING: This Request message is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
message UpdateBackfillRequest {
|
||||
// A Backfill object with ID set and fields to update.
|
||||
Backfill backfill = 1;
|
||||
}
|
||||
|
||||
|
||||
// The FrontendService implements APIs to manage and query status of a Tickets.
|
||||
service FrontendService {
|
||||
// CreateTicket assigns an unique TicketId to the input Ticket and record it in state storage.
|
||||
@ -95,9 +146,7 @@ service FrontendService {
|
||||
}
|
||||
|
||||
// DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.
|
||||
// The client must delete the Ticket when finished matchmaking with it.
|
||||
// - If SearchFields exist in a Ticket, DeleteTicket will deindex the fields lazily.
|
||||
// Users may still be able to assign/get a ticket after calling DeleteTicket on it.
|
||||
// The client should delete the Ticket when finished matchmaking with it.
|
||||
rpc DeleteTicket(DeleteTicketRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
delete: "/v1/frontendservice/tickets/{ticket_id}"
|
||||
@ -119,4 +168,55 @@ service FrontendService {
|
||||
get: "/v1/frontendservice/tickets/{ticket_id}/assignments"
|
||||
};
|
||||
}
|
||||
|
||||
// AcknowledgeBackfill is used to notify OpenMatch about GameServer connection info
|
||||
// This triggers an assignment process.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc AcknowledgeBackfill(AcknowledgeBackfillRequest) returns (AcknowledgeBackfillResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/frontendservice/backfills/{backfill_id}/acknowledge"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// CreateBackfill creates a new Backfill object.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc CreateBackfill(CreateBackfillRequest) returns (Backfill) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/frontendservice/backfills"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// DeleteBackfill receives a backfill ID and deletes its resource.
|
||||
// Any tickets waiting for this backfill will be returned to the active pool, no longer pending.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc DeleteBackfill(DeleteBackfillRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
delete: "/v1/frontendservice/backfills/{backfill_id}"
|
||||
};
|
||||
}
|
||||
|
||||
// GetBackfill returns a backfill object by its ID.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc GetBackfill(GetBackfillRequest) returns (Backfill) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/frontendservice/backfills/{backfill_id}"
|
||||
};
|
||||
}
|
||||
|
||||
// UpdateBackfill updates search_fields and extensions for the backfill with the provided id.
|
||||
// Any tickets waiting for this backfill will be returned to the active pool, no longer pending.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc UpdateBackfill(UpdateBackfillRequest) returns (Backfill) {
|
||||
option (google.api.http) = {
|
||||
patch: "/v1/frontendservice/backfills"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "FrontendService"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
@ -24,10 +29,211 @@
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/frontendservice/backfills": {
|
||||
"post": {
|
||||
"summary": "CreateBackfill creates a new Backfill object.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "FrontendService_CreateBackfill",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchBackfill"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchCreateBackfillRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"summary": "UpdateBackfill updates search_fields and extensions for the backfill with the provided id.\nAny tickets waiting for this backfill will be returned to the active pool, no longer pending.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "FrontendService_UpdateBackfill",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchBackfill"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchUpdateBackfillRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/frontendservice/backfills/{backfill_id}": {
|
||||
"get": {
|
||||
"summary": "GetBackfill returns a backfill object by its ID.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "FrontendService_GetBackfill",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchBackfill"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "backfill_id",
|
||||
"description": "An existing ID of Backfill to retrieve.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "DeleteBackfill receives a backfill ID and deletes its resource.\nAny tickets waiting for this backfill will be returned to the active pool, no longer pending.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "FrontendService_DeleteBackfill",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "backfill_id",
|
||||
"description": "An existing ID of Backfill to delete.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/frontendservice/backfills/{backfill_id}/acknowledge": {
|
||||
"post": {
|
||||
"summary": "AcknowledgeBackfill is used to notify OpenMatch about GameServer connection info\nThis triggers an assignment process.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "FrontendService_AcknowledgeBackfill",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchAcknowledgeBackfillResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "backfill_id",
|
||||
"description": "An existing ID of Backfill to acknowledge.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchAcknowledgeBackfillRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"FrontendService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/frontendservice/tickets": {
|
||||
"post": {
|
||||
"summary": "CreateTicket assigns an unique TicketId to the input Ticket and record it in state storage.\nA ticket is considered as ready for matchmaking once it is created.\n - If a TicketId exists in a Ticket request, an auto-generated TicketId will override this field.\n - If SearchFields exist in a Ticket, CreateTicket will also index these fields such that one can query the ticket with query.QueryTickets function.",
|
||||
"operationId": "CreateTicket",
|
||||
"operationId": "FrontendService_CreateTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@ -41,6 +247,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -61,7 +273,7 @@
|
||||
"/v1/frontendservice/tickets/{ticket_id}": {
|
||||
"get": {
|
||||
"summary": "GetTicket get the Ticket associated with the specified TicketId.",
|
||||
"operationId": "GetTicket",
|
||||
"operationId": "FrontendService_GetTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@ -75,6 +287,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -91,8 +309,8 @@
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"summary": "DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.\nThe client must delete the Ticket when finished matchmaking with it. \n - If SearchFields exist in a Ticket, DeleteTicket will deindex the fields lazily.\nUsers may still be able to assign/get a ticket after calling DeleteTicket on it.",
|
||||
"operationId": "DeleteTicket",
|
||||
"summary": "DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.\nThe client should delete the Ticket when finished matchmaking with it.",
|
||||
"operationId": "FrontendService_DeleteTicket",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
@ -106,6 +324,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -125,12 +349,21 @@
|
||||
"/v1/frontendservice/tickets/{ticket_id}/assignments": {
|
||||
"get": {
|
||||
"summary": "WatchAssignments stream back Assignment of the specified TicketId if it is updated.\n - If the Assignment is not updated, GetAssignment will retry using the configured backoff strategy.",
|
||||
"operationId": "WatchAssignments",
|
||||
"operationId": "FrontendService_WatchAssignments",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchWatchAssignmentsResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchWatchAssignmentsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchWatchAssignmentsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -139,6 +372,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -157,6 +396,37 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"openmatchAcknowledgeBackfillRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backfill_id": {
|
||||
"type": "string",
|
||||
"description": "An existing ID of Backfill to acknowledge."
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An updated Assignment of the requested Backfill."
|
||||
}
|
||||
},
|
||||
"description": "BETA FEATURE WARNING: This Request message is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"openmatchAcknowledgeBackfillResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "The Backfill that was acknowledged."
|
||||
},
|
||||
"tickets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchTicket"
|
||||
},
|
||||
"title": "All of the Tickets that were successfully assigned"
|
||||
}
|
||||
},
|
||||
"description": "BETA FEATURE WARNING: This Request message is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -172,7 +442,48 @@
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket. Open\nmatch does not require or inspect any fields on assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchBackfill": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by\nthe Match Function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
},
|
||||
"generation": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Generation gets incremented on GameServers update operations.\nPrevents the MMF from overriding a newer version from the game server.\nDo NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs."
|
||||
}
|
||||
},
|
||||
"description": "Represents a backfill entity which is used to fill partially full matches.\n\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal."
|
||||
},
|
||||
"openmatchCreateBackfillRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "An empty Backfill object."
|
||||
}
|
||||
},
|
||||
"description": "BETA FEATURE WARNING: This Request message is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"openmatchCreateTicketRequest": {
|
||||
"type": "object",
|
||||
@ -220,7 +531,7 @@
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
@ -236,10 +547,20 @@
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"openmatchUpdateBackfillRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "A Backfill object with ID set and fields to update."
|
||||
}
|
||||
},
|
||||
"description": "UpdateBackfillRequest - update searchFields, extensions and set assignment.\n\nBETA FEATURE WARNING: This Request message is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"openmatchWatchAssignmentsResponse": {
|
||||
"type": "object",
|
||||
@ -265,44 +586,27 @@
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchWatchAssignmentsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchWatchAssignmentsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use."
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchWatchAssignmentsResponse"
|
||||
"description": "The `Status` type defines a logical error model that is suitable for\ndifferent programming environments, including REST APIs and RPC APIs. It is\nused by [gRPC](https://github.com/grpc). Each `Status` message contains\nthree pieces of data: error code, error message, and error details.\n\nYou can find out more about this error model and how to work with it in the\n[API Design Guide](https://cloud.google.com/apis/design/errors)."
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
@ -19,9 +19,9 @@ option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "Match Function"
|
||||
version: "1.0"
|
||||
@ -69,8 +69,9 @@ message RunResponse {
|
||||
// The MatchFunction service implements APIs to run user-defined matchmaking logics.
|
||||
service MatchFunction {
|
||||
// DO NOT CALL THIS FUNCTION MANUALLY. USE backend.FetchMatches INSTEAD.
|
||||
// Run pulls Tickets that satisify Profile constraints from QueryService, runs matchmaking logics against them, then
|
||||
// constructs and streams back match candidates to the Backend service.
|
||||
// Run pulls Tickets that satisfy Profile constraints from QueryService,
|
||||
// runs matchmaking logic against them, then constructs and streams back
|
||||
// match candidates to the Backend service.
|
||||
rpc Run(RunRequest) returns (stream RunResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/matchfunction:run"
|
||||
|
@ -13,6 +13,11 @@
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "MatchFunction"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
@ -26,13 +31,22 @@
|
||||
"paths": {
|
||||
"/v1/matchfunction:run": {
|
||||
"post": {
|
||||
"summary": "DO NOT CALL THIS FUNCTION MANUALLY. USE backend.FetchMatches INSTEAD.\nRun pulls Tickets that satisify Profile constraints from QueryService, runs matchmaking logics against them, then\nconstructs and streams back match candidates to the Backend service.",
|
||||
"operationId": "Run",
|
||||
"summary": "DO NOT CALL THIS FUNCTION MANUALLY. USE backend.FetchMatches INSTEAD.\nRun pulls Tickets that satisfy Profile constraints from QueryService,\nruns matchmaking logic against them, then constructs and streams back\nmatch candidates to the Backend service.",
|
||||
"operationId": "MatchFunction_Run",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchRunResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchRunResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchRunResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -41,6 +55,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -60,6 +80,17 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"DoubleRangeFilterExclude": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NONE",
|
||||
"MIN",
|
||||
"MAX",
|
||||
"BOTH"
|
||||
],
|
||||
"default": "NONE",
|
||||
"title": "- NONE: No bounds should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c= MAX\n - MIN: Only the minimum bound should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c= MAX\n - MAX: Only the maximum bound should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c MAX\n - BOTH: Both bounds should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c MAX"
|
||||
},
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -75,7 +106,38 @@
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket. Open\nmatch does not require or inspect any fields on assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchBackfill": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by\nthe Match Function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
},
|
||||
"generation": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Generation gets incremented on GameServers update operations.\nPrevents the MMF from overriding a newer version from the game server.\nDo NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs."
|
||||
}
|
||||
},
|
||||
"description": "Represents a backfill entity which is used to fill partially full matches.\n\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
@ -93,6 +155,10 @@
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
},
|
||||
"exclude": {
|
||||
"$ref": "#/definitions/DoubleRangeFilterExclude",
|
||||
"description": "Defines the bounds to apply when filtering tickets by their search_fields.double_args value.\nBETA FEATURE WARNING: This field and the associated values are\nnot finalized and still subject to possible change or removal."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
@ -125,6 +191,14 @@
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"backfill": {
|
||||
"$ref": "#/definitions/openmatchBackfill",
|
||||
"description": "Backfill request which contains additional information to the match\nand contains an association to a GameServer.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
},
|
||||
"allocate_gameserver": {
|
||||
"type": "boolean",
|
||||
"description": "AllocateGameServer signalise Director that Backfill is new and it should \nallocate a GameServer, this Backfill would be assigned.\nBETA FEATURE WARNING: This field is not finalized and still subject\nto possible change or removal."
|
||||
}
|
||||
},
|
||||
"description": "A Match is used to represent a completed match object. It can be generated by\na MatchFunction as a proposal or can be returned by OpenMatch as a result in\nresponse to the FetchMatches call.\nWhen a match is returned by the FetchMatches call, it should contain at least\none ticket to be considered as valid."
|
||||
@ -269,7 +343,7 @@
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
@ -285,10 +359,10 @@
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
@ -305,44 +379,27 @@
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchRunResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchRunResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use."
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchRunResponse"
|
||||
"description": "The `Status` type defines a logical error model that is suitable for\ndifferent programming environments, including REST APIs and RPC APIs. It is\nused by [gRPC](https://github.com/grpc). Each `Status` message contains\nthree pieces of data: error code, error message, and error details.\n\nYou can find out more about this error model and how to work with it in the\n[API Design Guide](https://cloud.google.com/apis/design/errors)."
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
@ -21,16 +21,18 @@ import "google/rpc/status.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an
|
||||
// individual 'Player' or a 'Group' of players. Open Match will not interpret
|
||||
// what the Ticket represents but just treat it as a matchmaking unit with a set
|
||||
// of SearchFields. Open Match stores the Ticket in state storage and enables an
|
||||
// Assignment to be associated with this Ticket.
|
||||
// A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent
|
||||
// an individual 'Player', a 'Group' of players, or any other concepts unique to
|
||||
// your use case. Open Match will not interpret what the Ticket represents but
|
||||
// just treat it as a matchmaking unit with a set of SearchFields. Open Match
|
||||
// stores the Ticket in state storage and enables an Assignment to be set on the
|
||||
// Ticket.
|
||||
message Ticket {
|
||||
// Id represents an auto-generated Id issued by Open Match.
|
||||
string id = 1;
|
||||
|
||||
// An Assignment represents a game server assignment associated with a Ticket.
|
||||
// An Assignment represents a game server assignment associated with a Ticket,
|
||||
// or whatever finalized matched state means for your use case.
|
||||
// Open Match does not require or inspect any fields on Assignment.
|
||||
Assignment assignment = 3;
|
||||
|
||||
@ -43,8 +45,8 @@ message Ticket {
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 5;
|
||||
|
||||
// Create time represents the time at which this Ticket was created. It is
|
||||
// populated by Open Match at the time of Ticket creation.
|
||||
// Create time is the time the Ticket was created. It is populated by Open
|
||||
// Match at the time of Ticket creation.
|
||||
google.protobuf.Timestamp create_time = 6;
|
||||
|
||||
// Deprecated fields.
|
||||
@ -64,8 +66,8 @@ message SearchFields {
|
||||
repeated string tags = 3;
|
||||
}
|
||||
|
||||
// An Assignment represents a game server assignment associated with a Ticket. Open
|
||||
// match does not require or inspect any fields on assignment.
|
||||
// An Assignment represents a game server assignment associated with a Ticket.
|
||||
// Open Match does not require or inspect any fields on assignment.
|
||||
message Assignment {
|
||||
// Connection information for this Assignment.
|
||||
string connection = 1;
|
||||
@ -101,6 +103,25 @@ message DoubleRangeFilter {
|
||||
|
||||
// Minimum value.
|
||||
double min = 3;
|
||||
|
||||
enum Exclude {
|
||||
// No bounds should be excluded when evaluating the filter, i.e.: MIN <= x <= MAX
|
||||
NONE = 0;
|
||||
|
||||
// Only the minimum bound should be excluded when evaluating the filter, i.e.: MIN < x <= MAX
|
||||
MIN = 1;
|
||||
|
||||
// Only the maximum bound should be excluded when evaluating the filter, i.e.: MIN <= x < MAX
|
||||
MAX = 2;
|
||||
|
||||
// Both bounds should be excluded when evaluating the filter, i.e.: MIN < x < MAX
|
||||
BOTH = 3;
|
||||
}
|
||||
|
||||
// Defines the bounds to apply when filtering tickets by their search_fields.double_args value.
|
||||
// BETA FEATURE WARNING: This field and the associated values are
|
||||
// not finalized and still subject to possible change or removal.
|
||||
Exclude exclude = 4;
|
||||
}
|
||||
|
||||
// Filters strings exactly equaling a value.
|
||||
@ -199,6 +220,45 @@ message Match {
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 7;
|
||||
|
||||
// Backfill request which contains additional information to the match
|
||||
// and contains an association to a GameServer.
|
||||
// BETA FEATURE WARNING: This field is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
Backfill backfill = 8;
|
||||
|
||||
// AllocateGameServer signalise Director that Backfill is new and it should
|
||||
// allocate a GameServer, this Backfill would be assigned.
|
||||
// BETA FEATURE WARNING: This field is not finalized and still subject
|
||||
// to possible change or removal.
|
||||
bool allocate_gameserver = 9;
|
||||
|
||||
// Deprecated fields.
|
||||
reserved 5, 6;
|
||||
}
|
||||
|
||||
// Represents a backfill entity which is used to fill partially full matches.
|
||||
//
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
message Backfill {
|
||||
// Id represents an auto-generated Id issued by Open Match.
|
||||
string id = 1;
|
||||
|
||||
// Search fields are the fields which Open Match is aware of, and can be used
|
||||
// when specifying filters.
|
||||
SearchFields search_fields = 2;
|
||||
|
||||
// Customized information not inspected by Open Match, to be used by
|
||||
// the Match Function, evaluator, and components making calls to Open Match.
|
||||
// Optional, depending on the requirements of the connected systems.
|
||||
map<string, google.protobuf.Any> extensions = 3;
|
||||
|
||||
// Create time is the time the Ticket was created. It is populated by Open
|
||||
// Match at the time of Ticket creation.
|
||||
google.protobuf.Timestamp create_time = 4;
|
||||
|
||||
// Generation gets incremented on GameServers update operations.
|
||||
// Prevents the MMF from overriding a newer version from the game server.
|
||||
// Do NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs.
|
||||
int64 generation = 5;
|
||||
}
|
@ -19,9 +19,9 @@ option csharp_namespace = "OpenMatch";
|
||||
|
||||
import "api/messages.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "protoc-gen-swagger/options/annotations.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
|
||||
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
title: "MM Logic (Data Layer)"
|
||||
version: "1.0"
|
||||
@ -52,7 +52,7 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
|
||||
}
|
||||
// TODO Add annotations for security_defintiions.
|
||||
// See
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/proto/examplepb/a_bit_of_everything.proto
|
||||
// https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto
|
||||
};
|
||||
|
||||
message QueryTicketsRequest {
|
||||
@ -75,12 +75,26 @@ message QueryTicketIdsResponse {
|
||||
repeated string ids = 1;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request messages are not finalized and
|
||||
// still subject to possible change or removal.
|
||||
message QueryBackfillsRequest {
|
||||
// The Pool representing the set of Filters to be queried.
|
||||
Pool pool = 1;
|
||||
}
|
||||
|
||||
// BETA FEATURE WARNING: This Request messages are not finalized and
|
||||
// still subject to possible change or removal.
|
||||
message QueryBackfillsResponse {
|
||||
// Backfills that meet all the filtering criteria requested by the pool.
|
||||
repeated Backfill backfills = 1;
|
||||
}
|
||||
|
||||
// The QueryService service implements helper APIs for Match Function to query Tickets from state storage.
|
||||
service QueryService {
|
||||
// QueryTickets gets a list of Tickets that match all Filters of the input Pool.
|
||||
// - If the Pool contains no Filters, QueryTickets will return all Tickets in the state storage.
|
||||
// QueryTickets pages the Tickets by `storage.pool.size` and stream back responses.
|
||||
// - storage.pool.size is default to 1000 if not set, and has a mininum of 10 and maximum of 10000.
|
||||
// QueryTickets pages the Tickets by `queryPageSize` and stream back responses.
|
||||
// - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.
|
||||
rpc QueryTickets(QueryTicketsRequest) returns (stream QueryTicketsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/queryservice/tickets:query"
|
||||
@ -90,12 +104,22 @@ service QueryService {
|
||||
|
||||
// QueryTicketIds gets the list of TicketIDs that meet all the filtering criteria requested by the pool.
|
||||
// - If the Pool contains no Filters, QueryTicketIds will return all TicketIDs in the state storage.
|
||||
// QueryTicketIds pages the TicketIDs by `storage.pool.size` and stream back responses.
|
||||
// - storage.pool.size is default to 1000 if not set, and has a mininum of 10 and maximum of 10000.
|
||||
// QueryTicketIds pages the TicketIDs by `queryPageSize` and stream back responses.
|
||||
// - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.
|
||||
rpc QueryTicketIds(QueryTicketIdsRequest) returns (stream QueryTicketIdsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/queryservice/ticketids:query"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// QueryBackfills gets a list of Backfills.
|
||||
// BETA FEATURE WARNING: This call and the associated Request and Response
|
||||
// messages are not finalized and still subject to possible change or removal.
|
||||
rpc QueryBackfills(QueryBackfillsRequest) returns (stream QueryBackfillsResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/queryservice/backfills:query"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
"url": "https://github.com/googleforgames/open-match/blob/master/LICENSE"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "QueryService"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
"http",
|
||||
"https"
|
||||
@ -24,15 +29,24 @@
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/queryservice/ticketids:query": {
|
||||
"/v1/queryservice/backfills:query": {
|
||||
"post": {
|
||||
"summary": "QueryTicketIds gets the list of TicketIDs that meet all the filtering criteria requested by the pool.\n - If the Pool contains no Filters, QueryTicketIds will return all TicketIDs in the state storage.\nQueryTicketIds pages the TicketIDs by `storage.pool.size` and stream back responses.\n - storage.pool.size is default to 1000 if not set, and has a mininum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryTicketIds",
|
||||
"summary": "QueryBackfills gets a list of Backfills.\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal.",
|
||||
"operationId": "QueryService_QueryBackfills",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchQueryTicketIdsResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryBackfillsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryBackfillsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -41,6 +55,61 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/openmatchQueryBackfillsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"QueryService"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/queryservice/ticketids:query": {
|
||||
"post": {
|
||||
"summary": "QueryTicketIds gets the list of TicketIDs that meet all the filtering criteria requested by the pool.\n - If the Pool contains no Filters, QueryTicketIds will return all TicketIDs in the state storage.\nQueryTicketIds pages the TicketIDs by `queryPageSize` and stream back responses.\n - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryService_QueryTicketIds",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketIdsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketIdsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Returned when the resource does not exist.",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -60,13 +129,22 @@
|
||||
},
|
||||
"/v1/queryservice/tickets:query": {
|
||||
"post": {
|
||||
"summary": "QueryTickets gets a list of Tickets that match all Filters of the input Pool.\n - If the Pool contains no Filters, QueryTickets will return all Tickets in the state storage.\nQueryTickets pages the Tickets by `storage.pool.size` and stream back responses.\n - storage.pool.size is default to 1000 if not set, and has a mininum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryTickets",
|
||||
"summary": "QueryTickets gets a list of Tickets that match all Filters of the input Pool.\n - If the Pool contains no Filters, QueryTickets will return all Tickets in the state storage.\nQueryTickets pages the Tickets by `queryPageSize` and stream back responses.\n - queryPageSize is default to 1000 if not set, and has a minimum of 10 and maximum of 10000.",
|
||||
"operationId": "QueryService_QueryTickets",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.(streaming responses)",
|
||||
"schema": {
|
||||
"$ref": "#/x-stream-definitions/openmatchQueryTicketsResponse"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketsResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
@ -75,6 +153,12 @@
|
||||
"type": "string",
|
||||
"format": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -94,6 +178,17 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"DoubleRangeFilterExclude": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NONE",
|
||||
"MIN",
|
||||
"MAX",
|
||||
"BOTH"
|
||||
],
|
||||
"default": "NONE",
|
||||
"title": "- NONE: No bounds should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c= MAX\n - MIN: Only the minimum bound should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c= MAX\n - MAX: Only the maximum bound should be excluded when evaluating the filter, i.e.: MIN \u003c= x \u003c MAX\n - BOTH: Both bounds should be excluded when evaluating the filter, i.e.: MIN \u003c x \u003c MAX"
|
||||
},
|
||||
"openmatchAssignment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -109,7 +204,38 @@
|
||||
"description": "Customized information not inspected by Open Match, to be used by the match\nmaking function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
}
|
||||
},
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket. Open\nmatch does not require or inspect any fields on assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on assignment."
|
||||
},
|
||||
"openmatchBackfill": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Id represents an auto-generated Id issued by Open Match."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
"description": "Search fields are the fields which Open Match is aware of, and can be used\nwhen specifying filters."
|
||||
},
|
||||
"extensions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
},
|
||||
"description": "Customized information not inspected by Open Match, to be used by\nthe Match Function, evaluator, and components making calls to Open Match.\nOptional, depending on the requirements of the connected systems."
|
||||
},
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
},
|
||||
"generation": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "Generation gets incremented on GameServers update operations.\nPrevents the MMF from overriding a newer version from the game server.\nDo NOT read or write to this field, it is for internal tracking, and changing the value will cause bugs."
|
||||
}
|
||||
},
|
||||
"description": "Represents a backfill entity which is used to fill partially full matches.\n\nBETA FEATURE WARNING: This call and the associated Request and Response\nmessages are not finalized and still subject to possible change or removal."
|
||||
},
|
||||
"openmatchDoubleRangeFilter": {
|
||||
"type": "object",
|
||||
@ -127,6 +253,10 @@
|
||||
"type": "number",
|
||||
"format": "double",
|
||||
"description": "Minimum value."
|
||||
},
|
||||
"exclude": {
|
||||
"$ref": "#/definitions/DoubleRangeFilterExclude",
|
||||
"description": "Defines the bounds to apply when filtering tickets by their search_fields.double_args value.\nBETA FEATURE WARNING: This field and the associated values are\nnot finalized and still subject to possible change or removal."
|
||||
}
|
||||
},
|
||||
"title": "Filters numerical values to only those within a range.\n double_arg: \"foo\"\n max: 10\n min: 5\nmatches:\n {\"foo\": 5}\n {\"foo\": 7.5}\n {\"foo\": 10}\ndoes not match:\n {\"foo\": 4}\n {\"foo\": 10.01}\n {\"foo\": \"7.5\"}\n {}"
|
||||
@ -170,6 +300,29 @@
|
||||
},
|
||||
"description": "Pool specfies a set of criteria that are used to select a subset of Tickets\nthat meet all the criteria."
|
||||
},
|
||||
"openmatchQueryBackfillsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pool": {
|
||||
"$ref": "#/definitions/openmatchPool",
|
||||
"description": "The Pool representing the set of Filters to be queried."
|
||||
}
|
||||
},
|
||||
"description": "BETA FEATURE WARNING: This Request messages are not finalized and \nstill subject to possible change or removal."
|
||||
},
|
||||
"openmatchQueryBackfillsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backfills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/openmatchBackfill"
|
||||
},
|
||||
"description": "Backfills that meet all the filtering criteria requested by the pool."
|
||||
}
|
||||
},
|
||||
"description": "BETA FEATURE WARNING: This Request messages are not finalized and \nstill subject to possible change or removal."
|
||||
},
|
||||
"openmatchQueryTicketIdsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -271,7 +424,7 @@
|
||||
},
|
||||
"assignment": {
|
||||
"$ref": "#/definitions/openmatchAssignment",
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
"description": "An Assignment represents a game server assignment associated with a Ticket,\nor whatever finalized matched state means for your use case.\nOpen Match does not require or inspect any fields on Assignment."
|
||||
},
|
||||
"search_fields": {
|
||||
"$ref": "#/definitions/openmatchSearchFields",
|
||||
@ -287,10 +440,10 @@
|
||||
"create_time": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Create time represents the time at which this Ticket was created. It is\npopulated by Open Match at the time of Ticket creation."
|
||||
"description": "Create time is the time the Ticket was created. It is populated by Open\nMatch at the time of Ticket creation."
|
||||
}
|
||||
},
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket represents either an\nindividual 'Player' or a 'Group' of players. Open Match will not interpret\nwhat the Ticket represents but just treat it as a matchmaking unit with a set\nof SearchFields. Open Match stores the Ticket in state storage and enables an\nAssignment to be associated with this Ticket."
|
||||
"description": "A Ticket is a basic matchmaking entity in Open Match. A Ticket may represent\nan individual 'Player', a 'Group' of players, or any other concepts unique to\nyour use case. Open Match will not interpret what the Ticket represents but\njust treat it as a matchmaking unit with a set of SearchFields. Open Match\nstores the Ticket in state storage and enables an Assignment to be set on the\nTicket."
|
||||
},
|
||||
"protobufAny": {
|
||||
"type": "object",
|
||||
@ -307,56 +460,27 @@
|
||||
},
|
||||
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
|
||||
},
|
||||
"runtimeStreamError": {
|
||||
"rpcStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"grpc_code": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"http_code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"format": "int32",
|
||||
"description": "The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]."
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"http_status": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"description": "A developer-facing error message, which should be in English. Any\nuser-facing error message should be localized and sent in the\n[google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client."
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/protobufAny"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-stream-definitions": {
|
||||
"openmatchQueryTicketIdsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketIdsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
},
|
||||
"description": "A list of messages that carry the error details. There is a common set of\nmessage types for APIs to use."
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketIdsResponse"
|
||||
},
|
||||
"openmatchQueryTicketsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"$ref": "#/definitions/openmatchQueryTicketsResponse"
|
||||
},
|
||||
"error": {
|
||||
"$ref": "#/definitions/runtimeStreamError"
|
||||
}
|
||||
},
|
||||
"title": "Stream result of openmatchQueryTicketsResponse"
|
||||
"description": "The `Status` type defines a logical error model that is suitable for\ndifferent programming environments, including REST APIs and RPC APIs. It is\nused by [gRPC](https://github.com/grpc). Each `Status` message contains\nthree pieces of data: error code, error message, and error details.\n\nYou can find out more about this error model and how to work with it in the\n[API Design Guide](https://cloud.google.com/apis/design/errors)."
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
@ -90,7 +90,7 @@ steps:
|
||||
|
||||
- id: 'Build: Assets'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', 'assets', '-j12']
|
||||
args: ['make', '_CHARTS_BUCKET=${_CHARTS_BUCKET}', 'assets', '-j12']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
path: '/go'
|
||||
@ -132,7 +132,7 @@ steps:
|
||||
|
||||
- id: 'Deploy: Deployment Configs'
|
||||
name: 'gcr.io/$PROJECT_ID/open-match-build'
|
||||
args: ['make', '_GCB_POST_SUBMIT=${_GCB_POST_SUBMIT}', '_GCB_LATEST_VERSION=${_GCB_LATEST_VERSION}', 'SHORT_SHA=${SHORT_SHA}', 'BRANCH_NAME=${BRANCH_NAME}', 'ci-deploy-artifacts']
|
||||
args: ['make', '_GCB_POST_SUBMIT=${_GCB_POST_SUBMIT}', '_GCB_LATEST_VERSION=${_GCB_LATEST_VERSION}', 'SHORT_SHA=${SHORT_SHA}', 'BRANCH_NAME=${BRANCH_NAME}', '_CHARTS_BUCKET=${_CHARTS_BUCKET}', 'ci-deploy-artifacts']
|
||||
waitFor: ['Lint: Format, Vet, Charts', 'Test: Deploy Open Match']
|
||||
volumes:
|
||||
- name: 'go-vol'
|
||||
@ -153,7 +153,7 @@ steps:
|
||||
|
||||
artifacts:
|
||||
objects:
|
||||
location: gs://open-match-build-artifacts/output/
|
||||
location: '${_ARTIFACTS_BUCKET}'
|
||||
paths:
|
||||
- install/yaml/install.yaml
|
||||
- install/yaml/01-open-match-core.yaml
|
||||
@ -164,10 +164,13 @@ artifacts:
|
||||
- install/yaml/06-open-match-override-configmap.yaml
|
||||
|
||||
substitutions:
|
||||
_OM_VERSION: "0.0.0-dev"
|
||||
_OM_VERSION: "1.3.0"
|
||||
_GCB_POST_SUBMIT: "0"
|
||||
_GCB_LATEST_VERSION: "undefined"
|
||||
logsBucket: 'gs://open-match-build-logs/'
|
||||
_ARTIFACTS_BUCKET: "gs://open-match-build-artifacts/output/"
|
||||
_LOGS_BUCKET: "gs://open-match-build-logs/"
|
||||
_CHARTS_BUCKET: "gs://open-match-chart"
|
||||
logsBucket: '${_LOGS_BUCKET}'
|
||||
options:
|
||||
sourceProvenanceHash: ['SHA256']
|
||||
machineType: 'N1_HIGHCPU_32'
|
||||
|
@ -16,11 +16,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/app/backend"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("backend", config.Read, backend.BindService)
|
||||
appmain.RunApplication("backend", backend.BindService)
|
||||
}
|
||||
|
@ -11,14 +11,14 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app/evaluator"
|
||||
"open-match.dev/open-match/internal/app/evaluator/defaulteval"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Invoke the harness to setup a GRPC service that handles requests to run the evaluator.
|
||||
evaluator.RunEvaluator(defaulteval.Evaluate)
|
||||
appmain.RunApplication("evaluator", defaulteval.BindService)
|
||||
}
|
||||
|
@ -16,11 +16,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/app/frontend"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("frontend", config.Read, frontend.BindService)
|
||||
appmain.RunApplication("frontend", frontend.BindService)
|
||||
}
|
||||
|
@ -16,11 +16,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/app/minimatch"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("minimatch", config.Read, minimatch.BindService)
|
||||
appmain.RunApplication("minimatch", minimatch.BindService)
|
||||
}
|
||||
|
@ -16,11 +16,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/app/query"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("query", config.Read, query.BindService)
|
||||
appmain.RunApplication("query", query.BindService)
|
||||
}
|
||||
|
@ -16,10 +16,9 @@ package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/examples/scale/backend"
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("scale", config.Read, backend.BindService)
|
||||
appmain.RunApplication("scale", backend.BindService)
|
||||
}
|
||||
|
@ -16,10 +16,9 @@ package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/examples/scale/frontend"
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("scale", config.Read, frontend.BindService)
|
||||
appmain.RunApplication("scale", frontend.BindService)
|
||||
}
|
||||
|
@ -16,11 +16,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/app/synchronizer"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.RunApplication("synchronizer", config.Read, synchronizer.BindService)
|
||||
appmain.RunApplication("synchronizer", synchronizer.BindService)
|
||||
}
|
||||
|
@ -9,14 +9,12 @@ To build Open Match you'll need the following applications installed.
|
||||
|
||||
* [Git](https://git-scm.com/downloads)
|
||||
* [Go](https://golang.org/doc/install)
|
||||
* [Python3 with virtualenv](https://wiki.python.org/moin/BeginnersGuide/Download)
|
||||
* Make (Mac: install [XCode](https://itunes.apple.com/us/app/xcode/id497799835))
|
||||
* [Docker](https://docs.docker.com/install/) including the
|
||||
[post-install steps](https://docs.docker.com/install/linux/linux-postinstall/).
|
||||
|
||||
Optional Software
|
||||
|
||||
* [Google Cloud Platform](gcloud.md)
|
||||
* [Visual Studio Code](https://code.visualstudio.com/Download) for IDE.
|
||||
Vim and Emacs work to.
|
||||
* [VirtualBox](https://www.virtualbox.org/wiki/Downloads) recommended for
|
||||
@ -27,8 +25,7 @@ running:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -q python3 python3-virtualenv virtualenv make \
|
||||
google-cloud-sdk git unzip tar
|
||||
sudo apt-get install -y -q make google-cloud-sdk git unzip tar
|
||||
```
|
||||
|
||||
*It's recommended that you install Go using their instructions because package
|
||||
@ -49,15 +46,13 @@ make
|
||||
|
||||
*Typically for contributing you'll want to
|
||||
[create a fork](https://help.github.com/en/articles/fork-a-repo) and use that
|
||||
but for purpose of this guide we'll be using the upstream/master.*
|
||||
but for purpose of this guide we'll be using the upstream/main.*
|
||||
|
||||
## Building
|
||||
## Building code and images
|
||||
|
||||
```bash
|
||||
# Reset workspace
|
||||
make clean
|
||||
# Compile all the binaries
|
||||
make all -j$(nproc)
|
||||
# Run tests
|
||||
make test
|
||||
# Build all the images.
|
||||
@ -87,11 +82,9 @@ default context the Makefile will honor that._
|
||||
# GKE cluster: make create-gke-cluster/delete-gke-cluster
|
||||
# or create a local Minikube cluster
|
||||
make create-gke-cluster
|
||||
# Step 2: Download helm and install Tiller in the cluster
|
||||
make push-helm
|
||||
# Step 3: Build and Push Open Match Images to gcr.io
|
||||
# Step 2: Build and Push Open Match Images to gcr.io
|
||||
make push-images -j$(nproc)
|
||||
# Install Open Match in the cluster.
|
||||
# Step 3: Install Open Match in the cluster.
|
||||
make install-chart
|
||||
|
||||
# Create a proxy to Open Match pods so that you can access them locally.
|
||||
@ -105,12 +98,29 @@ make proxy
|
||||
make delete-chart
|
||||
```
|
||||
|
||||
## Interaction
|
||||
## Iterating
|
||||
While iterating on the project, you may need to:
|
||||
1. Install/Run everything
|
||||
2. Make some code changes
|
||||
3. Make sure the changes compile by running `make test`
|
||||
4. Build and push Docker images to your personal registry by running `make push-images -j$(nproc)`
|
||||
5. Deploy the code change by running `make install-chart`
|
||||
6. Verify it's working by [looking at the logs](#accessing-logs) or looking at the monitoring dashboard by running `make proxy-grafana`
|
||||
7. Tear down Open Match by running `make delete-chart`
|
||||
|
||||
Before integrating with Open Match you can manually interact with it to get a feel for how it works.
|
||||
## Accessing logs
|
||||
To look at Open Match core services' logs, run:
|
||||
```bash
|
||||
# Replace open-match-frontend with the service name that you would like to access
|
||||
kubectl logs -n open-match svc/open-match-frontend
|
||||
```
|
||||
|
||||
`make proxy-ui` exposes the Swagger UI for Open Match locally on your computer.
|
||||
You can then go to http://localhost:51500 and view the API as well as interactively call Open Match.
|
||||
## API References
|
||||
While integrating with Open Match you may want to understand its API surface concepts or interact with it and get a feel for how it works.
|
||||
|
||||
The APIs are defined in `proto` format under the `api/` folder, with references available at [open-match.dev](https://open-match.dev/site/docs/reference/api/).
|
||||
|
||||
You can also run `make proxy-ui` to exposes the Swagger UI for Open Match locally on your computer after [deploying it to Kubernetes](#deploying-to-kubernetes), then go to http://localhost:51500 and view the REST APIs as well as interactively call Open Match.
|
||||
|
||||
By default you will be talking to the frontend server but you can change the target API url to any of the following:
|
||||
|
||||
@ -144,55 +154,9 @@ export GOPATH=$HOME/workspace/
|
||||
|
||||
## Pull Requests
|
||||
|
||||
If you want to submit a Pull Request there's some tools to help prepare your
|
||||
change.
|
||||
|
||||
```bash
|
||||
# Runs code generators, tests, and linters.
|
||||
make presubmit
|
||||
```
|
||||
|
||||
`make presubmit` catches most of the issues your change can run into. If the
|
||||
submit checks fail you can run it locally via,
|
||||
|
||||
```bash
|
||||
make local-cloud-build
|
||||
```
|
||||
If you want to submit a Pull Request, `make presubmit` can catch most of the issues your change can run into.
|
||||
|
||||
Our [continuous integration](https://console.cloud.google.com/cloud-build/builds?project=open-match-build)
|
||||
runs against all PRs. In order to see your build results you'll need to
|
||||
become a member of
|
||||
[open-match-discuss@googlegroups.com](https://groups.google.com/forum/#!forum/open-match-discuss).
|
||||
|
||||
|
||||
## Makefile
|
||||
|
||||
The Makefile is the core of Open Match's build process. There's a lot of
|
||||
commands but here's a list of the important ones and patterns to remember them.
|
||||
|
||||
```bash
|
||||
# Help
|
||||
make
|
||||
|
||||
# Reset workspace (delete all build artifacts)
|
||||
make clean
|
||||
# Delete auto-generated protobuf code and swagger API docs.
|
||||
make clean-protos clean-swagger-docs
|
||||
# make clean-* deletes some part of the build outputs.
|
||||
|
||||
# Build all Docker images
|
||||
make build-images
|
||||
# Build frontend docker image.
|
||||
make build-frontend-image
|
||||
|
||||
# Formats, Vets, and tests the codebase.
|
||||
make fmt vet test
|
||||
# Same as above also regenerates autogen files.
|
||||
make presubmit
|
||||
|
||||
# Run website on http://localhost:8080
|
||||
make run-site
|
||||
|
||||
# Proxy all Open Match processes to view them.
|
||||
make proxy
|
||||
```
|
||||
|
@ -12,24 +12,13 @@ SOURCE_VERSION=$1
|
||||
DEST_VERSION=$2
|
||||
SOURCE_PROJECT_ID=open-match-build
|
||||
DEST_PROJECT_ID=open-match-public-images
|
||||
IMAGE_NAMES="openmatch-backend openmatch-frontend openmatch-query openmatch-synchronizer openmatch-minimatch openmatch-demo-first-match openmatch-mmf-go-soloduel openmatch-mmf-go-pool openmatch-evaluator-go-simple openmatch-swaggerui openmatch-reaper"
|
||||
IMAGE_NAMES=$(make list-images)
|
||||
|
||||
for name in $IMAGE_NAMES
|
||||
do
|
||||
source_image=gcr.io/$SOURCE_PROJECT_ID/$name:$SOURCE_VERSION
|
||||
dest_image=gcr.io/$DEST_PROJECT_ID/$name:$DEST_VERSION
|
||||
source_image=gcr.io/$SOURCE_PROJECT_ID/openmatch-$name:$SOURCE_VERSION
|
||||
dest_image=gcr.io/$DEST_PROJECT_ID/openmatch-$name:$DEST_VERSION
|
||||
docker pull $source_image
|
||||
docker tag $source_image $dest_image
|
||||
docker push $dest_image
|
||||
done
|
||||
|
||||
echo "=============================================================="
|
||||
echo "=============================================================="
|
||||
echo "=============================================================="
|
||||
echo "=============================================================="
|
||||
|
||||
echo "Add these lines to your release notes:"
|
||||
for name in $IMAGE_NAMES
|
||||
do
|
||||
echo "docker pull gcr.io/$DEST_PROJECT_ID/$name:$DEST_VERSION"
|
||||
done
|
||||
|
7
docs/hugo_apiheader.txt
Normal file
7
docs/hugo_apiheader.txt
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: "Open Match API References"
|
||||
linkTitle: "Open Match API References"
|
||||
weight: 2
|
||||
description:
|
||||
This document provides API references for Open Match services.
|
||||
---
|
@ -37,7 +37,7 @@ func New() *ByteSub {
|
||||
}
|
||||
}
|
||||
|
||||
// AnnounceLatest writes b to all of the subscribers, with caviets listed in Subscribe.
|
||||
// AnnounceLatest writes b to all of the subscribers, with caveats listed in Subscribe.
|
||||
func (s *ByteSub) AnnounceLatest(b []byte) {
|
||||
s.r.Lock()
|
||||
defer s.r.Unlock()
|
||||
|
@ -51,7 +51,7 @@ func TestFastAndSlow(t *testing.T) {
|
||||
for count := 0; true; count++ {
|
||||
if v := <-slow; v == "3" {
|
||||
if count > 1 {
|
||||
t.Error("Expected to recieve at most 1 other value on slow before recieving the latest value.")
|
||||
t.Error("Expected to receive at most 1 other value on slow before receiving the latest value.")
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ func runScenario(ctx context.Context, name string, update updater.SetFunc) {
|
||||
update(s)
|
||||
|
||||
// See https://open-match.dev/site/docs/guides/api/
|
||||
conn, err := grpc.Dial("om-frontend.open-match.svc.cluster.local:50504", grpc.WithInsecure())
|
||||
conn, err := grpc.Dial("open-match-frontend.open-match.svc.cluster.local:50504", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ func run(ds *components.DemoShared) {
|
||||
ds.Update(s)
|
||||
|
||||
// See https://open-match.dev/site/docs/guides/api/
|
||||
conn, err := grpc.Dial("om-backend.open-match.svc.cluster.local:50505", grpc.WithInsecure())
|
||||
conn, err := grpc.Dial("open-match-backend.open-match.svc.cluster.local:50505", grpc.WithInsecure())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ type Updater struct {
|
||||
type SetFunc func(v interface{})
|
||||
|
||||
// New creates an Updater. Set is called when fields update, using the json
|
||||
// sererialized value of Updater's tree. All updates after ctx is canceled are
|
||||
// serialized value of Updater's tree. All updates after ctx is canceled are
|
||||
// ignored.
|
||||
func New(ctx context.Context, set func([]byte)) *Updater {
|
||||
f := func(v interface{}) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2019 Google LLC
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -14,11 +14,11 @@
|
||||
|
||||
FROM open-match-base-build as builder
|
||||
|
||||
WORKDIR /go/src/open-match.dev/open-match/test/matchfunction
|
||||
WORKDIR /go/src/open-match.dev/open-match/examples/functions/golang/backfill
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o matchfunction .
|
||||
|
||||
FROM gcr.io/distroless/static:nonroot
|
||||
WORKDIR /app/
|
||||
COPY --from=builder --chown=nonroot /go/src/open-match.dev/open-match/test/matchfunction/matchfunction /app/
|
||||
COPY --from=builder --chown=nonroot /go/src/open-match.dev/open-match/examples/functions/golang/backfill/matchfunction /app/
|
||||
|
||||
ENTRYPOINT ["/app/matchfunction"]
|
||||
ENTRYPOINT ["/app/matchfunction"]
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 Google LLC
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package main a sample match function that uses the GRPC harness to set up
|
||||
// Package main defines a sample match function that uses the GRPC harness to set up
|
||||
// the match making function as a service. This sample is a reference
|
||||
// to demonstrate the usage of the GRPC harness and should only be used as
|
||||
// a starting point for your match function. You will need to modify the
|
||||
@ -20,16 +20,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
internalMmf "open-match.dev/open-match/internal/testing/mmf"
|
||||
"open-match.dev/open-match/test/matchfunction/mmf"
|
||||
"open-match.dev/open-match/examples/functions/golang/backfill/mmf"
|
||||
)
|
||||
|
||||
const (
|
||||
queryServiceAddr = "open-match-query.open-match.svc.cluster.local:50503" // Address of the QueryService endpoint.
|
||||
serverPort = 50502 // The port for hosting the Match Function.
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Invoke the harness to setup a GRPC service that handles requests to run the
|
||||
// match function. The harness itself queries open match for player pools for
|
||||
// the specified request and passes the pools to the match function to generate
|
||||
// proposals.
|
||||
internalMmf.RunMatchFunction(&internalMmf.FunctionSettings{
|
||||
Func: mmf.MakeMatches,
|
||||
})
|
||||
mmf.Start(queryServiceAddr, serverPort)
|
||||
}
|
297
examples/functions/golang/backfill/mmf/matchfunction.go
Normal file
297
examples/functions/golang/backfill/mmf/matchfunction.go
Normal file
@ -0,0 +1,297 @@
|
||||
// Copyright 2020 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 mmf provides a sample match function that uses the GRPC harness to set up 1v1 matches.
|
||||
// This sample is a reference to demonstrate the usage of backfill and should only be used as
|
||||
// a starting point for your match function. You will need to modify the
|
||||
// matchmaking logic in this function based on your game's requirements.
|
||||
package mmf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"log"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/pkg/matchfunction"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
const (
|
||||
playersPerMatch = 2
|
||||
openSlotsKey = "open-slots"
|
||||
matchName = "backfill-matchfunction"
|
||||
)
|
||||
|
||||
// matchFunctionService implements pb.MatchFunctionServer, the server generated
|
||||
// by compiling the protobuf, by fulfilling the pb.MatchFunctionServer interface.
|
||||
type matchFunctionService struct {
|
||||
grpc *grpc.Server
|
||||
queryServiceClient pb.QueryServiceClient
|
||||
port int
|
||||
}
|
||||
|
||||
func (s *matchFunctionService) Run(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
|
||||
log.Printf("Generating proposals for function %v", req.GetProfile().GetName())
|
||||
|
||||
var proposals []*pb.Match
|
||||
profile := req.GetProfile()
|
||||
pools := profile.GetPools()
|
||||
|
||||
for _, p := range pools {
|
||||
tickets, err := matchfunction.QueryPool(stream.Context(), s.queryServiceClient, p)
|
||||
if err != nil {
|
||||
log.Printf("Failed to query tickets for the given pool, got %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
backfills, err := matchfunction.QueryBackfillPool(stream.Context(), s.queryServiceClient, p)
|
||||
if err != nil {
|
||||
log.Printf("Failed to query backfills for the given pool, got %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
matches, err := makeMatches(profile, p, tickets, backfills)
|
||||
if err != nil {
|
||||
log.Printf("Failed to generate matches, got %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
proposals = append(proposals, matches...)
|
||||
}
|
||||
|
||||
log.Printf("Streaming %v proposals to Open Match", len(proposals))
|
||||
// Stream the generated proposals back to Open Match.
|
||||
for _, proposal := range proposals {
|
||||
if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil {
|
||||
log.Printf("Failed to stream proposals to Open Match, got %s", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makeMatches tries to handle backfills at first, then it makes full matches, at the end it makes a match with backfill
|
||||
// if tickets left
|
||||
func makeMatches(profile *pb.MatchProfile, pool *pb.Pool, tickets []*pb.Ticket, backfills []*pb.Backfill) ([]*pb.Match, error) {
|
||||
var matches []*pb.Match
|
||||
newMatches, remainingTickets, err := handleBackfills(profile, tickets, backfills, len(matches))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches = append(matches, newMatches...)
|
||||
newMatches, remainingTickets = makeFullMatches(profile, remainingTickets, len(matches))
|
||||
matches = append(matches, newMatches...)
|
||||
|
||||
if len(remainingTickets) > 0 {
|
||||
match, err := makeMatchWithBackfill(profile, pool, remainingTickets, len(matches))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches = append(matches, match)
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// handleBackfills looks at each backfill's openSlots which is a number of required tickets,
|
||||
// acquires that tickets, decreases openSlots in backfill and makes a match with updated backfill and associated tickets.
|
||||
func handleBackfills(profile *pb.MatchProfile, tickets []*pb.Ticket, backfills []*pb.Backfill, lastMatchId int) ([]*pb.Match, []*pb.Ticket, error) {
|
||||
matchId := lastMatchId
|
||||
var matches []*pb.Match
|
||||
|
||||
for _, b := range backfills {
|
||||
openSlots, err := getOpenSlots(b)
|
||||
if err != nil {
|
||||
return nil, tickets, err
|
||||
}
|
||||
|
||||
var matchTickets []*pb.Ticket
|
||||
for openSlots > 0 && len(tickets) > 0 {
|
||||
matchTickets = append(matchTickets, tickets[0])
|
||||
tickets = tickets[1:]
|
||||
openSlots--
|
||||
}
|
||||
|
||||
if len(matchTickets) > 0 {
|
||||
err := setOpenSlots(b, openSlots)
|
||||
if err != nil {
|
||||
return nil, tickets, err
|
||||
}
|
||||
|
||||
matchId++
|
||||
match := newMatch(matchId, profile.Name, matchTickets, b)
|
||||
matches = append(matches, &match)
|
||||
}
|
||||
}
|
||||
|
||||
return matches, tickets, nil
|
||||
}
|
||||
|
||||
// makeMatchWithBackfill makes not full match, creates backfill for it with openSlots = playersPerMatch-len(tickets).
|
||||
func makeMatchWithBackfill(profile *pb.MatchProfile, pool *pb.Pool, tickets []*pb.Ticket, lastMatchId int) (*pb.Match, error) {
|
||||
if len(tickets) == 0 {
|
||||
return nil, fmt.Errorf("tickets are required")
|
||||
}
|
||||
|
||||
if len(tickets) >= playersPerMatch {
|
||||
return nil, fmt.Errorf("too many tickets")
|
||||
}
|
||||
|
||||
matchId := lastMatchId
|
||||
searchFields := newSearchFields(pool)
|
||||
backfill, err := newBackfill(searchFields, playersPerMatch-len(tickets))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matchId++
|
||||
match := newMatch(matchId, profile.Name, tickets, backfill)
|
||||
// indicates that it is a new match and new game server should be allocated for it
|
||||
match.AllocateGameserver = true
|
||||
|
||||
return &match, nil
|
||||
}
|
||||
|
||||
// makeFullMatches makes matches without backfill
|
||||
func makeFullMatches(profile *pb.MatchProfile, tickets []*pb.Ticket, lastMatchId int) ([]*pb.Match, []*pb.Ticket) {
|
||||
ticketNum := 0
|
||||
matchId := lastMatchId
|
||||
var matches []*pb.Match
|
||||
|
||||
for ticketNum < playersPerMatch && len(tickets) >= playersPerMatch {
|
||||
ticketNum++
|
||||
|
||||
if ticketNum == playersPerMatch {
|
||||
matchId++
|
||||
|
||||
match := newMatch(matchId, profile.Name, tickets[:playersPerMatch], nil)
|
||||
matches = append(matches, &match)
|
||||
|
||||
tickets = tickets[playersPerMatch:]
|
||||
ticketNum = 0
|
||||
}
|
||||
}
|
||||
|
||||
return matches, tickets
|
||||
}
|
||||
|
||||
// newSearchFields creates search fields based on pool's search criteria. This is just example of how it can be done.
|
||||
func newSearchFields(pool *pb.Pool) *pb.SearchFields {
|
||||
searchFields := pb.SearchFields{}
|
||||
rangeFilters := pool.GetDoubleRangeFilters()
|
||||
|
||||
if rangeFilters != nil {
|
||||
doubleArgs := make(map[string]float64)
|
||||
for _, f := range rangeFilters {
|
||||
doubleArgs[f.DoubleArg] = (f.Max - f.Min) / 2
|
||||
}
|
||||
|
||||
if len(doubleArgs) > 0 {
|
||||
searchFields.DoubleArgs = doubleArgs
|
||||
}
|
||||
}
|
||||
|
||||
stringFilters := pool.GetStringEqualsFilters()
|
||||
|
||||
if stringFilters != nil {
|
||||
stringArgs := make(map[string]string)
|
||||
for _, f := range stringFilters {
|
||||
stringArgs[f.StringArg] = f.Value
|
||||
}
|
||||
|
||||
if len(stringArgs) > 0 {
|
||||
searchFields.StringArgs = stringArgs
|
||||
}
|
||||
}
|
||||
|
||||
tagFilters := pool.GetTagPresentFilters()
|
||||
|
||||
if tagFilters != nil {
|
||||
tags := make([]string, len(tagFilters))
|
||||
for _, f := range tagFilters {
|
||||
tags = append(tags, f.Tag)
|
||||
}
|
||||
|
||||
if len(tags) > 0 {
|
||||
searchFields.Tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
return &searchFields
|
||||
}
|
||||
|
||||
func newBackfill(searchFields *pb.SearchFields, openSlots int) (*pb.Backfill, error) {
|
||||
b := pb.Backfill{
|
||||
SearchFields: searchFields,
|
||||
Generation: 0,
|
||||
CreateTime: ptypes.TimestampNow(),
|
||||
}
|
||||
|
||||
err := setOpenSlots(&b, int32(openSlots))
|
||||
return &b, err
|
||||
}
|
||||
|
||||
func newMatch(num int, profile string, tickets []*pb.Ticket, b *pb.Backfill) pb.Match {
|
||||
t := time.Now().Format("2006-01-02T15:04:05.00")
|
||||
|
||||
return pb.Match{
|
||||
MatchId: fmt.Sprintf("profile-%s-time-%s-num-%d", profile, t, num),
|
||||
MatchProfile: profile,
|
||||
MatchFunction: matchName,
|
||||
Tickets: tickets,
|
||||
Backfill: b,
|
||||
}
|
||||
}
|
||||
|
||||
func setOpenSlots(b *pb.Backfill, val int32) error {
|
||||
if b.Extensions == nil {
|
||||
b.Extensions = make(map[string]*any.Any)
|
||||
}
|
||||
|
||||
any, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: val})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Extensions[openSlotsKey] = any
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOpenSlots(b *pb.Backfill) (int32, error) {
|
||||
if b == nil {
|
||||
return 0, fmt.Errorf("expected backfill is not nil")
|
||||
}
|
||||
|
||||
if b.Extensions != nil {
|
||||
if any, ok := b.Extensions[openSlotsKey]; ok {
|
||||
var val wrappers.Int32Value
|
||||
err := ptypes.UnmarshalAny(any, &val)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return val.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
return playersPerMatch, nil
|
||||
}
|
142
examples/functions/golang/backfill/mmf/matchfunction_test.go
Normal file
142
examples/functions/golang/backfill/mmf/matchfunction_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright 2020 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 mmf
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"github.com/stretchr/testify/require"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
func TestHandleBackfills(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
tickets []*pb.Ticket
|
||||
backfills []*pb.Backfill
|
||||
lastMatchId int
|
||||
expectedMatchLen int
|
||||
expectedTicketLen int
|
||||
expectedOpenSlots int32
|
||||
expectedErr bool
|
||||
}{
|
||||
{name: "returns no matches when no backfills specified", expectedMatchLen: 0, expectedTicketLen: 0},
|
||||
{name: "returns no matches when no tickets specified", expectedMatchLen: 0, expectedTicketLen: 0},
|
||||
{name: "returns a match with open slots decreased", tickets: []*pb.Ticket{{Id: "1"}}, backfills: []*pb.Backfill{withOpenSlots(1)}, expectedMatchLen: 1, expectedTicketLen: 0, expectedOpenSlots: playersPerMatch - 2},
|
||||
} {
|
||||
testCase := tc
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
profile := pb.MatchProfile{Name: "matchProfile"}
|
||||
matches, tickets, err := handleBackfills(&profile, testCase.tickets, testCase.backfills, testCase.lastMatchId)
|
||||
require.Equal(t, testCase.expectedErr, err != nil)
|
||||
require.Equal(t, testCase.expectedTicketLen, len(tickets))
|
||||
|
||||
if err != nil {
|
||||
require.Equal(t, 0, len(matches))
|
||||
} else {
|
||||
for _, m := range matches {
|
||||
require.NotNil(t, m.Backfill)
|
||||
|
||||
openSlots, err := getOpenSlots(m.Backfill)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testCase.expectedOpenSlots, openSlots)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeMatchWithBackfill(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
tickets []*pb.Ticket
|
||||
lastMatchId int
|
||||
expectedOpenSlots int32
|
||||
expectedErr bool
|
||||
}{
|
||||
{name: "returns an error when length of tickets is greater then playerPerMatch", tickets: []*pb.Ticket{{Id: "1"}, {Id: "2"}, {Id: "3"}, {Id: "4"}, {Id: "5"}}, expectedErr: true},
|
||||
{name: "returns an error when length of tickets is equal to playerPerMatch", tickets: []*pb.Ticket{{Id: "1"}, {Id: "2"}, {Id: "3"}, {Id: "4"}}, expectedErr: true},
|
||||
{name: "returns an error when no tickets are provided", expectedErr: true},
|
||||
{name: "returns a match with backfill", tickets: []*pb.Ticket{{Id: "1"}}, expectedOpenSlots: playersPerMatch - 1},
|
||||
} {
|
||||
testCase := testCase
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pool := pb.Pool{}
|
||||
profile := pb.MatchProfile{Name: "matchProfile"}
|
||||
match, err := makeMatchWithBackfill(&profile, &pool, testCase.tickets, testCase.lastMatchId)
|
||||
require.Equal(t, testCase.expectedErr, err != nil)
|
||||
|
||||
if err == nil {
|
||||
require.NotNil(t, match)
|
||||
require.NotNil(t, match.Backfill)
|
||||
require.True(t, match.AllocateGameserver)
|
||||
require.Equal(t, "", match.Backfill.Id)
|
||||
|
||||
openSlots, err := getOpenSlots(match.Backfill)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, testCase.expectedOpenSlots, openSlots)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeFullMatches(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
tickets []*pb.Ticket
|
||||
lastMatchId int
|
||||
expectedMatchLen int
|
||||
expectedTicketLen int
|
||||
}{
|
||||
{name: "returns no matches when there are no tickets", tickets: []*pb.Ticket{}, expectedMatchLen: 0, expectedTicketLen: 0},
|
||||
{name: "returns no matches when length of tickets is less then playersPerMatch", tickets: []*pb.Ticket{{Id: "1"}}, expectedMatchLen: 0, expectedTicketLen: 1},
|
||||
{name: "returns a match when length of tickets is greater then playersPerMatch", tickets: []*pb.Ticket{{Id: "1"}, {Id: "2"}}, expectedMatchLen: 1, expectedTicketLen: 0},
|
||||
} {
|
||||
testCase := testCase
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
profile := pb.MatchProfile{Name: "matchProfile"}
|
||||
matches, tickets := makeFullMatches(&profile, testCase.tickets, testCase.lastMatchId)
|
||||
|
||||
require.Equal(t, testCase.expectedMatchLen, len(matches))
|
||||
require.Equal(t, testCase.expectedTicketLen, len(tickets))
|
||||
|
||||
for _, m := range matches {
|
||||
require.Nil(t, m.Backfill)
|
||||
require.Equal(t, playersPerMatch, len(m.Tickets))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func withOpenSlots(openSlots int) *pb.Backfill {
|
||||
val, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: int32(openSlots)})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &pb.Backfill{
|
||||
Extensions: map[string]*any.Any{
|
||||
openSlotsKey: val,
|
||||
},
|
||||
}
|
||||
}
|
59
examples/functions/golang/backfill/mmf/server.go
Normal file
59
examples/functions/golang/backfill/mmf/server.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2020 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 mmf provides a sample match function that uses the GRPC harness to set up 1v1 matches.
|
||||
// This sample is a reference to demonstrate the usage of backfill and should only be used as
|
||||
// a starting point for your match function. You will need to modify the
|
||||
// matchmaking logic in this function based on your game's requirements.
|
||||
package mmf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
func Start(queryServiceAddr string, serverPort int) {
|
||||
// Connect to QueryService.
|
||||
conn, err := grpc.Dial(queryServiceAddr, grpc.WithInsecure())
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to Open Match, got %s", err.Error())
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
mmfService := matchFunctionService{
|
||||
queryServiceClient: pb.NewQueryServiceClient(conn),
|
||||
}
|
||||
|
||||
// Create and host a new gRPC service on the configured port.
|
||||
server := grpc.NewServer()
|
||||
pb.RegisterMatchFunctionServer(server, &mmfService)
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("TCP net listener initialization failed for port %v, got %s", serverPort, err.Error())
|
||||
}
|
||||
|
||||
log.Printf("TCP net listener initialized for port %v", serverPort)
|
||||
err = server.Serve(ln)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("gRPC serve failed, got %s", err.Error())
|
||||
}
|
||||
}
|
@ -24,8 +24,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
queryServiceAddr = "om-query.open-match.svc.cluster.local:50503" // Address of the QueryService endpoint.
|
||||
serverPort = 50502 // The port for hosting the Match Function.
|
||||
queryServiceAddr = "open-match-query.open-match.svc.cluster.local:50503" // Address of the QueryService endpoint.
|
||||
serverPort = 50502 // The port for hosting the Match Function.
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -19,11 +19,11 @@ import (
|
||||
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMakeMatchesDeduplicate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
poolNameToTickets := map[string][]*pb.Ticket{
|
||||
"pool1": {{Id: "1"}},
|
||||
@ -31,12 +31,12 @@ func TestMakeMatchesDeduplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
matches, err := makeMatches(poolNameToTickets)
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(matches), 0)
|
||||
require.Nil(err)
|
||||
require.Equal(len(matches), 0)
|
||||
}
|
||||
|
||||
func TestMakeMatches(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
poolNameToTickets := map[string][]*pb.Ticket{
|
||||
"pool1": {{Id: "1"}, {Id: "2"}, {Id: "3"}},
|
||||
@ -45,11 +45,11 @@ func TestMakeMatches(t *testing.T) {
|
||||
}
|
||||
|
||||
matches, err := makeMatches(poolNameToTickets)
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(matches), 3)
|
||||
require.Nil(err)
|
||||
require.Equal(len(matches), 3)
|
||||
|
||||
for _, match := range matches {
|
||||
assert.Equal(2, len(match.Tickets))
|
||||
assert.Equal(matchName, match.MatchFunction)
|
||||
require.Equal(2, len(match.Tickets))
|
||||
require.Equal(matchName, match.MatchFunction)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/trace"
|
||||
"open-match.dev/open-match/examples/scale/scenarios"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
@ -39,22 +40,22 @@ var (
|
||||
|
||||
activeScenario = scenarios.ActiveScenario
|
||||
|
||||
mIterations = telemetry.Counter("scale_backend_iterations", "fetch match iterations")
|
||||
mFetchMatchCalls = telemetry.Counter("scale_backend_fetch_match_calls", "fetch match calls")
|
||||
mFetchMatchSuccesses = telemetry.Counter("scale_backend_fetch_match_successes", "fetch match successes")
|
||||
mFetchMatchErrors = telemetry.Counter("scale_backend_fetch_match_errors", "fetch match errors")
|
||||
mMatchesReturned = telemetry.Counter("scale_backend_matches_returned", "matches returned")
|
||||
mSumTicketsReturned = telemetry.Counter("scale_backend_sum_tickets_returned", "tickets in matches returned")
|
||||
mMatchesAssigned = telemetry.Counter("scale_backend_matches_assigned", "matches assigned")
|
||||
mMatchAssignsFailed = telemetry.Counter("scale_backend_match_assigns_failed", "match assigns failed")
|
||||
mTicketsDeleted = telemetry.Counter("scale_backend_tickets_deleted", "tickets deleted")
|
||||
mTicketDeletesFailed = telemetry.Counter("scale_backend_ticket_deletes_failed", "ticket deletes failed")
|
||||
mIterations = telemetry.Counter("scale_backend_iterations", "fetch match iterations")
|
||||
mFetchMatchCalls = telemetry.Counter("scale_backend_fetch_match_calls", "fetch match calls")
|
||||
mFetchMatchSuccesses = telemetry.Counter("scale_backend_fetch_match_successes", "fetch match successes")
|
||||
mFetchMatchErrors = telemetry.Counter("scale_backend_fetch_match_errors", "fetch match errors")
|
||||
mMatchesReturned = telemetry.Counter("scale_backend_matches_returned", "matches returned")
|
||||
mSumTicketsReturned = telemetry.Counter("scale_backend_sum_tickets_returned", "tickets in matches returned")
|
||||
mMatchesAssigned = telemetry.Counter("scale_backend_matches_assigned", "matches assigned")
|
||||
mMatchAssignsFailed = telemetry.Counter("scale_backend_match_assigns_failed", "match assigns failed")
|
||||
mBackfillsDeleted = telemetry.Counter("scale_backend_backfills_deleted", "backfills deleted")
|
||||
mBackfillDeletesFailed = telemetry.Counter("scale_backend_backfill_deletes_failed", "backfill deletes failed")
|
||||
)
|
||||
|
||||
// Run triggers execution of functions that continuously fetch, assign and
|
||||
// delete matches.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View) error {
|
||||
go run(cfg)
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
go run(p.Config())
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -78,12 +79,28 @@ func run(cfg config.View) {
|
||||
w := logger.Writer()
|
||||
defer w.Close()
|
||||
|
||||
matchesForAssignment := make(chan *pb.Match, 30000)
|
||||
ticketsForDeletion := make(chan string, 30000)
|
||||
matchesToAssign := make(chan *pb.Match, 30000)
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
go runAssignments(be, matchesForAssignment, ticketsForDeletion)
|
||||
go runDeletions(fe, ticketsForDeletion)
|
||||
if activeScenario.BackendAssignsTickets {
|
||||
for i := 0; i < 100; i++ {
|
||||
go runAssignments(be, matchesToAssign)
|
||||
}
|
||||
}
|
||||
|
||||
backfillsToDelete := make(chan *pb.Backfill, 30000)
|
||||
|
||||
if activeScenario.BackendDeletesBackfills {
|
||||
for i := 0; i < 100; i++ {
|
||||
go runDeleteBackfills(fe, backfillsToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
matchesToAcknowledge := make(chan *pb.Match, 30000)
|
||||
|
||||
if activeScenario.BackendAcknowledgesBackfills {
|
||||
for i := 0; i < 100; i++ {
|
||||
go runAcknowledgeBackfills(fe, matchesToAcknowledge, backfillsToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't go faster than this, as it likely means that FetchMatches is throwing
|
||||
@ -97,7 +114,7 @@ func run(cfg config.View) {
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup, p *pb.MatchProfile) {
|
||||
defer wg.Done()
|
||||
runFetchMatches(be, p, matchesForAssignment)
|
||||
runFetchMatches(be, p, matchesToAssign, matchesToAcknowledge)
|
||||
}(&wg, p)
|
||||
}
|
||||
|
||||
@ -107,13 +124,13 @@ func run(cfg config.View) {
|
||||
}
|
||||
}
|
||||
|
||||
func runFetchMatches(be pb.BackendServiceClient, p *pb.MatchProfile, matchesForAssignment chan<- *pb.Match) {
|
||||
func runFetchMatches(be pb.BackendServiceClient, p *pb.MatchProfile, matchesToAssign chan<- *pb.Match, matchesToAcknowledge chan<- *pb.Match) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "scale.backend/FetchMatches")
|
||||
defer span.End()
|
||||
|
||||
req := &pb.FetchMatchesRequest{
|
||||
Config: &pb.FunctionConfig{
|
||||
Host: "om-function",
|
||||
Host: "open-match-function",
|
||||
Port: 50502,
|
||||
Type: pb.FunctionConfig_GRPC,
|
||||
},
|
||||
@ -145,62 +162,90 @@ func runFetchMatches(be pb.BackendServiceClient, p *pb.MatchProfile, matchesForA
|
||||
telemetry.RecordNUnitMeasurement(ctx, mSumTicketsReturned, int64(len(resp.GetMatch().Tickets)))
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchesReturned)
|
||||
|
||||
matchesForAssignment <- resp.GetMatch()
|
||||
if activeScenario.BackendAssignsTickets {
|
||||
matchesToAssign <- resp.GetMatch()
|
||||
}
|
||||
|
||||
if activeScenario.BackendAcknowledgesBackfills {
|
||||
matchesToAcknowledge <- resp.GetMatch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runAssignments(be pb.BackendServiceClient, matchesForAssignment <-chan *pb.Match, ticketsForDeletion chan<- string) {
|
||||
func runDeleteBackfills(fe pb.FrontendServiceClient, backfillsToDelete <-chan *pb.Backfill) {
|
||||
for b := range backfillsToDelete {
|
||||
if !activeScenario.BackfillDeleteCond(b) {
|
||||
continue
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_, err := fe.DeleteBackfill(ctx, &pb.DeleteBackfillRequest{BackfillId: b.Id})
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to delete backfill: %s", b.Id)
|
||||
telemetry.RecordUnitMeasurement(ctx, mBackfillDeletesFailed)
|
||||
} else {
|
||||
telemetry.RecordUnitMeasurement(ctx, mBackfillsDeleted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runAcknowledgeBackfills(fe pb.FrontendServiceClient, matchesToAcknowledge <-chan *pb.Match, backfillsToDelete chan<- *pb.Backfill) {
|
||||
for m := range matchesToAcknowledge {
|
||||
backfillId := m.Backfill.GetId()
|
||||
if backfillId == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
err := acknowledgeBackfill(fe, backfillId)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to acknowledge backfill: %s", backfillId)
|
||||
continue
|
||||
}
|
||||
|
||||
if activeScenario.BackendDeletesBackfills {
|
||||
backfillsToDelete <- m.Backfill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func acknowledgeBackfill(fe pb.FrontendServiceClient, backfillId string) error {
|
||||
ctx, span := trace.StartSpan(context.Background(), "scale.frontend/AcknowledgeBackfill")
|
||||
defer span.End()
|
||||
|
||||
_, err := fe.AcknowledgeBackfill(ctx, &pb.AcknowledgeBackfillRequest{
|
||||
BackfillId: backfillId,
|
||||
Assignment: &pb.Assignment{
|
||||
Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func runAssignments(be pb.BackendServiceClient, matchesToAssign <-chan *pb.Match) {
|
||||
ctx := context.Background()
|
||||
|
||||
for m := range matchesForAssignment {
|
||||
for m := range matchesToAssign {
|
||||
ids := []string{}
|
||||
for _, t := range m.Tickets {
|
||||
ids = append(ids, t.GetId())
|
||||
}
|
||||
|
||||
if activeScenario.BackendAssignsTickets {
|
||||
_, err := be.AssignTickets(context.Background(), &pb.AssignTicketsRequest{
|
||||
Assignments: []*pb.AssignmentGroup{
|
||||
{
|
||||
TicketIds: ids,
|
||||
Assignment: &pb.Assignment{
|
||||
Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
|
||||
},
|
||||
_, err := be.AssignTickets(context.Background(), &pb.AssignTicketsRequest{
|
||||
Assignments: []*pb.AssignmentGroup{
|
||||
{
|
||||
TicketIds: ids,
|
||||
Assignment: &pb.Assignment{
|
||||
Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchAssignsFailed)
|
||||
logger.WithError(err).Error("failed to assign tickets")
|
||||
continue
|
||||
}
|
||||
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchesAssigned)
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchAssignsFailed)
|
||||
logger.WithError(err).Error("failed to assign tickets")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
ticketsForDeletion <- id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runDeletions(fe pb.FrontendServiceClient, ticketsForDeletion <-chan string) {
|
||||
ctx := context.Background()
|
||||
|
||||
for id := range ticketsForDeletion {
|
||||
if activeScenario.BackendDeletesTickets {
|
||||
req := &pb.DeleteTicketRequest{
|
||||
TicketId: id,
|
||||
}
|
||||
|
||||
_, err := fe.DeleteTicket(context.Background(), req)
|
||||
|
||||
if err == nil {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketsDeleted)
|
||||
} else {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketDeletesFailed)
|
||||
logger.WithError(err).Error("failed to delete tickets")
|
||||
}
|
||||
}
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchesAssigned)
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/trace"
|
||||
"open-match.dev/open-match/examples/scale/scenarios"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
@ -37,16 +38,26 @@ var (
|
||||
})
|
||||
activeScenario = scenarios.ActiveScenario
|
||||
|
||||
mTicketsCreated = telemetry.Counter("scale_frontend_tickets_created", "tickets created")
|
||||
mTicketCreationsFailed = telemetry.Counter("scale_frontend_ticket_creations_failed", "tickets created")
|
||||
mRunnersWaiting = concurrentGauge(telemetry.Gauge("scale_frontend_runners_waiting", "runners waiting"))
|
||||
mRunnersCreating = concurrentGauge(telemetry.Gauge("scale_frontend_runners_creating", "runners creating"))
|
||||
mTicketsCreated = telemetry.Counter("scale_frontend_tickets_created", "tickets created")
|
||||
mTicketCreationsFailed = telemetry.Counter("scale_frontend_ticket_creations_failed", "tickets created")
|
||||
mRunnersWaiting = concurrentGauge(telemetry.Gauge("scale_frontend_runners_waiting", "runners waiting"))
|
||||
mRunnersCreating = concurrentGauge(telemetry.Gauge("scale_frontend_runners_creating", "runners creating"))
|
||||
mTicketsDeleted = telemetry.Counter("scale_frontend_tickets_deleted", "tickets deleted")
|
||||
mTicketDeletesFailed = telemetry.Counter("scale_frontend_ticket_deletes_failed", "ticket deletes failed")
|
||||
mBackfillsCreated = telemetry.Counter("scale_frontend_backfills_created", "backfills_created")
|
||||
mBackfillCreationsFailed = telemetry.Counter("scale_frontend_backfill_creations_failed", "backfill creations failed")
|
||||
mTicketsTimeToAssignment = telemetry.HistogramWithBounds("scale_frontend_tickets_time_to_assignment", "tickets time to assignment", stats.UnitMilliseconds, []float64{0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000})
|
||||
)
|
||||
|
||||
type ticketToWatch struct {
|
||||
id string
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
// Run triggers execution of the scale frontend component that creates
|
||||
// tickets at scale in Open Match.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View) error {
|
||||
go run(cfg)
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
go run(p.Config())
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -60,9 +71,12 @@ func run(cfg config.View) {
|
||||
}
|
||||
fe := pb.NewFrontendServiceClient(conn)
|
||||
|
||||
if activeScenario.FrontendCreatesBackfillsOnStart {
|
||||
createBackfills(fe, activeScenario.FrontendTotalBackfillsToCreate)
|
||||
}
|
||||
|
||||
ticketQPS := int(activeScenario.FrontendTicketCreatedQPS)
|
||||
ticketTotal := activeScenario.FrontendTotalTicketsToCreate
|
||||
|
||||
totalCreated := 0
|
||||
|
||||
for range time.Tick(time.Second) {
|
||||
@ -88,13 +102,27 @@ func runner(fe pb.FrontendServiceClient) {
|
||||
time.Sleep(time.Duration(rand.Int63n(int64(time.Second))))
|
||||
|
||||
g.start(mRunnersCreating)
|
||||
createdAt := time.Now()
|
||||
id, err := createTicket(ctx, fe)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to create a ticket")
|
||||
return
|
||||
}
|
||||
|
||||
_ = id
|
||||
err = watchAssignments(ctx, fe, ticketToWatch{id: id, createdAt: createdAt})
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to get ticket assignment: %s", id)
|
||||
} else {
|
||||
ms := time.Since(createdAt).Nanoseconds() / 1e6
|
||||
stats.Record(ctx, mTicketsTimeToAssignment.M(ms))
|
||||
}
|
||||
|
||||
if activeScenario.FrontendDeletesTickets {
|
||||
err = deleteTicket(ctx, fe, id)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to delete ticket: %s", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createTicket(ctx context.Context, fe pb.FrontendServiceClient) (string, error) {
|
||||
@ -115,6 +143,68 @@ func createTicket(ctx context.Context, fe pb.FrontendServiceClient) (string, err
|
||||
return resp.Id, nil
|
||||
}
|
||||
|
||||
func watchAssignments(ctx context.Context, fe pb.FrontendServiceClient, ticket ticketToWatch) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
stream, err := fe.WatchAssignments(ctx, &pb.WatchAssignmentsRequest{TicketId: ticket.id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var a *pb.Assignment
|
||||
for a.GetConnection() == "" {
|
||||
resp, err := stream.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a = resp.Assignment
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBackfills(fe pb.FrontendServiceClient, numBackfillsToCreate int) error {
|
||||
for i := 0; i < numBackfillsToCreate; i++ {
|
||||
err := createBackfill(fe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBackfill(fe pb.FrontendServiceClient) error {
|
||||
ctx, span := trace.StartSpan(context.Background(), "scale.frontend/CreateBackfill")
|
||||
defer span.End()
|
||||
|
||||
req := pb.CreateBackfillRequest{
|
||||
Backfill: activeScenario.Backfill(),
|
||||
}
|
||||
|
||||
_, err := fe.CreateBackfill(ctx, &req)
|
||||
if err != nil {
|
||||
telemetry.RecordUnitMeasurement(ctx, mBackfillCreationsFailed)
|
||||
logger.WithError(err).Error("failed to create backfill")
|
||||
return err
|
||||
}
|
||||
|
||||
telemetry.RecordUnitMeasurement(ctx, mBackfillsCreated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteTicket(ctx context.Context, fe pb.FrontendServiceClient, ticketId string) error {
|
||||
_, err := fe.DeleteTicket(ctx, &pb.DeleteTicketRequest{TicketId: ticketId})
|
||||
if err != nil {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketDeletesFailed)
|
||||
} else {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketsDeleted)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Allows concurrent moficiation of a gauge value by modifying the concurrent
|
||||
// value with a delta.
|
||||
func concurrentGauge(s *stats.Int64Measure) func(delta int64) {
|
||||
|
@ -39,7 +39,7 @@ var (
|
||||
func Run() {
|
||||
activeScenario := scenarios.ActiveScenario
|
||||
|
||||
conn, err := grpc.Dial("om-query.open-match.svc.cluster.local:50503", utilTesting.NewGRPCDialOptions(logger)...)
|
||||
conn, err := grpc.Dial("open-match-query.open-match.svc.cluster.local:50503", utilTesting.NewGRPCDialOptions(logger)...)
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to connect to Open Match, got %v", err)
|
||||
}
|
||||
|
271
examples/scale/scenarios/backfill/backfill.go
Normal file
271
examples/scale/scenarios/backfill/backfill.go
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2020 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 backfill
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
const (
|
||||
poolName = "all"
|
||||
openSlotsKey = "open-slots"
|
||||
)
|
||||
|
||||
func Scenario() *BackfillScenario {
|
||||
ticketsPerMatch := 4
|
||||
return &BackfillScenario{
|
||||
TicketsPerMatch: ticketsPerMatch,
|
||||
MaxTicketsPerNotFullMatch: 3,
|
||||
BackfillDeleteCond: func(b *pb.Backfill) bool {
|
||||
openSlots := getOpenSlots(b, ticketsPerMatch)
|
||||
return openSlots <= 0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type BackfillScenario struct {
|
||||
TicketsPerMatch int
|
||||
MaxTicketsPerNotFullMatch int
|
||||
BackfillDeleteCond func(*pb.Backfill) bool
|
||||
}
|
||||
|
||||
func (s *BackfillScenario) Profiles() []*pb.MatchProfile {
|
||||
return []*pb.MatchProfile{
|
||||
{
|
||||
Name: "entirePool",
|
||||
Pools: []*pb.Pool{
|
||||
{
|
||||
Name: poolName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BackfillScenario) Ticket() *pb.Ticket {
|
||||
return &pb.Ticket{}
|
||||
}
|
||||
|
||||
func (s *BackfillScenario) Backfill() *pb.Backfill {
|
||||
return &pb.Backfill{}
|
||||
}
|
||||
|
||||
func (s *BackfillScenario) MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
return statefullMMF(p, poolBackfills, poolTickets, s.TicketsPerMatch, s.MaxTicketsPerNotFullMatch)
|
||||
}
|
||||
|
||||
// statefullMMF is a MMF implementation which is used in scenario when we want MMF to create not full match and fill it later.
|
||||
// 1. The first FetchMatches is called
|
||||
// 2. MMF grabs maxTicketsPerNotFullMatch tickets and makes a match and new backfill for it
|
||||
// 3. MMF sets backfill's open slots to ticketsPerMatch - maxTicketsPerNotFullMatch
|
||||
// 4. MMF returns the match as a result
|
||||
// 5. The second FetchMatches is called
|
||||
// 6. MMF gets previously created backfill
|
||||
// 7. MMF gets backfill's open slots value
|
||||
// 8. MMF grabs openSlots tickets and makes a match with previously created backfill
|
||||
// 9. MMF sets backfill's open slots to 0
|
||||
// 10. MMF returns the match as a result
|
||||
func statefullMMF(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket, ticketsPerMatch int, maxTicketsPerNotFullMatch int) ([]*pb.Match, error) {
|
||||
var matches []*pb.Match
|
||||
|
||||
for pool, backfills := range poolBackfills {
|
||||
tickets, ok := poolTickets[pool]
|
||||
|
||||
if !ok || len(tickets) == 0 {
|
||||
// no tickets in pool
|
||||
continue
|
||||
}
|
||||
|
||||
// process backfills first
|
||||
for _, b := range backfills {
|
||||
l := len(tickets)
|
||||
if l == 0 {
|
||||
// no tickets left
|
||||
break
|
||||
}
|
||||
|
||||
openSlots := getOpenSlots(b, ticketsPerMatch)
|
||||
if openSlots <= 0 {
|
||||
// no free open slots
|
||||
continue
|
||||
}
|
||||
|
||||
if l > openSlots {
|
||||
l = openSlots
|
||||
}
|
||||
|
||||
setOpenSlots(b, openSlots-l)
|
||||
matches = append(matches, &pb.Match{
|
||||
MatchId: fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), len(matches)),
|
||||
Tickets: tickets[0:l],
|
||||
MatchProfile: p.GetName(),
|
||||
MatchFunction: "backfill",
|
||||
Backfill: b,
|
||||
})
|
||||
tickets = tickets[l:]
|
||||
}
|
||||
|
||||
// create not full matches with backfill
|
||||
for {
|
||||
l := len(tickets)
|
||||
if l == 0 {
|
||||
// no tickets left
|
||||
break
|
||||
}
|
||||
|
||||
if l > maxTicketsPerNotFullMatch {
|
||||
l = maxTicketsPerNotFullMatch
|
||||
}
|
||||
b := pb.Backfill{}
|
||||
setOpenSlots(&b, ticketsPerMatch-l)
|
||||
matches = append(matches, &pb.Match{
|
||||
MatchId: fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), len(matches)),
|
||||
Tickets: tickets[0:l],
|
||||
MatchProfile: p.GetName(),
|
||||
MatchFunction: "backfill",
|
||||
Backfill: &b,
|
||||
AllocateGameserver: true,
|
||||
})
|
||||
tickets = tickets[l:]
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
func getOpenSlots(b *pb.Backfill, defaultVal int) int {
|
||||
if b.Extensions == nil {
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
any, ok := b.Extensions[openSlotsKey]
|
||||
if !ok {
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
var val wrappers.Int32Value
|
||||
err := ptypes.UnmarshalAny(any, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(val.Value)
|
||||
}
|
||||
|
||||
func setOpenSlots(b *pb.Backfill, val int) {
|
||||
if b.Extensions == nil {
|
||||
b.Extensions = make(map[string]*any.Any)
|
||||
}
|
||||
|
||||
any, err := ptypes.MarshalAny(&wrappers.Int32Value{Value: int32(val)})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b.Extensions[openSlotsKey] = any
|
||||
}
|
||||
|
||||
// statelessMMF is a MMF implementation which is used in scenario when we want MMF to fill backfills created by a Gameserver. It doesn't create
|
||||
// or update any backfill.
|
||||
// 1. FetchMatches is called
|
||||
// 2. MMF gets a backfill
|
||||
// 3. MMF grabs ticketsPerMatch tickets and makes a match with the backfill
|
||||
// 4. MMF returns the match as a result
|
||||
func statelessMMF(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket, ticketsPerMatch int) ([]*pb.Match, error) {
|
||||
var matches []*pb.Match
|
||||
|
||||
for pool, backfills := range poolBackfills {
|
||||
tickets, ok := poolTickets[pool]
|
||||
|
||||
if !ok || len(tickets) == 0 {
|
||||
// no tickets in pool
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range backfills {
|
||||
l := len(tickets)
|
||||
if l == 0 {
|
||||
// no tickets left
|
||||
break
|
||||
}
|
||||
|
||||
if l > ticketsPerMatch && ticketsPerMatch > 0 {
|
||||
l = ticketsPerMatch
|
||||
}
|
||||
|
||||
matches = append(matches, &pb.Match{
|
||||
MatchId: fmt.Sprintf("profile-%v-time-%v-%v", p.GetName(), time.Now().Format("2006-01-02T15:04:05.00"), len(matches)),
|
||||
Tickets: tickets[0:l],
|
||||
MatchProfile: p.GetName(),
|
||||
MatchFunction: "backfill",
|
||||
Backfill: b,
|
||||
})
|
||||
tickets = tickets[l:]
|
||||
}
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
func (s *BackfillScenario) Evaluate(stream pb.Evaluator_EvaluateServer) error {
|
||||
tickets := map[string]struct{}{}
|
||||
backfills := map[string]struct{}{}
|
||||
matchIds := []string{}
|
||||
|
||||
outer:
|
||||
for {
|
||||
req, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read evaluator input stream: %w", err)
|
||||
}
|
||||
|
||||
m := req.GetMatch()
|
||||
|
||||
if _, ok := backfills[m.Backfill.Id]; ok {
|
||||
continue outer
|
||||
}
|
||||
|
||||
for _, t := range m.Tickets {
|
||||
if _, ok := tickets[t.Id]; ok {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range m.Tickets {
|
||||
tickets[t.Id] = struct{}{}
|
||||
}
|
||||
|
||||
matchIds = append(matchIds, m.GetMatchId())
|
||||
}
|
||||
|
||||
for _, id := range matchIds {
|
||||
err := stream.Send(&pb.EvaluateResponse{MatchId: id})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sending evaluator output stream: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -78,7 +78,11 @@ func (b *BattleRoyalScenario) Ticket() *pb.Ticket {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BattleRoyalScenario) MatchFunction(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
func (b *BattleRoyalScenario) Backfill() *pb.Backfill {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BattleRoyalScenario) MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
const playersInMatch = 100
|
||||
|
||||
tickets := poolTickets[poolName]
|
||||
@ -101,7 +105,7 @@ func (b *BattleRoyalScenario) MatchFunction(p *pb.MatchProfile, poolTickets map[
|
||||
func (b *BattleRoyalScenario) Evaluate(stream pb.Evaluator_EvaluateServer) error {
|
||||
used := map[string]struct{}{}
|
||||
|
||||
// TODO: once the evaluator client supports sending and recieving at the
|
||||
// TODO: once the evaluator client supports sending and receiving at the
|
||||
// same time, don't buffer, just send results immediately.
|
||||
matchIDs := []string{}
|
||||
|
||||
|
@ -33,7 +33,7 @@ func Scenario() *FirstMatchScenario {
|
||||
type FirstMatchScenario struct {
|
||||
}
|
||||
|
||||
func (_ *FirstMatchScenario) Profiles() []*pb.MatchProfile {
|
||||
func (*FirstMatchScenario) Profiles() []*pb.MatchProfile {
|
||||
return []*pb.MatchProfile{
|
||||
{
|
||||
Name: "entirePool",
|
||||
@ -46,11 +46,15 @@ func (_ *FirstMatchScenario) Profiles() []*pb.MatchProfile {
|
||||
}
|
||||
}
|
||||
|
||||
func (_ *FirstMatchScenario) Ticket() *pb.Ticket {
|
||||
func (*FirstMatchScenario) Ticket() *pb.Ticket {
|
||||
return &pb.Ticket{}
|
||||
}
|
||||
|
||||
func (_ *FirstMatchScenario) MatchFunction(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
func (*FirstMatchScenario) Backfill() *pb.Backfill {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*FirstMatchScenario) MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
tickets := poolTickets[poolName]
|
||||
var matches []*pb.Match
|
||||
|
||||
@ -68,10 +72,10 @@ func (_ *FirstMatchScenario) MatchFunction(p *pb.MatchProfile, poolTickets map[s
|
||||
|
||||
// fifoEvaluate accepts all matches which don't contain the same ticket as in a
|
||||
// previously accepted match. Essentially first to claim the ticket wins.
|
||||
func (_ *FirstMatchScenario) Evaluate(stream pb.Evaluator_EvaluateServer) error {
|
||||
func (*FirstMatchScenario) Evaluate(stream pb.Evaluator_EvaluateServer) error {
|
||||
used := map[string]struct{}{}
|
||||
|
||||
// TODO: once the evaluator client supports sending and recieving at the
|
||||
// TODO: once the evaluator client supports sending and receiving at the
|
||||
// same time, don't buffer, just send results immediately.
|
||||
matchIDs := []string{}
|
||||
|
||||
|
@ -19,16 +19,15 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/examples/scale/scenarios/battleroyal"
|
||||
"open-match.dev/open-match/examples/scale/scenarios/backfill"
|
||||
"open-match.dev/open-match/examples/scale/scenarios/firstmatch"
|
||||
"open-match.dev/open-match/examples/scale/scenarios/teamshooter"
|
||||
"open-match.dev/open-match/internal/util/testing"
|
||||
"open-match.dev/open-match/pkg/matchfunction"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
var (
|
||||
queryServiceAddress = "om-query.open-match.svc.cluster.local:50503" // Address of the QueryService Endpoint.
|
||||
queryServiceAddress = "open-match-query.open-match.svc.cluster.local:50503" // Address of the QueryService Endpoint.
|
||||
|
||||
logger = logrus.WithFields(logrus.Fields{
|
||||
"app": "scale",
|
||||
@ -40,11 +39,14 @@ type GameScenario interface {
|
||||
// Ticket creates a new ticket, with randomized parameters.
|
||||
Ticket() *pb.Ticket
|
||||
|
||||
// Backfill creates a new backfill, with randomized parameters.
|
||||
Backfill() *pb.Backfill
|
||||
|
||||
// Profiles lists all of the profiles that should run.
|
||||
Profiles() []*pb.MatchProfile
|
||||
|
||||
// MatchFunction is the custom logic implementation of the match function.
|
||||
MatchFunction(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error)
|
||||
MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error)
|
||||
|
||||
// Evaluate is the custom logic implementation of the evaluator.
|
||||
Evaluate(stream pb.Evaluator_EvaluateServer) error
|
||||
@ -56,18 +58,26 @@ var ActiveScenario = func() *Scenario {
|
||||
|
||||
// TODO: Select which scenario to use based on some configuration or choice,
|
||||
// so it's easier to run different scenarios without changing code.
|
||||
gs = battleroyal.Scenario()
|
||||
gs = teamshooter.Scenario()
|
||||
//gs = battleroyal.Scenario()
|
||||
//gs = teamshooter.Scenario()
|
||||
s := backfill.Scenario()
|
||||
gs = s
|
||||
|
||||
return &Scenario{
|
||||
FrontendTotalTicketsToCreate: -1,
|
||||
FrontendTicketCreatedQPS: 100,
|
||||
FrontendTotalTicketsToCreate: -1,
|
||||
FrontendTicketCreatedQPS: 100,
|
||||
FrontendCreatesBackfillsOnStart: true,
|
||||
FrontendTotalBackfillsToCreate: 1000,
|
||||
FrontendDeletesTickets: true,
|
||||
|
||||
BackendAssignsTickets: true,
|
||||
BackendDeletesTickets: true,
|
||||
BackendAssignsTickets: false,
|
||||
BackendAcknowledgesBackfills: true,
|
||||
BackendDeletesBackfills: true,
|
||||
|
||||
Ticket: gs.Ticket,
|
||||
Profiles: gs.Profiles,
|
||||
Ticket: gs.Ticket,
|
||||
Backfill: gs.Backfill,
|
||||
BackfillDeleteCond: s.BackfillDeleteCond,
|
||||
Profiles: gs.Profiles,
|
||||
|
||||
MMF: queryPoolsWrapper(gs.MatchFunction),
|
||||
Evaluator: gs.Evaluate,
|
||||
@ -87,17 +97,23 @@ type Scenario struct {
|
||||
// TicketExtensionSize int
|
||||
// PendingTicketNumber int
|
||||
// MatchExtensionSize int
|
||||
FrontendTotalTicketsToCreate int // TotalTicketsToCreate = -1 let scale-frontend create tickets forever
|
||||
FrontendTicketCreatedQPS uint32
|
||||
FrontendTicketCreatedQPS uint32
|
||||
FrontendTotalTicketsToCreate int // TotalTicketsToCreate = -1 let scale-frontend create tickets forever
|
||||
FrontendTotalBackfillsToCreate int
|
||||
FrontendCreatesBackfillsOnStart bool
|
||||
FrontendDeletesTickets bool
|
||||
|
||||
// GameBackend Configs
|
||||
// ProfileNumber int
|
||||
// FilterNumber int
|
||||
BackendAssignsTickets bool
|
||||
BackendDeletesTickets bool
|
||||
BackendAssignsTickets bool
|
||||
BackendAcknowledgesBackfills bool
|
||||
BackendDeletesBackfills bool
|
||||
|
||||
Ticket func() *pb.Ticket
|
||||
Profiles func() []*pb.MatchProfile
|
||||
Ticket func() *pb.Ticket
|
||||
Backfill func() *pb.Backfill
|
||||
BackfillDeleteCond func(*pb.Backfill) bool
|
||||
Profiles func() []*pb.MatchProfile
|
||||
|
||||
MMF matchFunction
|
||||
Evaluator evaluatorFunction
|
||||
@ -122,7 +138,7 @@ func getQueryServiceGRPCClient() pb.QueryServiceClient {
|
||||
return pb.NewQueryServiceClient(conn)
|
||||
}
|
||||
|
||||
func queryPoolsWrapper(mmf func(req *pb.MatchProfile, pools map[string][]*pb.Ticket) ([]*pb.Match, error)) matchFunction {
|
||||
func queryPoolsWrapper(mmf func(req *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error)) matchFunction {
|
||||
var q pb.QueryServiceClient
|
||||
var startQ sync.Once
|
||||
|
||||
@ -136,7 +152,12 @@ func queryPoolsWrapper(mmf func(req *pb.MatchProfile, pools map[string][]*pb.Tic
|
||||
return err
|
||||
}
|
||||
|
||||
proposals, err := mmf(req.GetProfile(), poolTickets)
|
||||
poolBackfills, err := matchfunction.QueryBackfillPools(stream.Context(), q, req.GetProfile().GetPools())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
proposals, err := mmf(req.GetProfile(), poolBackfills, poolTickets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -154,9 +154,13 @@ func (t *TeamShooterScenario) Ticket() *pb.Ticket {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TeamShooterScenario) Backfill() *pb.Backfill {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MatchFunction puts tickets into matches based on their skill, finding the
|
||||
// required number of tickets for a game within the maximum skill difference.
|
||||
func (t *TeamShooterScenario) MatchFunction(p *pb.MatchProfile, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
func (t *TeamShooterScenario) MatchFunction(p *pb.MatchProfile, poolBackfills map[string][]*pb.Backfill, poolTickets map[string][]*pb.Ticket) ([]*pb.Match, error) {
|
||||
skill := func(t *pb.Ticket) float64 {
|
||||
return t.SearchFields.DoubleArgs[skillArg]
|
||||
}
|
||||
|
78
go.mod
78
go.mod
@ -18,55 +18,49 @@ module open-match.dev/open-match
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.47.0 // indirect
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.1.0
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.1.0
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.12.8
|
||||
github.com/Bose/minisentinel v0.0.0-20191213132324-b7726ed8ed71
|
||||
contrib.go.opencensus.io/exporter/jaeger v0.2.1
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.0
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.2.0
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.4
|
||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
|
||||
github.com/TV4/logrus-stackdriver-formatter v0.1.0
|
||||
github.com/alicebob/miniredis/v2 v2.11.0
|
||||
github.com/apache/thrift v0.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.25.27 // indirect
|
||||
github.com/alicebob/miniredis/v2 v2.14.1
|
||||
github.com/aws/aws-sdk-go v1.35.26 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-redsync/redsync/v4 v4.3.0
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/gomodule/redigo v2.0.1-0.20191111085604-09d84710e01a+incompatible
|
||||
github.com/googleapis/gnostic v0.3.1 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.12.0
|
||||
github.com/imdario/mergo v0.3.8 // indirect
|
||||
github.com/json-iterator/go v1.1.8 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml v1.6.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v1.2.1
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.8.0
|
||||
github.com/pseudomuto/protoc-gen-doc v1.5.0 // indirect
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/afero v1.2.1 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/afero v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.5.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
go.opencensus.io v0.22.1
|
||||
golang.org/x/crypto v0.0.0-20191105034135-c7e5f84aec59 // indirect
|
||||
golang.org/x/net v0.0.0-20191105084925-a882066a44e0
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
google.golang.org/api v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6
|
||||
google.golang.org/grpc v1.25.0
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.opencensus.io v0.23.0
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect
|
||||
google.golang.org/api v0.35.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6
|
||||
google.golang.org/grpc v1.36.0
|
||||
google.golang.org/protobuf v1.25.1-0.20201208041424-160c7477e0e8
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.5 // indirect
|
||||
k8s.io/api v0.0.0-20191004102255-dacd7df5a50b // kubernetes-1.13.12
|
||||
k8s.io/apimachinery v0.0.0-20191004074956-01f8b7d1121a // kubernetes-1.13.12
|
||||
k8s.io/client-go v0.0.0-20191004102537-eb5b9a8cfde7 // kubernetes-1.13.12
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/api v0.0.0-20191004102349-159aefb8556b // kubernetes-1.14.10
|
||||
k8s.io/apimachinery v0.0.0-20191004074956-c5d2f014d689 // kubernetes-1.14.10
|
||||
k8s.io/client-go v11.0.1-0.20191029005444-8e4128053008+incompatible // kubernetes-1.14.10
|
||||
k8s.io/klog v1.0.0 // indirect
|
||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||
k8s.io/utils v0.0.0-20200729134348-d5654de09c73 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
@ -13,13 +13,13 @@
|
||||
# limitations under the License.
|
||||
|
||||
apiVersion: v2
|
||||
appVersion: "0.0.0-dev"
|
||||
version: 0.0.0-dev
|
||||
appVersion: "1.3.0"
|
||||
version: 1.3.0
|
||||
name: open-match
|
||||
dependencies:
|
||||
- name: redis
|
||||
version: 9.5.0
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
version: 12.3.3
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
condition: open-match-core.redis.enabled
|
||||
- name: open-match-telemetry
|
||||
version: 0.0.0-dev
|
||||
|
@ -0,0 +1,20 @@
|
||||
{*
|
||||
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.
|
||||
*}
|
||||
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{- define "openmatchcustomize.function.hostName" -}}
|
||||
{{- .Values.function.hostName | default (printf "%s-function" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
@ -1,41 +0,0 @@
|
||||
# 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.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: customize-configmap
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
component: config
|
||||
release: {{ .Release.Name }}
|
||||
data:
|
||||
matchmaker_config_default.yaml: |-
|
||||
api:
|
||||
functions:
|
||||
hostname: "{{ .Values.function.hostName }}"
|
||||
grpcport: "{{ .Values.function.grpcPort }}"
|
||||
httpport: "{{ .Values.function.httpPort }}"
|
||||
|
||||
evaluator:
|
||||
hostname: "{{ .Values.evaluator.hostName }}"
|
||||
grpcport: "{{ .Values.evaluator.grpcPort }}"
|
||||
httpport: "{{ .Values.evaluator.httpPort }}"
|
||||
matchmaker_config_override.yaml: |-
|
||||
api:
|
||||
query:
|
||||
hostname: "{{ .Values.query.hostName }}.{{ .Release.Namespace }}.svc.cluster.local"
|
||||
grpcport: "{{ .Values.query.grpcPort }}"
|
@ -18,7 +18,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.evaluator.hostName }}
|
||||
name: {{ include "openmatch.evaluator.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -46,20 +46,20 @@ spec:
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Values.evaluator.hostName }}
|
||||
name: {{ include "openmatch.evaluator.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Values.evaluator.hostName }}
|
||||
name: {{ include "openmatch.evaluator.hostName" . }}
|
||||
{{- include "openmatch.HorizontalPodAutoscaler.spec.common" . | nindent 2 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.evaluator.hostName }}
|
||||
name: {{ include "openmatch.evaluator.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
@ -82,12 +82,13 @@ spec:
|
||||
component: evaluator
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.evaluatorConfigs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.evaluatorConfigs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.evaluator.hostName }}
|
||||
- name: {{ include "openmatch.evaluator.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.evaluatorConfigs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -18,7 +18,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.function.hostName }}
|
||||
name: {{ include "openmatchcustomize.function.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -46,20 +46,20 @@ spec:
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Values.function.hostName }}
|
||||
name: {{ include "openmatchcustomize.function.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Values.function.hostName }}
|
||||
name: {{ include "openmatchcustomize.function.hostName" . }}
|
||||
{{- include "openmatch.HorizontalPodAutoscaler.spec.common" . | nindent 2 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.function.hostName }}
|
||||
name: {{ include "openmatchcustomize.function.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -83,12 +83,13 @@ spec:
|
||||
component: matchfunction
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.mmfConfigs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.mmfConfigs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.function.hostName }}
|
||||
- name: {{ include "openmatchcustomize.function.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.mmfConfigs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -35,11 +35,13 @@ evaluatorConfigs:
|
||||
default:
|
||||
volumeName: om-config-volume-default
|
||||
mountPath: /app/config/default
|
||||
configName: customize-configmap
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.default" . }}'
|
||||
customize:
|
||||
volumeName: customize-config-volume
|
||||
volumeName: om-config-volume-override
|
||||
mountPath: /app/config/override
|
||||
configName: customize-configmap
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.override" . }}'
|
||||
|
||||
mmfConfigs:
|
||||
# We use harness to implement the MMFs. MMF itself only requires one configmap but harness expects two,
|
||||
@ -48,8 +50,10 @@ mmfConfigs:
|
||||
default:
|
||||
volumeName: om-config-volume-default
|
||||
mountPath: /app/config/default
|
||||
configName: customize-configmap
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.default" . }}'
|
||||
customize:
|
||||
volumeName: customize-config-volume
|
||||
volumeName: om-config-volume-override
|
||||
mountPath: /app/config/override
|
||||
configName: customize-configmap
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.override" . }}'
|
||||
|
@ -18,13 +18,15 @@
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 16,
|
||||
"id": 28,
|
||||
"panels": [],
|
||||
"title": "Iterations",
|
||||
"type": "row"
|
||||
},
|
||||
@ -130,11 +132,317 @@
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"id": 16,
|
||||
"panels": [],
|
||||
"title": "Backfills",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
"id": 30,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_backfills_deleted[5m]))",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backfilld Deleted per second",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_backfill_deletes_failed[5m]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backfill Deletions Failed per second",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Backfill Deletion",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": "0",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
"id": 14,
|
||||
"panels": [],
|
||||
"title": "Tickets",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"fill": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
},
|
||||
"id": 26,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": true,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(scale_frontend_tickets_time_to_assignment_bucket[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "99%-ile",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.95, sum(rate(scale_frontend_tickets_time_to_assignment_bucket[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "95%-ile",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.90, sum(rate(scale_frontend_tickets_time_to_assignment_bucket[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "90%-ile",
|
||||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.50, sum(rate(scale_frontend_tickets_time_to_assignment_bucket[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "50%-ile",
|
||||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.10, sum(rate(scale_frontend_tickets_time_to_assignment_bucket[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "10%-ile",
|
||||
"refId": "E"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Ticket Time to Assignment",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "ms",
|
||||
"label": null,
|
||||
"logBase": 2,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 19
|
||||
},
|
||||
"id": 12,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_sum_tickets_returned[5m]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backend Tickets in Matches pers second",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Tickets In Matches",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": "0",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
@ -146,7 +454,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 10
|
||||
"y": 27
|
||||
},
|
||||
"id": 2,
|
||||
"legend": {
|
||||
@ -242,12 +550,12 @@
|
||||
"dashes": false,
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 10
|
||||
"y": 28
|
||||
},
|
||||
"id": 12,
|
||||
"id": 22,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@ -272,18 +580,26 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_sum_tickets_returned[5m]))",
|
||||
"expr": "sum(rate(scale_frontend_tickets_deleted[5m]))",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backend Tickets Deleted per second",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(scale_frontend_ticket_deletes_failed[5m]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backend Tickets in Matches pers second",
|
||||
"refId": "A"
|
||||
"legendFormat": "Backend Ticket Deletions Failed per second",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Tickets In Matches",
|
||||
"title": "Ticket Deletion",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
@ -331,7 +647,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
"y": 36
|
||||
},
|
||||
"id": 24,
|
||||
"legend": {
|
||||
@ -414,106 +730,13 @@
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 19
|
||||
},
|
||||
"id": 22,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_tickets_deleted[5m]))",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backend Tickets Deleted per second",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(scale_backend_ticket_deletes_failed[5m]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Backend Ticket Deletions Failed per second",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Ticket Deletion",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": "0",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 27
|
||||
"y": 44
|
||||
},
|
||||
"id": 18,
|
||||
"panels": [],
|
||||
@ -530,7 +753,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 28
|
||||
"y": 45
|
||||
},
|
||||
"id": 6,
|
||||
"legend": {
|
||||
@ -616,7 +839,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 28
|
||||
"y": 45
|
||||
},
|
||||
"id": 19,
|
||||
"legend": {
|
||||
@ -705,7 +928,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 36
|
||||
"y": 53
|
||||
},
|
||||
"id": 21,
|
||||
"panels": [],
|
||||
@ -722,7 +945,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 37
|
||||
"y": 54
|
||||
},
|
||||
"id": 8,
|
||||
"legend": {
|
||||
@ -807,7 +1030,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 37
|
||||
"y": 54
|
||||
},
|
||||
"id": 10,
|
||||
"legend": {
|
||||
@ -890,7 +1113,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 18,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
|
@ -0,0 +1,42 @@
|
||||
{*
|
||||
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.
|
||||
*}
|
||||
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "openmatchscale.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "openmatchscale.scaleBackend.hostName" -}}
|
||||
{{- .Values.scaleBackend.hostName | default (printf "%s-backend" (include "openmatchscale.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatchscale.scaleFrontend.hostName" -}}
|
||||
{{- .Values.scaleFrontend.hostName | default (printf "%s-frontend" (include "openmatchscale.fullname" . ) ) -}}
|
||||
{{- end -}}
|
@ -15,7 +15,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.scaleBackend.hostName }}
|
||||
name: {{ include "openmatchscale.scaleBackend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -34,7 +34,7 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.scaleBackend.hostName }}
|
||||
name: {{ include "openmatchscale.scaleBackend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -59,11 +59,11 @@ spec:
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.scaleBackend.hostName }}
|
||||
- name: {{ include "openmatchscale.scaleBackend.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -15,7 +15,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.scaleFrontend.hostName }}
|
||||
name: {{ include "openmatchscale.scaleFrontend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -34,7 +34,7 @@ spec:
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.scaleFrontend.hostName }}
|
||||
name: {{ include "openmatchscale.scaleFrontend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -59,11 +59,11 @@ spec:
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.scaleFrontend.hostName }}
|
||||
- name: {{ include "openmatchscale.scaleFrontend.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -16,7 +16,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: open-match-scale-dashboard
|
||||
name: {{ include "openmatchscale.fullname" . }}-dashboard
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
grafana_dashboard: "1"
|
||||
|
@ -13,13 +13,13 @@
|
||||
# limitations under the License.
|
||||
|
||||
scaleFrontend:
|
||||
hostName: om-scale-frontend
|
||||
hostName:
|
||||
httpPort: 51509
|
||||
replicas: 1
|
||||
image: openmatch-scale-frontend
|
||||
|
||||
scaleBackend:
|
||||
hostName: om-scale-backend
|
||||
hostName:
|
||||
httpPort: 51509
|
||||
replicas: 1
|
||||
image: openmatch-scale-backend
|
||||
@ -28,8 +28,10 @@ configs:
|
||||
default:
|
||||
volumeName: om-config-volume-default
|
||||
mountPath: /app/config/default
|
||||
configName: om-configmap-default
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.default" . }}'
|
||||
override:
|
||||
volumeName: om-config-volume-override
|
||||
mountPath: /app/config/override
|
||||
configName: om-configmap-override
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.override" . }}'
|
||||
|
@ -20,14 +20,14 @@ version: 0.0.0-dev
|
||||
dependencies:
|
||||
- name: prometheus
|
||||
version: 9.2.0
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
repository: https://charts.helm.sh/stable
|
||||
condition: global.telemetry.prometheus.enabled,prometheus.enabled
|
||||
- name: grafana
|
||||
version: 4.0.1
|
||||
repository: https://kubernetes-charts.storage.googleapis.com/
|
||||
repository: https://charts.helm.sh/stable
|
||||
condition: global.telemetry.grafana.enabled,grafana.enabled
|
||||
- name: jaeger
|
||||
version: 0.13.3
|
||||
repository: https://kubernetes-charts-incubator.storage.googleapis.com/
|
||||
repository: https://charts.helm.sh/stable
|
||||
condition: global.telemetry.jaeger.enabled,jaeger.enabled
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (pod_name) (\n sum(\n rate(container_cpu_usage_seconds_total{pod_name=~\"om-.*\", container_name!=\"POD\"}[5m])\n ) by (pod_name, container_name) \n \n /\n \n sum(\n container_spec_cpu_quota{pod_name=~\"om-.*\", container_name!=\"POD\"} / container_spec_cpu_period{pod_name=~\"om-.*\", container_name!=\"POD\"}\n ) by (pod_name, container_name) \n \n * \n \n 100\n)",
|
||||
"expr": "avg by (pod_name) (\n\nsum(\n rate(container_cpu_usage_seconds_total{container_name!=\"POD\"}[5m]) * on (pod_name) group_left(label_app) max by (pod_name, label_app) (label_replace(kube_pod_labels{label_app=\"open-match\"}, \"pod_name\", \"$1\", \"pod\", \"(.*)\"))\n) by (pod_name, container_name)\n\n/\n\nsum(\n (container_spec_cpu_quota{container_name!=\"POD\"} * on (pod_name) group_left(label_app) max by (pod_name, label_app) (label_replace(kube_pod_labels{label_app=\"open-match\"}, \"pod_name\", \"$1\", \"pod\", \"(.*)\")))\n /\n (container_spec_cpu_period{container_name!=\"POD\"} * on (pod_name) group_left(label_app) max by (pod_name, label_app) (label_replace(kube_pod_labels{label_app=\"open-match\"}, \"pod_name\", \"$1\", \"pod\", \"(.*)\")))\n) by (pod_name, container_name)\n\n*\n\n100\n)\n",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{pod_name}}",
|
||||
@ -155,7 +155,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (go_goroutines{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component) (go_goroutines{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}}",
|
||||
@ -256,7 +256,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component,app) (process_resident_memory_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component,app) (process_resident_memory_bytes{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - resident",
|
||||
@ -265,7 +265,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component,app) (process_virtual_memory_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component,app) (process_virtual_memory_bytes{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - virtual",
|
||||
@ -365,7 +365,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (deriv(process_resident_memory_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(process_resident_memory_bytes{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - resident",
|
||||
@ -374,7 +374,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (deriv(process_virtual_memory_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(process_virtual_memory_bytes{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - virtual",
|
||||
@ -475,7 +475,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (go_memstats_alloc_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component) (go_memstats_alloc_bytes{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - bytes allocated",
|
||||
@ -484,7 +484,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (rate(go_memstats_alloc_bytes_total{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (rate(go_memstats_alloc_bytes_total{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - alloc rate",
|
||||
@ -493,7 +493,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (go_memstats_stack_inuse_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component) (go_memstats_stack_inuse_bytes{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - stack inuse",
|
||||
@ -502,7 +502,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (go_memstats_heap_inuse_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component) (go_memstats_heap_inuse_bytes{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 2,
|
||||
@ -604,7 +604,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (deriv(go_memstats_alloc_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(go_memstats_alloc_bytes{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - bytes allocated",
|
||||
@ -613,7 +613,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (deriv(go_memstats_stack_inuse_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(go_memstats_stack_inuse_bytes{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}} - stack inuse",
|
||||
@ -622,7 +622,7 @@
|
||||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "avg by (component) (deriv(go_memstats_heap_inuse_bytes{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(go_memstats_heap_inuse_bytes{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 2,
|
||||
@ -719,7 +719,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (process_open_fds{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component) (process_open_fds{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}}",
|
||||
@ -815,7 +815,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component) (deriv(process_open_fds{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"}[$interval]))",
|
||||
"expr": "avg by (component) (deriv(process_open_fds{app=~\"open-match\"}[$interval]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}}",
|
||||
@ -911,7 +911,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg by (component, quantile) (go_gc_duration_seconds{app=~\"open-match\", kubernetes_pod_name=~\"om-.*\"})",
|
||||
"expr": "avg by (component, quantile) (go_gc_duration_seconds{app=~\"open-match\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{component}}: {{quantile}}",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -348,14 +348,14 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(container_cpu_usage_seconds_total{pod_name=~\"om-redis.*\", name!~\".*prometheus.*\", image!=\"\", container_name!=\"POD\"}[5m])) by (pod_name)",
|
||||
"expr": "sum(rate(container_cpu_usage_seconds_total{name!~\".*prometheus.*\", image!=\"\", container_name!=\"POD\"}[5m]) * on (pod_name) group_left(label_app) max by (pod_name, label_app) (label_replace(kube_pod_labels{label_app=\"redis\"}, \"pod_name\", \"$1\", \"pod\", \"(.*)\"))) by (pod_name)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{pod_name}} usage",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(kube_pod_container_resource_limits_cpu_cores{pod=~\"om-redis.*\"}) by (pod)",
|
||||
"expr": "sum(kube_pod_container_resource_limits_cpu_cores * on (pod) group_left(label_app) max by (pod, label_app) (kube_pod_labels{label_app=\"redis\"})) by (pod)",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 1,
|
||||
@ -363,7 +363,7 @@
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "sum(kube_pod_container_resource_requests_cpu_cores{pod=~\"om-redis.*\"}) by (pod)",
|
||||
"expr": "sum(kube_pod_container_resource_requests_cpu_cores * on (pod) group_left(label_app) max by (pod, label_app) (kube_pod_labels{label_app=\"redis\"})) by (pod)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "request",
|
||||
|
@ -1,290 +0,0 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": 3,
|
||||
"iteration": 1562886170229,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(frontend_tickets_created[$timewindow]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Created",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(frontend_tickets_deleted[$timewindow]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Deleted",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Ticket Flow",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"description": "",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"interval": "",
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"options": {},
|
||||
"percentage": false,
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(frontend_tickets_assignments_retrieved[$timewindow]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Assignments Retrieved",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Assignments",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"decimals": null,
|
||||
"format": "reqps",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": "0",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"schemaVersion": 18,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {
|
||||
"text": "5m",
|
||||
"value": "5m"
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Time Window",
|
||||
"multi": false,
|
||||
"name": "timewindow",
|
||||
"options": [
|
||||
{
|
||||
"selected": true,
|
||||
"text": "5m",
|
||||
"value": "5m"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "10m",
|
||||
"value": "10m"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "15m",
|
||||
"value": "15m"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "30m",
|
||||
"value": "30m"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "1h",
|
||||
"value": "1h"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "4h",
|
||||
"value": "4h"
|
||||
}
|
||||
],
|
||||
"query": "5m,10m,15m,30m,1h,4h",
|
||||
"skipUrlSync": false,
|
||||
"type": "custom"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "Tickets",
|
||||
"uid": "TlgyFfIWz",
|
||||
"version": 6
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: open-match-dashboards
|
||||
name: {{ include "openmatch.fullname" . }}-dashboards
|
||||
labels:
|
||||
grafana_dashboard: "1"
|
||||
data:
|
||||
|
@ -0,0 +1,31 @@
|
||||
# Copyright 2020 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.
|
||||
|
||||
{{- if .Values.global.telemetry.grafana.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "openmatch.fullname" . }}-datasource
|
||||
labels:
|
||||
grafana_datasource: "1"
|
||||
data:
|
||||
datasource.yaml: |-
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
url: {{ tpl .Values.global.telemetry.grafana.prometheusServer . }}
|
||||
access: proxy
|
||||
isDefault: true
|
||||
{{- end }}
|
@ -142,17 +142,10 @@ grafana:
|
||||
notifiers: {}
|
||||
sidecar:
|
||||
dashboards:
|
||||
enabled: true
|
||||
enabled: true
|
||||
datasources:
|
||||
enabled: true
|
||||
plugins: grafana-piechart-panel
|
||||
datasources:
|
||||
datasources.yaml:
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
url: http://open-match-prometheus-server.{{ .Release.Namespace }}.svc.cluster.local:80/
|
||||
access: proxy
|
||||
isDefault: true
|
||||
|
||||
jaeger:
|
||||
enabled: true
|
||||
|
@ -22,6 +22,26 @@ Expand the name of the chart.
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
Instead of .Chart.Name, we hard-code "open-match" as we need to call this from subcharts, but get the
|
||||
same result as if called from this chart.
|
||||
*/}}
|
||||
{{- define "openmatch.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default "open-match" .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Render chart metadata labels: "chart", "heritage" unless "openmatch.noChartMeta" is set.
|
||||
*/}}
|
||||
@ -57,7 +77,7 @@ resources:
|
||||
{{- range $configIndex, $configValues := .configs }}
|
||||
- name: {{ $configValues.volumeName }}
|
||||
configMap:
|
||||
name: {{ $configValues.configName }}
|
||||
name: {{ tpl $configValues.configName $ }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
@ -74,10 +94,10 @@ resources:
|
||||
{{- if .Values.global.tls.enabled }}
|
||||
- name: tls-server-volume
|
||||
secret:
|
||||
secretName: om-tls-server
|
||||
secretName: {{ include "openmatch.fullname" . }}-tls-server
|
||||
- name: root-ca-volume
|
||||
secret:
|
||||
secretName: om-tls-rootca
|
||||
secretName: {{ include "openmatch.fullname" . }}-tls-rootca
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@ -92,7 +112,7 @@ resources:
|
||||
{{- if .Values.redis.usePassword }}
|
||||
- name: redis-password
|
||||
secret:
|
||||
secretName: {{ .Values.redis.fullnameOverride }}
|
||||
secretName: {{ include "call-nested" (list . "redis" "redis.fullname") }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@ -135,3 +155,72 @@ minReplicas: {{ .Values.global.kubernetes.horizontalPodAutoScaler.minReplicas }}
|
||||
maxReplicas: {{ .Values.global.kubernetes.horizontalPodAutoScaler.maxReplicas }}
|
||||
targetCPUUtilizationPercentage: {{ .Values.global.kubernetes.horizontalPodAutoScaler.targetCPUUtilizationPercentage }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.serviceAccount.name" -}}
|
||||
{{- .Values.global.kubernetes.serviceAccount | default (printf "%s-unprivileged-service" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.swaggerui.hostName" -}}
|
||||
{{- .Values.swaggerui.hostName | default (printf "%s-swaggerui" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.query.hostName" -}}
|
||||
{{- .Values.query.hostName | default (printf "%s-query" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.frontend.hostName" -}}
|
||||
{{- .Values.frontend.hostName | default (printf "%s-frontend" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.backend.hostName" -}}
|
||||
{{- .Values.backend.hostName | default (printf "%s-backend" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.synchronizer.hostName" -}}
|
||||
{{- .Values.synchronizer.hostName | default (printf "%s-synchronizer" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.evaluator.hostName" -}}
|
||||
{{- .Values.evaluator.hostName | default (printf "%s-evaluator" (include "openmatch.fullname" . ) ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.configmap.default" -}}
|
||||
{{- printf "%s-configmap-default" (include "openmatch.fullname" . ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.configmap.override" -}}
|
||||
{{- printf "%s-configmap-override" (include "openmatch.fullname" . ) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.jaeger.agent" -}}
|
||||
{{- if index .Values "open-match-telemetry" "enabled" -}}
|
||||
{{- if index .Values "open-match-telemetry" "jaeger" "enabled" -}}
|
||||
{{ include "call-nested" (list . "open-match-telemetry.jaeger" "jaeger.agent.name") }}:6831
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "openmatch.jaeger.collector" -}}
|
||||
{{- if index .Values "open-match-telemetry" "enabled" -}}
|
||||
{{- if index .Values "open-match-telemetry" "jaeger" "enabled" -}}
|
||||
http://{{ include "call-nested" (list . "open-match-telemetry.jaeger" "jaeger.collector.name") }}:14268/api/traces
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Call templates from sub-charts in a synthesized context, workaround for https://github.com/helm/helm/issues/3920
|
||||
Mainly useful for things like `{{ include "call-nested" (list . "redis" "redis.fullname") }}`
|
||||
https://github.com/helm/helm/issues/4535#issuecomment-416022809
|
||||
https://github.com/helm/helm/issues/4535#issuecomment-477778391
|
||||
*/}}
|
||||
{{- define "call-nested" }}
|
||||
{{- $dot := index . 0 }}
|
||||
{{- $subchart := index . 1 | splitList "." }}
|
||||
{{- $template := index . 2 }}
|
||||
{{- $values := $dot.Values }}
|
||||
{{- range $subchart }}
|
||||
{{- $values = index $values . }}
|
||||
{{- end }}
|
||||
{{- include $template (dict "Chart" (dict "Name" (last $subchart)) "Values" $values "Release" $dot.Release "Capabilities" $dot.Capabilities) }}
|
||||
{{- end }}
|
@ -16,7 +16,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.backend.hostName }}
|
||||
name: {{ include "openmatch.backend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -44,19 +44,19 @@ spec:
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Values.backend.hostName }}
|
||||
name: {{ include "openmatch.backend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Values.backend.hostName }}
|
||||
name: {{ include "openmatch.backend.hostName" . }}
|
||||
{{- include "openmatch.HorizontalPodAutoscaler.spec.common" . | nindent 2 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.backend.hostName }}
|
||||
name: {{ include "openmatch.backend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -82,12 +82,12 @@ spec:
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
{{- include "openmatch.volumes.withredis" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.backend.hostName }}
|
||||
- name: {{ include "openmatch.backend.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -16,7 +16,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.frontend.hostName }}
|
||||
name: {{ include "openmatch.frontend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -44,19 +44,19 @@ spec:
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Values.frontend.hostName }}
|
||||
name: {{ include "openmatch.frontend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Values.frontend.hostName }}
|
||||
name: {{ include "openmatch.frontend.hostName" . }}
|
||||
{{- include "openmatch.HorizontalPodAutoscaler.spec.common" . | nindent 2 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.frontend.hostName }}
|
||||
name: {{ include "openmatch.frontend.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -82,12 +82,12 @@ spec:
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
{{- include "openmatch.volumes.withredis" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.frontend.hostName }}
|
||||
- name: {{ include "openmatch.frontend.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -16,7 +16,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: om-configmap-default
|
||||
name: {{ include "openmatch.configmap.default" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -50,26 +50,33 @@ data:
|
||||
|
||||
api:
|
||||
backend:
|
||||
hostname: "{{ .Values.backend.hostName }}"
|
||||
hostname: "{{ include "openmatch.backend.hostName" . }}"
|
||||
grpcport: "{{ .Values.backend.grpcPort }}"
|
||||
httpport: "{{ .Values.backend.httpPort }}"
|
||||
frontend:
|
||||
hostname: "{{ .Values.frontend.hostName }}"
|
||||
hostname: "{{ include "openmatch.frontend.hostName" . }}"
|
||||
grpcport: "{{ .Values.frontend.grpcPort }}"
|
||||
httpport: "{{ .Values.frontend.httpPort }}"
|
||||
query:
|
||||
hostname: "{{ .Values.query.hostName }}"
|
||||
hostname: "{{ include "openmatch.query.hostName" . }}"
|
||||
grpcport: "{{ .Values.query.grpcPort }}"
|
||||
httpport: "{{ .Values.query.httpPort }}"
|
||||
synchronizer:
|
||||
hostname: "{{ .Values.synchronizer.hostName }}"
|
||||
hostname: "{{ include "openmatch.synchronizer.hostName" . }}"
|
||||
grpcport: "{{ .Values.synchronizer.grpcPort }}"
|
||||
httpport: "{{ .Values.synchronizer.httpPort }}"
|
||||
swaggerui:
|
||||
hostname: "{{ .Values.swaggerui.hostName }}"
|
||||
hostname: "{{ include "openmatch.swaggerui.hostName" . }}"
|
||||
httpport: "{{ .Values.swaggerui.httpPort }}"
|
||||
|
||||
# Configurations for api.test and api.scale are used for testing.
|
||||
test:
|
||||
hostname: "{{ include "openmatch.fullname" . }}-test"
|
||||
grpcport: "50509"
|
||||
httpport: "51509"
|
||||
scale:
|
||||
httpport: "51509"
|
||||
|
||||
{{- if .Values.global.tls.enabled }}
|
||||
tls:
|
||||
trustedCertificatePath: "{{.Values.global.tls.rootca.mountPath}}/public.cert"
|
||||
@ -78,21 +85,16 @@ data:
|
||||
rootcertificatefile: "{{.Values.global.tls.rootca.mountPath}}/public.cert"
|
||||
{{- end }}
|
||||
|
||||
storage:
|
||||
ignoreListTTL: {{ index .Values "open-match-core" "ignoreListTTL" }}
|
||||
page:
|
||||
size: 10000
|
||||
|
||||
redis:
|
||||
{{- if index .Values "open-match-core" "redis" "enabled" }}
|
||||
{{- if index .Values "redis" "sentinel" "enabled"}}
|
||||
sentinelPort: {{ .Values.redis.sentinel.port }}
|
||||
sentinelMaster: {{ .Values.redis.sentinel.masterSet }}
|
||||
sentinelHostname: {{ .Values.redis.fullnameOverride }}.{{ .Release.Namespace }}.svc.cluster.local
|
||||
sentinelHostname: {{ include "call-nested" (list . "redis" "redis.fullname") }}
|
||||
sentinelUsePassword: {{ .Values.redis.sentinel.usePassword }}
|
||||
{{- else}}
|
||||
# Open Match's default Redis setups
|
||||
hostname: {{ .Values.redis.fullnameOverride }}-master.{{ .Release.Namespace }}.svc.cluster.local
|
||||
hostname: {{ include "call-nested" (list . "redis" "redis.fullname") }}-master.{{ .Release.Namespace }}.svc.cluster.local
|
||||
port: {{ .Values.redis.redisPort }}
|
||||
user: {{ .Values.redis.user }}
|
||||
{{- end}}
|
||||
@ -111,13 +113,19 @@ data:
|
||||
healthCheckTimeout: {{ index .Values "open-match-core" "redis" "pool" "healthCheckTimeout" }}
|
||||
|
||||
telemetry:
|
||||
reportingPeriod: "{{ .Values.global.telemetry.reportingPeriod }}"
|
||||
traceSamplingFraction: "{{ .Values.global.telemetry.traceSamplingFraction }}"
|
||||
zpages:
|
||||
enable: "{{ .Values.global.telemetry.zpages.enabled }}"
|
||||
jaeger:
|
||||
enable: "{{ .Values.global.telemetry.jaeger.enabled }}"
|
||||
samplerFraction: {{ .Values.global.telemetry.jaeger.samplerFraction }}
|
||||
agentEndpoint: "{{ .Values.global.telemetry.jaeger.agentEndpoint }}"
|
||||
collectorEndpoint: "{{ .Values.global.telemetry.jaeger.collectorEndpoint }}"
|
||||
{{- if .Values.global.telemetry.jaeger.enabled }}
|
||||
agentEndpoint: "{{ tpl .Values.global.telemetry.jaeger.agentEndpoint . }}"
|
||||
collectorEndpoint: "{{ tpl .Values.global.telemetry.jaeger.collectorEndpoint . }}"
|
||||
{{- else }}
|
||||
agentEndpoint: ""
|
||||
collectorEndpoint: ""
|
||||
{{- end }}
|
||||
prometheus:
|
||||
enable: "{{ .Values.global.telemetry.prometheus.enabled }}"
|
||||
endpoint: "{{ .Values.global.telemetry.prometheus.endpoint }}"
|
||||
|
@ -16,7 +16,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: om-configmap-override
|
||||
name: {{ include "openmatch.configmap.override" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -25,12 +25,25 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
data:
|
||||
matchmaker_config_override.yaml: |-
|
||||
# Length of time between first fetch matches call, and when no further fetch
|
||||
# matches calls will join the current evaluation/synchronization cycle,
|
||||
# instead waiting for the next cycle.
|
||||
registrationInterval: {{ index .Values "open-match-core" "registrationInterval" }}
|
||||
# Length of time after match function as started before it will be canceled,
|
||||
# and evaluator call input is EOF.
|
||||
proposalCollectionInterval: {{ index .Values "open-match-core" "proposalCollectionInterval" }}
|
||||
# Time after a ticket has been returned from fetch matches (marked as pending)
|
||||
# before it automatically becomes active again and will be returned by query
|
||||
# calls.
|
||||
pendingReleaseTimeout: {{ index .Values "open-match-core" "pendingReleaseTimeout" }}
|
||||
# Time after a ticket has been assigned before it is automatically delted.
|
||||
assignedDeleteTimeout: {{ index .Values "open-match-core" "assignedDeleteTimeout" }}
|
||||
# Maximum number of tickets to return on a single QueryTicketsResponse.
|
||||
queryPageSize: {{ index .Values "open-match-core" "queryPageSize" }}
|
||||
backfillLockTimeout: {{ index .Values "open-match-core" "backfillLockTimeout" }}
|
||||
api:
|
||||
evaluator:
|
||||
hostname: "{{ .Values.evaluator.hostName }}"
|
||||
hostname: "{{ include "openmatch.evaluator.hostName" . }}"
|
||||
grpcport: "{{ .Values.evaluator.grpcPort }}"
|
||||
httpport: "{{ .Values.evaluator.httpPort }}"
|
||||
synchronizer:
|
||||
registrationIntervalMs: 250ms
|
||||
proposalCollectionIntervalMs: 20000ms
|
||||
{{- end }}
|
||||
|
@ -14,11 +14,11 @@
|
||||
|
||||
{{- if index .Values "open-match-core" "enabled" }}
|
||||
{{- if empty .Values.ci }}
|
||||
# om-redis-podsecuritypolicy is the least restricted PSP used to create privileged pods to disable THP in host kernel.
|
||||
# This is the least restricted PSP used to create privileged pods to disable THP in host kernel.
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodSecurityPolicy
|
||||
metadata:
|
||||
name: om-redis-podsecuritypolicy
|
||||
name: {{ include "openmatch.fullname" . }}-redis-podsecuritypolicy
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
{{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
@ -51,11 +51,11 @@ spec:
|
||||
fsGroup:
|
||||
rule: 'RunAsAny'
|
||||
---
|
||||
# om-core-podsecuritypolicy does not allow creating privileged pods and restrict binded pods to use the specified port ranges.
|
||||
# This does not allow creating privileged pods and restrict binded pods to use the specified port ranges.
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodSecurityPolicy
|
||||
metadata:
|
||||
name: om-core-podsecuritypolicy
|
||||
name: {{ include "openmatch.fullname" . }}-core-podsecuritypolicy
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
|
@ -16,7 +16,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.query.hostName }}
|
||||
name: {{ include "openmatch.query.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -44,19 +44,19 @@ spec:
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Values.query.hostName }}
|
||||
name: {{ include "openmatch.query.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Values.query.hostName }}
|
||||
name: {{ include "openmatch.query.hostName" . }}
|
||||
{{- include "openmatch.HorizontalPodAutoscaler.spec.common" . | nindent 2 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.query.hostName }}
|
||||
name: {{ include "openmatch.query.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -82,12 +82,12 @@ spec:
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
{{- include "openmatch.volumes.withredis" . | nindent 8 }}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.query.hostName }}
|
||||
- name: {{ include "openmatch.query.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -29,7 +29,7 @@ metadata:
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
name: {{ include "openmatch.serviceAccount.name" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -40,28 +40,26 @@ automountServiceAccountToken: true
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-service-role
|
||||
name: {{ include "openmatch.fullname" . }}-service-role
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
# Define om-service-role to use om-core-podsecuritypolicy
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- podsecuritypolicies
|
||||
resourceNames:
|
||||
- om-core-podsecuritypolicy
|
||||
- {{ include "openmatch.fullname" . }}-core-podsecuritypolicy
|
||||
verbs:
|
||||
- use
|
||||
---
|
||||
# This applies om-service-role to the open-match unprivileged service account under the release namespace.
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-service-role-binding
|
||||
name: {{ include "openmatch.fullname" . }}-service-role-binding
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -73,34 +71,33 @@ subjects:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: om-service-role
|
||||
name: {{ include "openmatch.fullname" . }}-service-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
{{- if index .Values "open-match-core" "redis" "enabled" }}
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-redis-role
|
||||
name: {{ include "openmatch.fullname" . }}-redis-role
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
# Define om-redis-role to use om-redis-podsecuritypolicy
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- podsecuritypolicies
|
||||
resourceNames:
|
||||
- om-redis-podsecuritypolicy
|
||||
- {{ include "openmatch.fullname" . }}-redis-podsecuritypolicy
|
||||
verbs:
|
||||
- use
|
||||
---
|
||||
# This applies om-redis role to the om-redis privileged service account under the release namespace.
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-redis-role-binding
|
||||
name: {{ include "openmatch.fullname" . }}-redis-role-binding
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -108,10 +105,11 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Values.redis.serviceAccount.name }} # Redis service account
|
||||
name: {{ include "call-nested" (list . "redis" "redis.serviceAccountName") }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: om-redis-role
|
||||
name: {{ include "openmatch.fullname" . }}-redis-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
@ -16,7 +16,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.swaggerui.hostName }}
|
||||
name: {{ include "openmatch.swaggerui.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -36,7 +36,7 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.swaggerui.hostName }}
|
||||
name: {{ include "openmatch.swaggerui.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -61,11 +61,11 @@ spec:
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.swaggerui.hostName }}
|
||||
- name: {{ include "openmatch.swaggerui.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -16,7 +16,7 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ .Values.synchronizer.hostName }}
|
||||
name: {{ include "openmatch.synchronizer.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -40,7 +40,7 @@ spec:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.synchronizer.hostName }}
|
||||
name: {{ include "openmatch.synchronizer.hostName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -66,12 +66,12 @@ spec:
|
||||
spec:
|
||||
{{- include "openmatch.labels.nodegrouping" . | nindent 6 }}
|
||||
volumes:
|
||||
{{- include "openmatch.volumes.configs" (dict "configs" .Values.configs) | nindent 8}}
|
||||
{{- include "openmatch.volumes.configs" (. | merge (dict "configs" .Values.configs)) | nindent 8}}
|
||||
{{- include "openmatch.volumes.tls" . | nindent 8}}
|
||||
{{- include "openmatch.volumes.withredis" . | nindent 8 }}
|
||||
serviceAccountName: {{ .Values.global.kubernetes.serviceAccount }}
|
||||
serviceAccountName: {{ include "openmatch.serviceAccount.name" . }}
|
||||
containers:
|
||||
- name: {{ .Values.synchronizer.hostName }}
|
||||
- name: {{ include "openmatch.synchronizer.hostName" . }}
|
||||
volumeMounts:
|
||||
{{- include "openmatch.volumemounts.configs" (dict "configs" .Values.configs) | nindent 10 }}
|
||||
{{- include "openmatch.volumemounts.tls" . | nindent 10 }}
|
||||
|
@ -1,8 +1,23 @@
|
||||
# This applies om-test-role to the open-match-test-service account under the release namespace.
|
||||
# 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.
|
||||
|
||||
{{- if .Values.ci }}
|
||||
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-test-role-binding
|
||||
name: {{ include "openmatch.fullname" . }}-test-role-binding
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -10,9 +25,11 @@ metadata:
|
||||
release: {{ .Release.Name }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: open-match-test-service
|
||||
name: {{ include "openmatch.fullname" . }}-test-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: om-test-role
|
||||
name: {{ include "openmatch.fullname" . }}-test-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
{{- end }}
|
||||
|
@ -1,23 +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.
|
||||
|
||||
{{- if .Values.ci }}
|
||||
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: om-test-role
|
||||
name: {{ include "openmatch.fullname" . }}-test-role
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
rules:
|
||||
# Define om-test-role to use om-core-podsecuritypolicy
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- podsecuritypolicies
|
||||
resourceNames:
|
||||
- om-core-podsecuritypolicy
|
||||
- {{ include "openmatch.fullname" . }}-core-podsecuritypolicy
|
||||
verbs:
|
||||
- use
|
||||
# Grant om-test-role get & list permission for k8s endpoints and pods resources
|
||||
# Grant this role get & list permission for k8s endpoints and pods resources
|
||||
# Required for e2e in-cluster testing.
|
||||
- apiGroups:
|
||||
- ""
|
||||
@ -27,3 +42,5 @@ rules:
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
||||
{{- end }}
|
||||
|
@ -1,11 +1,29 @@
|
||||
# Create a service account for open-match-test services.
|
||||
# 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.
|
||||
|
||||
{{- if .Values.ci }}
|
||||
|
||||
# Create a service account for test services.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: open-match-test-service
|
||||
name: {{ include "openmatch.fullname" . }}-test-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
automountServiceAccountToken: true
|
||||
|
||||
{{- end }}
|
||||
|
@ -1,24 +1,83 @@
|
||||
# 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.
|
||||
|
||||
{{- if .Values.ci }}
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ include "openmatch.fullname" . }}-test
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
component: test
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
selector:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
component: test
|
||||
release: {{ .Release.Name }}
|
||||
ports:
|
||||
- name: grpc
|
||||
protocol: TCP
|
||||
port: 50509
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 51509
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: om-test
|
||||
name: {{ include "openmatch.fullname" . }}-test
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
{{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
"helm.sh/hook": test-success
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
component: om-test
|
||||
component: test
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
# Specifies the duration in seconds relative to the startTime that the job may be active before the system tries to terminate it.
|
||||
activeDeadlineSeconds: 900
|
||||
serviceAccountName: open-match-test-service
|
||||
serviceAccountName: {{ include "openmatch.fullname" . }}-test-service
|
||||
automountServiceAccountToken: true
|
||||
volumes:
|
||||
- configMap:
|
||||
defaultMode: 420
|
||||
name: {{ include "openmatch.configmap.default" . }}
|
||||
name: om-config-volume-default
|
||||
- configMap:
|
||||
defaultMode: 420
|
||||
name: {{ include "openmatch.configmap.override" . }}
|
||||
name: om-config-volume-override
|
||||
containers:
|
||||
- image: "{{ .Values.global.image.registry }}/openmatch-base-build:{{ .Values.global.image.tag }}"
|
||||
- name: {{ include "openmatch.fullname" . }}-test
|
||||
volumeMounts:
|
||||
- mountPath: /app/config/default
|
||||
name: om-config-volume-default
|
||||
- mountPath: /app/config/override
|
||||
name: om-config-volume-override
|
||||
image: "{{ .Values.global.image.registry }}/openmatch-base-build:{{ .Values.global.image.tag }}"
|
||||
ports:
|
||||
- name: grpc
|
||||
containerPort: 50509
|
||||
- name: http
|
||||
containerPort: 51509
|
||||
imagePullPolicy: Always
|
||||
name: om-test
|
||||
name: test
|
||||
resources:
|
||||
limits:
|
||||
memory: 800Mi
|
||||
@ -32,7 +91,7 @@ spec:
|
||||
command: ["go"]
|
||||
args:
|
||||
- "test"
|
||||
- "./test/e2e"
|
||||
- "./internal/testing/e2e"
|
||||
- "-v"
|
||||
- "-timeout"
|
||||
- "150s"
|
||||
@ -40,3 +99,5 @@ spec:
|
||||
- "-tags"
|
||||
- "e2ecluster"
|
||||
restartPolicy: Never
|
||||
|
||||
{{- end }}
|
||||
|
@ -17,7 +17,7 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: om-tls-rootca
|
||||
name: {{ include "openmatch.fullname" . }}-tls-rootca
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
@ -31,9 +31,9 @@ data:
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: om-tls-server
|
||||
name: {{ include "openmatch.fullname" . }}-tls-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 2 }}
|
||||
annotations: {{- include "openmatch.chartmeta" . | nindent 4 }}
|
||||
labels:
|
||||
app: {{ template "openmatch.name" . }}
|
||||
component: tls
|
||||
|
@ -23,7 +23,7 @@
|
||||
# Begins the configuration section for `query` component in Open Match.
|
||||
# query:
|
||||
#
|
||||
# # Specifies om-query as the in-cluster domain name for the `query` service.
|
||||
# # Override the default in-cluster domain name for the `query` service to om-query.
|
||||
# hostName: om-query
|
||||
#
|
||||
# # Specifies the port for receiving RESTful HTTP requests in the `query` service.
|
||||
@ -44,67 +44,68 @@
|
||||
# # Specifies the image name to be used in a Kubernetes pod for `query` compoenent.
|
||||
# image: openmatch-query
|
||||
swaggerui: &swaggerui
|
||||
hostName: om-swaggerui
|
||||
hostName:
|
||||
httpPort: 51500
|
||||
portType: ClusterIP
|
||||
replicas: 1
|
||||
image: openmatch-swaggerui
|
||||
query: &query
|
||||
hostName: om-query
|
||||
hostName:
|
||||
grpcPort: 50503
|
||||
httpPort: 51503
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-query
|
||||
frontend: &frontend
|
||||
hostName: om-frontend
|
||||
hostName:
|
||||
grpcPort: 50504
|
||||
httpPort: 51504
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-frontend
|
||||
backend: &backend
|
||||
hostName: om-backend
|
||||
hostName:
|
||||
grpcPort: 50505
|
||||
httpPort: 51505
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-backend
|
||||
synchronizer: &synchronizer
|
||||
hostName: om-synchronizer
|
||||
hostName:
|
||||
grpcPort: 50506
|
||||
httpPort: 51506
|
||||
portType: ClusterIP
|
||||
replicas: 1
|
||||
image: openmatch-synchronizer
|
||||
evaluator: &evaluator
|
||||
hostName: om-evaluator
|
||||
hostName:
|
||||
grpcPort: 50508
|
||||
httpPort: 51508
|
||||
replicas: 3
|
||||
function: &function
|
||||
hostName: om-function
|
||||
hostName:
|
||||
grpcPort: 50502
|
||||
httpPort: 51502
|
||||
replicas: 3
|
||||
|
||||
# Specifies the location and name of the Open Match application-level config volumes.
|
||||
# Used in template: `openmatch.volumemounts.configs` and `openmatch.volumes.configs` under `templates/_helpers.tpl` file.
|
||||
# Used in template: `openmatch.volumemounts.configs` and `openmatch.volumes.configs` under `templates/_helpers.tpl` file.
|
||||
configs:
|
||||
default:
|
||||
volumeName: om-config-volume-default
|
||||
mountPath: /app/config/default
|
||||
configName: om-configmap-default
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.default" . }}'
|
||||
override:
|
||||
volumeName: om-config-volume-override
|
||||
mountPath: /app/config/override
|
||||
configName: om-configmap-override
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.override" . }}'
|
||||
|
||||
# Override Redis settings
|
||||
# https://hub.helm.sh/charts/stable/redis
|
||||
# https://github.com/helm/charts/tree/master/stable/redis
|
||||
redis:
|
||||
fullnameOverride: om-redis
|
||||
redisPort: 6379
|
||||
usePassword: false
|
||||
usePasswordFile: false
|
||||
@ -133,7 +134,6 @@ redis:
|
||||
slaveCount: 3
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: open-match-redis-service
|
||||
slave:
|
||||
persistence:
|
||||
enabled: false
|
||||
@ -172,14 +172,32 @@ redis:
|
||||
# Controls if users need to install backend, frontend, query, om-configmap, and swaggerui.
|
||||
open-match-core:
|
||||
enabled: true
|
||||
ignoreListTTL: 60000ms
|
||||
|
||||
# Length of time between first fetch matches call, and when no further fetch
|
||||
# matches calls will join the current evaluation/synchronization cycle,
|
||||
# instead waiting for the next cycle.
|
||||
registrationInterval: 250ms
|
||||
# Length of time after match function as started before it will be canceled,
|
||||
# and evaluator call input is EOF.
|
||||
proposalCollectionInterval: 20s
|
||||
# Time after a ticket has been returned from fetch matches (marked as pending)
|
||||
# before it automatically becomes active again and will be returned by query
|
||||
# calls.
|
||||
pendingReleaseTimeout: 1m
|
||||
# Time after a ticket has been assigned before it is automatically delted.
|
||||
assignedDeleteTimeout: 10m
|
||||
# Maximum number of tickets to return on a single QueryTicketsResponse.
|
||||
queryPageSize: 10000
|
||||
# Duration for redis locks to expire.
|
||||
backfillLockTimeout: 1m
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
# If open-match-core.redis.enabled is set to false, have Open Match components talk to this redis address instead.
|
||||
# Otherwise the default is set to the om-redis instance.
|
||||
hostname: # Your redis server address
|
||||
port: 6379
|
||||
user:
|
||||
user:
|
||||
pool:
|
||||
maxIdle: 500
|
||||
maxActive: 500
|
||||
@ -192,8 +210,6 @@ open-match-core:
|
||||
open-match-scale:
|
||||
# Switch the value between true/false to turn on/off this subchart
|
||||
enabled: false
|
||||
frontend: *frontend
|
||||
backend: *backend
|
||||
|
||||
# Controls if users need to install the monitoring tools in Open Match.
|
||||
open-match-telemetry:
|
||||
@ -206,7 +222,6 @@ open-match-customize:
|
||||
enabled: false
|
||||
evaluator: *evaluator
|
||||
function: *function
|
||||
query: *query
|
||||
# You can override the evaluator/mmf image
|
||||
# evaluator:
|
||||
# image: [YOUR_EVALUATOR_IMAGE]
|
||||
@ -233,8 +248,8 @@ global:
|
||||
limits:
|
||||
memory: 3Gi
|
||||
cpu: 2
|
||||
# Defines a service account which provides an identity for processes that run in a Pod in Open Match.
|
||||
serviceAccount: open-match-unprivileged-service
|
||||
# Overrides the name of the service account which provides an identity for processes that run in a Pod in Open Match.
|
||||
serviceAccount:
|
||||
# Use this field if you need to override the port type for all services defined in this chart
|
||||
service:
|
||||
portType:
|
||||
@ -259,18 +274,18 @@ global:
|
||||
tag: 0.0.0-dev
|
||||
pullPolicy: Always
|
||||
|
||||
|
||||
# Expose the telemetry configurations to all subcharts because prometheus, for example,
|
||||
# requires pod-level annotation to customize its scrape path.
|
||||
# See definitions in templates/_helpers.tpl - "prometheus.annotations" section for details
|
||||
telemetry:
|
||||
reportingPeriod: "1m"
|
||||
traceSamplingFraction: 0.005 # What fraction of traces to sample.
|
||||
zpages:
|
||||
enabled: true
|
||||
jaeger:
|
||||
enabled: false
|
||||
samplerFraction: 0.005 # Configures a sampler that samples a given fraction of traces.
|
||||
agentEndpoint: "open-match-jaeger-agent:6831"
|
||||
collectorEndpoint: "http://open-match-jaeger-collector:14268/api/traces"
|
||||
agentEndpoint: '{{ include "openmatch.jaeger.agent" . }}'
|
||||
collectorEndpoint: '{{ include "openmatch.jaeger.collector" . }}'
|
||||
prometheus:
|
||||
enabled: false
|
||||
endpoint: "/metrics"
|
||||
@ -280,4 +295,3 @@ global:
|
||||
prefix: "open_match"
|
||||
grafana:
|
||||
enabled: false
|
||||
reportingPeriod: "1m"
|
||||
|
@ -23,7 +23,7 @@
|
||||
# Begins the configuration section for `query` component in Open Match.
|
||||
# query:
|
||||
#
|
||||
# # Specifies om-query as the in-cluster domain name for the `query` service.
|
||||
# # Override the default in-cluster domain name for the `query` service to om-query.
|
||||
# hostName: om-query
|
||||
#
|
||||
# # Specifies the port for receiving RESTful HTTP requests in the `query` service.
|
||||
@ -44,46 +44,46 @@
|
||||
# # Specifies the image name to be used in a Kubernetes pod for `query` compoenent.
|
||||
# image: openmatch-query
|
||||
swaggerui: &swaggerui
|
||||
hostName: om-swaggerui
|
||||
hostName:
|
||||
httpPort: 51500
|
||||
portType: ClusterIP
|
||||
replicas: 1
|
||||
image: openmatch-swaggerui
|
||||
query: &query
|
||||
hostName: om-query
|
||||
hostName:
|
||||
grpcPort: 50503
|
||||
httpPort: 51503
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-query
|
||||
frontend: &frontend
|
||||
hostName: om-frontend
|
||||
hostName:
|
||||
grpcPort: 50504
|
||||
httpPort: 51504
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-frontend
|
||||
backend: &backend
|
||||
hostName: om-backend
|
||||
hostName:
|
||||
grpcPort: 50505
|
||||
httpPort: 51505
|
||||
portType: ClusterIP
|
||||
replicas: 3
|
||||
image: openmatch-backend
|
||||
synchronizer: &synchronizer
|
||||
hostName: om-synchronizer
|
||||
hostName:
|
||||
grpcPort: 50506
|
||||
httpPort: 51506
|
||||
portType: ClusterIP
|
||||
replicas: 1
|
||||
image: openmatch-synchronizer
|
||||
evaluator: &evaluator
|
||||
hostName: om-evaluator
|
||||
hostName:
|
||||
grpcPort: 50508
|
||||
httpPort: 51508
|
||||
replicas: 3
|
||||
function: &function
|
||||
hostName: om-function
|
||||
hostName:
|
||||
grpcPort: 50502
|
||||
httpPort: 51502
|
||||
replicas: 3
|
||||
@ -94,17 +94,18 @@ configs:
|
||||
default:
|
||||
volumeName: om-config-volume-default
|
||||
mountPath: /app/config/default
|
||||
configName: om-configmap-default
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.default" . }}'
|
||||
override:
|
||||
volumeName: om-config-volume-override
|
||||
mountPath: /app/config/override
|
||||
configName: om-configmap-override
|
||||
# This will be parsed through the `tpl` function.
|
||||
configName: '{{ include "openmatch.configmap.override" . }}'
|
||||
|
||||
# Override Redis settings
|
||||
# https://hub.helm.sh/charts/stable/redis
|
||||
# https://github.com/helm/charts/tree/master/stable/redis
|
||||
redis:
|
||||
fullnameOverride: om-redis
|
||||
redisPort: 6379
|
||||
usePassword: false
|
||||
usePasswordFile: false
|
||||
@ -116,6 +117,7 @@ redis:
|
||||
enabled: true
|
||||
masterSet: om-redis-master
|
||||
port: 26379
|
||||
usePassword: false
|
||||
master:
|
||||
disableCommands: [] # don't disable 'FLUSH-' commands
|
||||
resources:
|
||||
@ -128,7 +130,6 @@ redis:
|
||||
slaveCount: 2
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: open-match-redis-service
|
||||
sysctlImage:
|
||||
# Enable this setting in production if you are running Open Match under Linux environment
|
||||
enabled: false
|
||||
@ -157,14 +158,32 @@ redis:
|
||||
# Controls if users need to install backend, frontend, query, om-configmap, and swaggerui.
|
||||
open-match-core:
|
||||
enabled: true
|
||||
ignoreListTTL: 60000ms
|
||||
|
||||
# Length of time between first fetch matches call, and when no further fetch
|
||||
# matches calls will join the current evaluation/synchronization cycle,
|
||||
# instead waiting for the next cycle.
|
||||
registrationInterval: 250ms
|
||||
# Length of time after match function as started before it will be canceled,
|
||||
# and evaluator call input is EOF.
|
||||
proposalCollectionInterval: 20s
|
||||
# Time after a ticket has been returned from fetch matches (marked as pending)
|
||||
# before it automatically becomes active again and will be returned by query
|
||||
# calls.
|
||||
pendingReleaseTimeout: 1m
|
||||
# Time after a ticket has been assigned before it is automatically delted.
|
||||
assignedDeleteTimeout: 10m
|
||||
# Maximum number of tickets to return on a single QueryTicketsResponse.
|
||||
queryPageSize: 10000
|
||||
# Duration for redis locks to expire.
|
||||
backfillLockTimeout: 1m
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
# If open-match-core.redis.enabled is set to false, have Open Match components talk to this redis address instead.
|
||||
# Otherwise the default is set to the om-redis instance.
|
||||
hostname: # Your redis server address
|
||||
port: 6379
|
||||
user:
|
||||
user:
|
||||
pool:
|
||||
maxIdle: 200
|
||||
maxActive: 0
|
||||
@ -177,8 +196,6 @@ open-match-core:
|
||||
open-match-scale:
|
||||
# Switch the value between true/false to turn on/off this subchart
|
||||
enabled: false
|
||||
frontend: *frontend
|
||||
backend: *backend
|
||||
|
||||
# Controls if users need to install the monitoring tools in Open Match.
|
||||
open-match-telemetry:
|
||||
@ -191,7 +208,6 @@ open-match-customize:
|
||||
enabled: false
|
||||
evaluator: *evaluator
|
||||
function: *function
|
||||
query: *query
|
||||
# You can override the evaluator/mmf image
|
||||
# evaluator:
|
||||
# image: [YOUR_EVALUATOR_IMAGE]
|
||||
@ -218,8 +234,8 @@ global:
|
||||
limits:
|
||||
memory: 100Mi
|
||||
cpu: 100m
|
||||
# Defines a service account which provides an identity for processes that run in a Pod in Open Match.
|
||||
serviceAccount: open-match-unprivileged-service
|
||||
# Overrides the name of the service account which provides an identity for processes that run in a Pod in Open Match.
|
||||
serviceAccount:
|
||||
# Use this field if you need to override the port type for all services defined in this chart
|
||||
service:
|
||||
portType:
|
||||
@ -241,21 +257,21 @@ global:
|
||||
# Use this field if you need to override the image registry and image tag for all services defined in this chart
|
||||
image:
|
||||
registry: gcr.io/open-match-public-images
|
||||
tag: 0.0.0-dev
|
||||
tag: 1.3.0
|
||||
pullPolicy: Always
|
||||
|
||||
|
||||
# Expose the telemetry configurations to all subcharts because prometheus, for example,
|
||||
# requires pod-level annotation to customize its scrape path.
|
||||
# See definitions in templates/_helpers.tpl - "prometheus.annotations" section for details
|
||||
telemetry:
|
||||
reportingPeriod: "1m"
|
||||
traceSamplingFraction: 0.01 # What fraction of traces to sample.
|
||||
zpages:
|
||||
enabled: true
|
||||
jaeger:
|
||||
enabled: false
|
||||
samplerFraction: 0.01 # Configures a sampler that samples a given fraction of traces.
|
||||
agentEndpoint: "open-match-jaeger-agent:6831"
|
||||
collectorEndpoint: "http://open-match-jaeger-collector:14268/api/traces"
|
||||
agentEndpoint: '{{ include "openmatch.jaeger.agent" . }}'
|
||||
collectorEndpoint: '{{ include "openmatch.jaeger.collector" . }}'
|
||||
prometheus:
|
||||
enabled: false
|
||||
endpoint: "/metrics"
|
||||
@ -265,4 +281,5 @@ global:
|
||||
prefix: "open_match"
|
||||
grafana:
|
||||
enabled: false
|
||||
reportingPeriod: "1m"
|
||||
# This will be called with `tpl` in the open-match-telemetry subchart namespace.
|
||||
prometheusServer: 'http://{{ include "call-nested" (list . "prometheus" "prometheus.server.fullname") }}.{{ .Release.Namespace }}.svc.cluster.local:80/'
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 Google LLC
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,12 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package e2e
|
||||
syntax = "proto3";
|
||||
package openmatch.internal;
|
||||
option go_package = "open-match.dev/open-match/internal/ipb";
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
import "api/messages.proto";
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
RunMain(m)
|
||||
}
|
||||
message BackfillInternal {
|
||||
// Represents a backfill entity which is used to fill partially full matches
|
||||
openmatch.Backfill backfill = 1;
|
||||
// List of ticket IDs associated with a current backfill
|
||||
repeated string ticket_ids = 2;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// 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 app contains the common application initialization code for Open Match servers.
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/logging"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = logrus.WithFields(logrus.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "app.main",
|
||||
})
|
||||
)
|
||||
|
||||
// RunApplication creates a server.
|
||||
func RunApplication(serverName string, getCfg func() (config.View, error), bindService func(*rpc.ServerParams, config.View) error) {
|
||||
cfg, err := getCfg()
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
}).Fatalf("cannot read configuration.")
|
||||
}
|
||||
logging.ConfigureLogging(cfg)
|
||||
p, err := rpc.NewServerParamsFromConfig(cfg, "api."+serverName)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
}).Fatalf("cannot construct server.")
|
||||
}
|
||||
|
||||
if err := bindService(p, cfg); err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
}).Fatalf("failed to bind %s service.", serverName)
|
||||
}
|
||||
|
||||
rpc.MustServeForever(p)
|
||||
}
|
@ -15,25 +15,81 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
// BindService creates the backend service and binds it to the serving harness.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View) error {
|
||||
service := &backendService{
|
||||
synchronizer: newSynchronizerClient(cfg),
|
||||
store: statestore.New(cfg),
|
||||
cc: rpc.NewClientCache(cfg),
|
||||
var (
|
||||
totalBytesPerMatch = stats.Int64("open-match.dev/backend/total_bytes_per_match", "Total bytes per match", stats.UnitBytes)
|
||||
ticketsPerMatch = stats.Int64("open-match.dev/backend/tickets_per_match", "Number of tickets per match", stats.UnitDimensionless)
|
||||
ticketsReleased = stats.Int64("open-match.dev/backend/tickets_released", "Number of tickets released per request", stats.UnitDimensionless)
|
||||
ticketsAssigned = stats.Int64("open-match.dev/backend/tickets_assigned", "Number of tickets assigned per request", stats.UnitDimensionless)
|
||||
ticketsTimeToAssignment = stats.Int64("open-match.dev/backend/ticket_time_to_assignment", "Time to assignment for tickets", stats.UnitMilliseconds)
|
||||
|
||||
totalMatchesView = &view.View{
|
||||
Measure: totalBytesPerMatch,
|
||||
Name: "open-match.dev/backend/total_matches",
|
||||
Description: "Total number of matches",
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
totalBytesPerMatchView = &view.View{
|
||||
Measure: totalBytesPerMatch,
|
||||
Name: "open-match.dev/backend/total_bytes_per_match",
|
||||
Description: "Total bytes per match",
|
||||
Aggregation: telemetry.DefaultBytesDistribution,
|
||||
}
|
||||
ticketsPerMatchView = &view.View{
|
||||
Measure: ticketsPerMatch,
|
||||
Name: "open-match.dev/backend/tickets_per_match",
|
||||
Description: "Tickets per ticket",
|
||||
Aggregation: telemetry.DefaultCountDistribution,
|
||||
}
|
||||
ticketsAssignedView = &view.View{
|
||||
Measure: ticketsAssigned,
|
||||
Name: "open-match.dev/backend/tickets_assigned",
|
||||
Description: "Number of tickets assigned per request",
|
||||
Aggregation: view.Sum(),
|
||||
}
|
||||
ticketsReleasedView = &view.View{
|
||||
Measure: ticketsReleased,
|
||||
Name: "open-match.dev/backend/tickets_released",
|
||||
Description: "Number of tickets released per request",
|
||||
Aggregation: view.Sum(),
|
||||
}
|
||||
|
||||
p.AddHealthCheckFunc(service.store.HealthCheck)
|
||||
p.AddHandleFunc(func(s *grpc.Server) {
|
||||
ticketsTimeToAssignmentView = &view.View{
|
||||
Measure: ticketsTimeToAssignment,
|
||||
Name: "open-match.dev/backend/ticket_time_to_assignment",
|
||||
Description: "Time to assignment for tickets",
|
||||
Aggregation: telemetry.DefaultMillisecondsDistribution,
|
||||
}
|
||||
)
|
||||
|
||||
// BindService creates the backend service and binds it to the serving harness.
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
service := &backendService{
|
||||
synchronizer: newSynchronizerClient(p.Config()),
|
||||
store: statestore.New(p.Config()),
|
||||
cc: rpc.NewClientCache(p.Config()),
|
||||
}
|
||||
|
||||
b.AddHealthCheckFunc(service.store.HealthCheck)
|
||||
b.AddHandleFunc(func(s *grpc.Server) {
|
||||
pb.RegisterBackendServiceServer(s, service)
|
||||
}, pb.RegisterBackendServiceHandlerFromEndpoint)
|
||||
|
||||
b.RegisterViews(
|
||||
totalMatchesView,
|
||||
totalBytesPerMatchView,
|
||||
ticketsPerMatchView,
|
||||
ticketsAssignedView,
|
||||
ticketsReleasedView,
|
||||
ticketsTimeToAssignmentView,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
@ -22,17 +22,24 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/xid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"open-match.dev/open-match/internal/appmain/contextcause"
|
||||
"open-match.dev/open-match/internal/ipb"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
@ -49,10 +56,7 @@ var (
|
||||
"app": "openmatch",
|
||||
"component": "app.backend",
|
||||
})
|
||||
mMatchesFetched = telemetry.Counter("backend/matches_fetched", "matches fetched")
|
||||
mMatchesSentToEvaluation = telemetry.Counter("backend/matches_sent_to_evaluation", "matches sent to evaluation")
|
||||
mTicketsAssigned = telemetry.Counter("backend/tickets_assigned", "tickets assigned")
|
||||
mTicketsReleased = telemetry.Counter("backend/tickets_released", "tickets released")
|
||||
errBackfillGenerationMismatch = errors.New("backfill generation mismatch")
|
||||
)
|
||||
|
||||
// FetchMatches triggers a MatchFunction with the specified MatchProfiles, while each MatchProfile
|
||||
@ -60,10 +64,10 @@ var (
|
||||
// FetchMatches immediately returns an error if it encounters any execution failures.
|
||||
// - If the synchronizer is enabled, FetchMatch will then call the synchronizer to deduplicate proposals with overlapped tickets.
|
||||
func (s *backendService) FetchMatches(req *pb.FetchMatchesRequest, stream pb.BackendService_FetchMatchesServer) error {
|
||||
if req.GetConfig() == nil {
|
||||
if req.Config == nil {
|
||||
return status.Error(codes.InvalidArgument, ".config is required")
|
||||
}
|
||||
if req.GetProfile() == nil {
|
||||
if req.Profile == nil {
|
||||
return status.Error(codes.InvalidArgument, ".profile is required")
|
||||
}
|
||||
|
||||
@ -77,7 +81,7 @@ func (s *backendService) FetchMatches(req *pb.FetchMatchesRequest, stream pb.Bac
|
||||
// The mmf must be canceled if the synchronizer call fails (which will
|
||||
// cancel the context from the error group). However the synchronizer call
|
||||
// is NOT dependant on the mmf call.
|
||||
mmfCtx, cancelMmfs := context.WithCancel(ctx)
|
||||
mmfCtx, cancelMmfs := contextcause.WithCancelCause(ctx)
|
||||
// Closed when mmfs should start.
|
||||
startMmfs := make(chan struct{})
|
||||
proposals := make(chan *pb.Match)
|
||||
@ -87,7 +91,7 @@ func (s *backendService) FetchMatches(req *pb.FetchMatchesRequest, stream pb.Bac
|
||||
return synchronizeSend(ctx, syncStream, m, proposals)
|
||||
})
|
||||
eg.Go(func() error {
|
||||
return synchronizeRecv(ctx, syncStream, m, stream, startMmfs, cancelMmfs)
|
||||
return synchronizeRecv(ctx, syncStream, m, stream, startMmfs, cancelMmfs, s.store)
|
||||
})
|
||||
|
||||
var mmfErr error
|
||||
@ -102,13 +106,8 @@ func (s *backendService) FetchMatches(req *pb.FetchMatchesRequest, stream pb.Bac
|
||||
|
||||
// TODO: Send mmf error in FetchSummary instead of erroring call.
|
||||
if syncErr != nil || mmfErr != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"syncErr": syncErr,
|
||||
"mmfErr": mmfErr,
|
||||
}).Error("error(s) in FetchMatches call.")
|
||||
|
||||
return fmt.Errorf(
|
||||
"error(s) in FetchMatches call. syncErr=[%s], mmfErr=[%s]",
|
||||
"error(s) in FetchMatches call. syncErr=[%v], mmfErr=[%v]",
|
||||
syncErr,
|
||||
mmfErr,
|
||||
)
|
||||
@ -127,11 +126,10 @@ sendProposals:
|
||||
if !ok {
|
||||
break sendProposals
|
||||
}
|
||||
id, loaded := m.LoadOrStore(p.GetMatchId(), p)
|
||||
_, loaded := m.LoadOrStore(p.GetMatchId(), p)
|
||||
if loaded {
|
||||
return fmt.Errorf("found duplicate matchID %s returned from MMF", id)
|
||||
return fmt.Errorf("MatchMakingFunction returned same match_id twice: \"%s\"", p.GetMatchId())
|
||||
}
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchesSentToEvaluation)
|
||||
err := syncStream.Send(&ipb.SynchronizeRequest{Proposal: p})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending proposal to synchronizer: %w", err)
|
||||
@ -146,7 +144,7 @@ sendProposals:
|
||||
return nil
|
||||
}
|
||||
|
||||
func synchronizeRecv(ctx context.Context, syncStream synchronizerStream, m *sync.Map, stream pb.BackendService_FetchMatchesServer, startMmfs chan<- struct{}, cancelMmfs context.CancelFunc) error {
|
||||
func synchronizeRecv(ctx context.Context, syncStream synchronizerStream, m *sync.Map, stream pb.BackendService_FetchMatchesServer, startMmfs chan<- struct{}, cancelMmfs contextcause.CancelErrFunc, store statestore.Service) error {
|
||||
var startMmfsOnce sync.Once
|
||||
|
||||
for {
|
||||
@ -165,12 +163,42 @@ func synchronizeRecv(ctx context.Context, syncStream synchronizerStream, m *sync
|
||||
}
|
||||
|
||||
if resp.CancelMmfs {
|
||||
cancelMmfs()
|
||||
cancelMmfs(errors.New("match function ran longer than proposal window, canceling"))
|
||||
}
|
||||
|
||||
if match, ok := m.Load(resp.GetMatchId()); ok {
|
||||
telemetry.RecordUnitMeasurement(ctx, mMatchesFetched)
|
||||
err = stream.Send(&pb.FetchMatchesResponse{Match: match.(*pb.Match)})
|
||||
if v, ok := m.Load(resp.GetMatchId()); ok {
|
||||
match, ok := v.(*pb.Match)
|
||||
if !ok {
|
||||
return fmt.Errorf("error casting sync map value into *pb.Match: %w", err)
|
||||
}
|
||||
|
||||
backfill := match.GetBackfill()
|
||||
if backfill != nil {
|
||||
ticketIds := make([]string, 0, len(match.Tickets))
|
||||
|
||||
for _, t := range match.Tickets {
|
||||
ticketIds = append(ticketIds, t.Id)
|
||||
}
|
||||
|
||||
err = createOrUpdateBackfill(ctx, backfill, ticketIds, store)
|
||||
if err != nil {
|
||||
e, ok := status.FromError(err)
|
||||
if err == errBackfillGenerationMismatch || (ok && e.Code() == codes.NotFound) {
|
||||
err = doReleaseTickets(ctx, ticketIds, store)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to remove match tickets from pending release: %v", ticketIds)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return errors.Wrapf(err, "failed to handle match backfill: %s", match.MatchId)
|
||||
}
|
||||
}
|
||||
|
||||
stats.Record(ctx, totalBytesPerMatch.M(int64(proto.Size(match))))
|
||||
stats.Record(ctx, ticketsPerMatch.M(int64(len(match.GetTickets()))))
|
||||
err = stream.Send(&pb.FetchMatchesResponse{Match: match})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending match to caller of backend: %w", err)
|
||||
}
|
||||
@ -197,17 +225,17 @@ func callGrpcMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProf
|
||||
var conn *grpc.ClientConn
|
||||
conn, err := cc.GetGRPC(address)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"function": address,
|
||||
}).Error("failed to establish grpc client connection to match function")
|
||||
return status.Error(codes.InvalidArgument, "failed to connect to match function")
|
||||
return status.Error(codes.InvalidArgument, "failed to establish grpc client connection to match function")
|
||||
}
|
||||
client := pb.NewMatchFunctionClient(conn)
|
||||
|
||||
stream, err := client.Run(ctx, &pb.RunRequest{Profile: profile})
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to run match function for profile")
|
||||
err = errors.Wrap(err, "failed to run match function for profile")
|
||||
if ctx.Err() != nil {
|
||||
// gRPC likes to suppress the context's error, so stop that.
|
||||
return ctx.Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@ -217,7 +245,11 @@ func callGrpcMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProf
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
logger.Errorf("%v.Run() error, %v\n", client, err)
|
||||
err = errors.Wrapf(err, "%v.Run() error, %v", client, err)
|
||||
if ctx.Err() != nil {
|
||||
// gRPC likes to suppress the context's error, so stop that.
|
||||
return ctx.Err()
|
||||
}
|
||||
return err
|
||||
}
|
||||
select {
|
||||
@ -233,11 +265,8 @@ func callGrpcMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProf
|
||||
func callHTTPMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProfile, address string, proposals chan<- *pb.Match) error {
|
||||
client, baseURL, err := cc.GetHTTP(address)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"function": address,
|
||||
}).Error("failed to establish rest client connection to match function")
|
||||
return status.Error(codes.InvalidArgument, "failed to connect to match function")
|
||||
err = errors.Wrapf(err, "failed to establish rest client connection to match function: %s", address)
|
||||
return status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
var m jsonpb.Marshaler
|
||||
@ -253,7 +282,7 @@ func callHTTPMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProf
|
||||
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "failed to get response from mmf run for proile %s: %s", profile.Name, err.Error())
|
||||
return status.Errorf(codes.Internal, "failed to get response from mmf run for profile %s: %s", profile.Name, err.Error())
|
||||
}
|
||||
defer func() {
|
||||
err = resp.Body.Close()
|
||||
@ -294,21 +323,37 @@ func callHTTPMmf(ctx context.Context, cc *rpc.ClientCache, profile *pb.MatchProf
|
||||
}
|
||||
|
||||
func (s *backendService) ReleaseTickets(ctx context.Context, req *pb.ReleaseTicketsRequest) (*pb.ReleaseTicketsResponse, error) {
|
||||
err := doReleasetickets(ctx, req, s.store)
|
||||
err := doReleaseTickets(ctx, req.GetTicketIds(), s.store)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to remove the awaiting tickets from the ignore list for requested tickets")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
telemetry.RecordNUnitMeasurement(ctx, mTicketsReleased, int64(len(req.TicketIds)))
|
||||
return &pb.ReleaseTicketsResponse{}, nil
|
||||
}
|
||||
|
||||
func doReleaseTickets(ctx context.Context, ticketIds []string, store statestore.Service) error {
|
||||
err := store.DeleteTicketsFromPendingRelease(ctx, ticketIds)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "failed to remove the awaiting tickets from the pending release for requested tickets")
|
||||
return err
|
||||
}
|
||||
|
||||
stats.Record(ctx, ticketsReleased.M(int64(len(ticketIds))))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *backendService) ReleaseAllTickets(ctx context.Context, req *pb.ReleaseAllTicketsRequest) (*pb.ReleaseAllTicketsResponse, error) {
|
||||
err := s.store.ReleaseAllTickets(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.ReleaseAllTicketsResponse{}, nil
|
||||
}
|
||||
|
||||
// AssignTickets overwrites the Assignment field of the input TicketIds.
|
||||
func (s *backendService) AssignTickets(ctx context.Context, req *pb.AssignTicketsRequest) (*pb.AssignTicketsResponse, error) {
|
||||
resp, err := doAssignTickets(ctx, req, s.store)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to update assignments for requested tickets")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -317,17 +362,73 @@ func (s *backendService) AssignTickets(ctx context.Context, req *pb.AssignTicket
|
||||
numIds += len(ag.TicketIds)
|
||||
}
|
||||
|
||||
telemetry.RecordNUnitMeasurement(ctx, mTicketsAssigned, int64(numIds))
|
||||
stats.Record(ctx, ticketsAssigned.M(int64(numIds)))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func doAssignTickets(ctx context.Context, req *pb.AssignTicketsRequest, store statestore.Service) (*pb.AssignTicketsResponse, error) {
|
||||
resp, err := store.UpdateAssignments(ctx, req)
|
||||
func createOrUpdateBackfill(ctx context.Context, backfill *pb.Backfill, ticketIds []string, store statestore.Service) error {
|
||||
if backfill.Id == "" {
|
||||
backfill.Id = xid.New().String()
|
||||
backfill.CreateTime = ptypes.TimestampNow()
|
||||
backfill.Generation = 1
|
||||
err := store.CreateBackfill(ctx, backfill, ticketIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.IndexBackfill(ctx, backfill)
|
||||
}
|
||||
|
||||
m := store.NewMutex(backfill.Id)
|
||||
err := m.Lock(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, unlockErr := m.Unlock(ctx)
|
||||
if unlockErr != nil {
|
||||
logger.WithFields(logrus.Fields{"backfill_id": backfill.Id}).WithError(unlockErr).Error("failed to make unlock")
|
||||
}
|
||||
}()
|
||||
|
||||
b, ids, err := store.GetBackfill(ctx, backfill.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Generation != backfill.Generation {
|
||||
logger.WithFields(logrus.Fields{"backfill_id": backfill.Id}).
|
||||
WithError(errBackfillGenerationMismatch).
|
||||
Errorf("failed to update backfill, expecting: %d generation but got: %d", b.Generation, backfill.Generation)
|
||||
return errBackfillGenerationMismatch
|
||||
}
|
||||
|
||||
b.SearchFields = backfill.SearchFields
|
||||
b.Extensions = backfill.Extensions
|
||||
b.Generation++
|
||||
|
||||
err = store.UpdateBackfill(ctx, b, append(ids, ticketIds...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.IndexBackfill(ctx, b)
|
||||
}
|
||||
|
||||
func doAssignTickets(ctx context.Context, req *pb.AssignTicketsRequest, store statestore.Service) (*pb.AssignTicketsResponse, error) {
|
||||
resp, tickets, err := store.UpdateAssignments(ctx, req)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to update assignments")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ticket := range tickets {
|
||||
err = recordTimeToAssignment(ctx, ticket)
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to record time to assignment for ticket %s", ticket.Id)
|
||||
}
|
||||
}
|
||||
|
||||
ids := []string{}
|
||||
|
||||
for _, ag := range req.Assignments {
|
||||
@ -343,7 +444,7 @@ func doAssignTickets(ctx context.Context, req *pb.AssignTicketsRequest, store st
|
||||
}
|
||||
}
|
||||
|
||||
if err = store.DeleteTicketsFromIgnoreList(ctx, ids); err != nil {
|
||||
if err = store.DeleteTicketsFromPendingRelease(ctx, ids); err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"ticket_ids": ids,
|
||||
}).Error(err)
|
||||
@ -352,14 +453,18 @@ func doAssignTickets(ctx context.Context, req *pb.AssignTicketsRequest, store st
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func doReleasetickets(ctx context.Context, req *pb.ReleaseTicketsRequest, store statestore.Service) error {
|
||||
err := store.DeleteTicketsFromIgnoreList(ctx, req.GetTicketIds())
|
||||
func recordTimeToAssignment(ctx context.Context, ticket *pb.Ticket) error {
|
||||
if ticket.Assignment == nil {
|
||||
return fmt.Errorf("assignment for ticket %s is nil", ticket.Id)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
created, err := ptypes.Timestamp(ticket.CreateTime)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"ticket_ids": req.GetTicketIds(),
|
||||
}).WithError(err).Error("failed to delete the tickets from the ignore list")
|
||||
return err
|
||||
}
|
||||
|
||||
stats.Record(ctx, ticketsTimeToAssignment.M(now.Sub(created).Milliseconds()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -16,12 +16,17 @@
|
||||
package defaulteval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/stats/view"
|
||||
"open-match.dev/open-match/internal/app/evaluator"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
@ -30,6 +35,14 @@ var (
|
||||
"app": "evaluator",
|
||||
"component": "evaluator.default",
|
||||
})
|
||||
|
||||
collidedMatchesPerEvaluate = stats.Int64("open-match.dev/defaulteval/collided_matches_per_call", "Number of collided matches per default evaluator call", stats.UnitDimensionless)
|
||||
collidedMatchesPerEvaluateView = &view.View{
|
||||
Measure: collidedMatchesPerEvaluate,
|
||||
Name: "open-match.dev/defaulteval/collided_matches_per_call",
|
||||
Description: "Number of collided matches per default evaluator call",
|
||||
Aggregation: view.Sum(),
|
||||
}
|
||||
)
|
||||
|
||||
type matchInp struct {
|
||||
@ -37,13 +50,22 @@ type matchInp struct {
|
||||
inp *pb.DefaultEvaluationCriteria
|
||||
}
|
||||
|
||||
// Evaluate sorts the matches by DefaultEvaluationCriteria.Score (optional),
|
||||
// then returns matches which don't collide with previously returned matches.
|
||||
func Evaluate(p *evaluator.Params) ([]string, error) {
|
||||
matches := make([]*matchInp, 0, len(p.Matches))
|
||||
nilEvlautionInputs := 0
|
||||
// BindService define the initialization steps for this evaluator
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
if err := evaluator.BindServiceFor(evaluate)(p, b); err != nil {
|
||||
return err
|
||||
}
|
||||
b.RegisterViews(collidedMatchesPerEvaluateView)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, m := range p.Matches {
|
||||
// evaluate sorts the matches by DefaultEvaluationCriteria.Score (optional),
|
||||
// then returns matches which don't collide with previously returned matches.
|
||||
func evaluate(ctx context.Context, in <-chan *pb.Match, out chan<- string) error {
|
||||
matches := make([]*matchInp, 0)
|
||||
nilEvaluationInputs := 0
|
||||
|
||||
for m := range in {
|
||||
// Evaluation criteria is optional, but sort it lower than any matches which
|
||||
// provided criteria.
|
||||
inp := &pb.DefaultEvaluationCriteria{
|
||||
@ -60,7 +82,7 @@ func Evaluate(p *evaluator.Params) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
nilEvlautionInputs++
|
||||
nilEvaluationInputs++
|
||||
}
|
||||
matches = append(matches, &matchInp{
|
||||
match: m,
|
||||
@ -68,23 +90,30 @@ func Evaluate(p *evaluator.Params) ([]string, error) {
|
||||
})
|
||||
}
|
||||
|
||||
if nilEvlautionInputs > 0 {
|
||||
if nilEvaluationInputs > 0 {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"count": nilEvlautionInputs,
|
||||
"count": nilEvaluationInputs,
|
||||
}).Info("Some matches don't have the optional field evaluation_input set.")
|
||||
}
|
||||
|
||||
sort.Sort(byScore(matches))
|
||||
|
||||
d := decollider{
|
||||
ticketsUsed: make(map[string]*collidingMatch),
|
||||
ticketsUsed: make(map[string]*collidingMatch),
|
||||
backfillsUsed: make(map[string]*collidingMatch),
|
||||
}
|
||||
|
||||
for _, m := range matches {
|
||||
d.maybeAdd(m)
|
||||
}
|
||||
|
||||
return d.resultIDs, nil
|
||||
stats.Record(context.Background(), collidedMatchesPerEvaluate.M(int64(len(matches)-len(d.resultIDs))))
|
||||
|
||||
for _, id := range d.resultIDs {
|
||||
out <- id
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type collidingMatch struct {
|
||||
@ -93,11 +122,25 @@ type collidingMatch struct {
|
||||
}
|
||||
|
||||
type decollider struct {
|
||||
resultIDs []string
|
||||
ticketsUsed map[string]*collidingMatch
|
||||
resultIDs []string
|
||||
ticketsUsed map[string]*collidingMatch
|
||||
backfillsUsed map[string]*collidingMatch
|
||||
}
|
||||
|
||||
func (d *decollider) maybeAdd(m *matchInp) {
|
||||
if m.match.Backfill != nil && m.match.Backfill.Id != "" {
|
||||
if cm, ok := d.backfillsUsed[m.match.Backfill.Id]; ok {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"match_id": m.match.GetMatchId(),
|
||||
"backfill_id": m.match.Backfill.Id,
|
||||
"match_score": m.inp.GetScore(),
|
||||
"colliding_match_id": cm.id,
|
||||
"colliding_match_score": cm.score,
|
||||
}).Info("Higher quality match with colliding backfill found. Rejecting match.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range m.match.GetTickets() {
|
||||
if cm, ok := d.ticketsUsed[t.Id]; ok {
|
||||
logger.WithFields(logrus.Fields{
|
||||
@ -111,6 +154,13 @@ func (d *decollider) maybeAdd(m *matchInp) {
|
||||
}
|
||||
}
|
||||
|
||||
if m.match.Backfill != nil && m.match.Backfill.Id != "" {
|
||||
d.backfillsUsed[m.match.Backfill.Id] = &collidingMatch{
|
||||
id: m.match.GetMatchId(),
|
||||
score: m.inp.GetScore(),
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range m.match.GetTickets() {
|
||||
d.ticketsUsed[t.Id] = &collidingMatch{
|
||||
id: m.match.GetMatchId(),
|
||||
|
@ -15,13 +15,13 @@
|
||||
package defaulteval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"open-match.dev/open-match/internal/app/evaluator"
|
||||
"github.com/stretchr/testify/require"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
@ -37,6 +37,9 @@ func TestEvaluate(t *testing.T) {
|
||||
ticket1 := &pb.Ticket{Id: "1"}
|
||||
ticket2 := &pb.Ticket{Id: "2"}
|
||||
ticket3 := &pb.Ticket{Id: "3"}
|
||||
backfill0 := &pb.Backfill{}
|
||||
backfill1 := &pb.Backfill{Id: "1"}
|
||||
backfill2 := &pb.Backfill{Id: "2"}
|
||||
|
||||
ticket12Score1 := &pb.Match{
|
||||
MatchId: "ticket12Score1",
|
||||
@ -78,6 +81,61 @@ func TestEvaluate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ticket1Backfill0Score1 := &pb.Match{
|
||||
MatchId: "ticket1Backfill0Score1",
|
||||
Tickets: []*pb.Ticket{ticket1},
|
||||
Backfill: backfill0,
|
||||
Extensions: map[string]*any.Any{
|
||||
"evaluation_input": mustAny(&pb.DefaultEvaluationCriteria{
|
||||
Score: 1,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ticket2Backfill0Score1 := &pb.Match{
|
||||
MatchId: "ticket2Backfill0Score1",
|
||||
Tickets: []*pb.Ticket{ticket2},
|
||||
Backfill: backfill0,
|
||||
Extensions: map[string]*any.Any{
|
||||
"evaluation_input": mustAny(&pb.DefaultEvaluationCriteria{
|
||||
Score: 1,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ticket12Backfill1Score1 := &pb.Match{
|
||||
MatchId: "ticket12Bacfill1Score1",
|
||||
Tickets: []*pb.Ticket{ticket1, ticket2},
|
||||
Backfill: backfill1,
|
||||
Extensions: map[string]*any.Any{
|
||||
"evaluation_input": mustAny(&pb.DefaultEvaluationCriteria{
|
||||
Score: 1,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ticket12Backfill1Score10 := &pb.Match{
|
||||
MatchId: "ticket12Bacfill1Score1",
|
||||
Tickets: []*pb.Ticket{ticket1, ticket2},
|
||||
Backfill: backfill1,
|
||||
Extensions: map[string]*any.Any{
|
||||
"evaluation_input": mustAny(&pb.DefaultEvaluationCriteria{
|
||||
Score: 10,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ticket12Backfill2Score5 := &pb.Match{
|
||||
MatchId: "ticket12Backfill2Score5",
|
||||
Tickets: []*pb.Ticket{ticket1, ticket2},
|
||||
Backfill: backfill2,
|
||||
Extensions: map[string]*any.Any{
|
||||
"evaluation_input": mustAny(&pb.DefaultEvaluationCriteria{
|
||||
Score: 5,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
testMatches []*pb.Match
|
||||
@ -108,19 +166,41 @@ func TestEvaluate(t *testing.T) {
|
||||
testMatches: []*pb.Match{ticket12Score1, ticket12Score10, ticket123Score5, ticket3Score50},
|
||||
wantMatchIDs: []string{ticket12Score10.GetMatchId(), ticket3Score50.GetMatchId()},
|
||||
},
|
||||
{
|
||||
description: "test evaluator ignores backfills with empty id",
|
||||
testMatches: []*pb.Match{ticket1Backfill0Score1, ticket2Backfill0Score1},
|
||||
wantMatchIDs: []string{ticket1Backfill0Score1.GetMatchId(), ticket2Backfill0Score1.GetMatchId()},
|
||||
},
|
||||
{
|
||||
description: "test deduplicates matches by backfill and tickets and returns match with higher score",
|
||||
testMatches: []*pb.Match{ticket12Backfill1Score1, ticket12Backfill1Score10, ticket12Backfill2Score5},
|
||||
wantMatchIDs: []string{ticket12Backfill1Score10.GetMatchId()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
gotMatchIDs, err := Evaluate(&evaluator.Params{Matches: test.testMatches})
|
||||
in := make(chan *pb.Match, 10)
|
||||
out := make(chan string, 10)
|
||||
for _, m := range test.testMatches {
|
||||
in <- m
|
||||
}
|
||||
close(in)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(test.wantMatchIDs), len(gotMatchIDs))
|
||||
err := evaluate(context.Background(), in, out)
|
||||
require.Nil(t, err)
|
||||
|
||||
gotMatchIDs := []string{}
|
||||
close(out)
|
||||
for id := range out {
|
||||
gotMatchIDs = append(gotMatchIDs, id)
|
||||
}
|
||||
require.Equal(t, len(test.wantMatchIDs), len(gotMatchIDs))
|
||||
|
||||
for _, mID := range gotMatchIDs {
|
||||
assert.Contains(t, test.wantMatchIDs, mID)
|
||||
require.Contains(t, test.wantMatchIDs, mID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -16,35 +16,42 @@
|
||||
package evaluator
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/internal/app"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
// RunEvaluator is a hook for the main() method in the main executable.
|
||||
func RunEvaluator(eval Evaluator) {
|
||||
app.RunApplication("evaluator", getCfg, func(p *rpc.ServerParams, cfg config.View) error {
|
||||
return BindService(p, cfg, eval)
|
||||
})
|
||||
}
|
||||
|
||||
// BindService creates the evaluator service to the server Params.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View, eval Evaluator) error {
|
||||
p.AddHandleFunc(func(s *grpc.Server) {
|
||||
pb.RegisterEvaluatorServer(s, &evaluatorService{evaluate: eval})
|
||||
}, pb.RegisterEvaluatorHandlerFromEndpoint)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCfg() (config.View, error) {
|
||||
cfg := viper.New()
|
||||
cfg.Set("api.evaluator.hostname", "om-evaluator")
|
||||
cfg.Set("api.evaluator.grpcport", 50508)
|
||||
cfg.Set("api.evaluator.httpport", 51508)
|
||||
|
||||
return cfg, nil
|
||||
var (
|
||||
matchesPerEvaluateRequest = stats.Int64("open-match.dev/evaluator/matches_per_request", "Number of matches sent to the evaluator per request", stats.UnitDimensionless)
|
||||
matchesPerEvaluateResponse = stats.Int64("open-match.dev/evaluator/matches_per_response", "Number of matches returned by the evaluator per response", stats.UnitDimensionless)
|
||||
|
||||
matchesPerEvaluateRequestView = &view.View{
|
||||
Measure: matchesPerEvaluateRequest,
|
||||
Name: "open-match.dev/evaluator/matches_per_request",
|
||||
Description: "Number of matches sent to the evaluator per request",
|
||||
Aggregation: telemetry.DefaultCountDistribution,
|
||||
}
|
||||
matchesPerEvaluateResponseView = &view.View{
|
||||
Measure: matchesPerEvaluateResponse,
|
||||
Name: "open-match.dev/evaluator/matches_per_response",
|
||||
Description: "Number of matches sent to the evaluator per response",
|
||||
Aggregation: telemetry.DefaultCountDistribution,
|
||||
}
|
||||
)
|
||||
|
||||
// BindServiceFor creates the evaluator service and binds it to the serving harness.
|
||||
func BindServiceFor(eval Evaluator) appmain.Bind {
|
||||
return func(p *appmain.Params, b *appmain.Bindings) error {
|
||||
b.AddHandleFunc(func(s *grpc.Server) {
|
||||
pb.RegisterEvaluatorServer(s, &evaluatorService{eval})
|
||||
}, pb.RegisterEvaluatorHandlerFromEndpoint)
|
||||
b.RegisterViews(
|
||||
matchesPerEvaluateRequestView,
|
||||
matchesPerEvaluateResponseView,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -16,26 +16,19 @@
|
||||
package evaluator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"github.com/pkg/errors"
|
||||
"go.opencensus.io/stats"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = logrus.WithFields(logrus.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "evaluator.harness.golang",
|
||||
})
|
||||
)
|
||||
|
||||
// Evaluator is the function signature for the Evaluator to be implemented by
|
||||
// the user. The harness will pass the Matches to evaluate to the Evaluator
|
||||
// and the Evaluator will return an accepted list of Matches.
|
||||
type Evaluator func(*Params) ([]string, error)
|
||||
type Evaluator func(ctx context.Context, in <-chan *pb.Match, out chan<- string) error
|
||||
|
||||
// evaluatorService implements pb.EvaluatorServer, the server generated by
|
||||
// compiling the protobuf, by fulfilling the pb.EvaluatorServer interface.
|
||||
@ -43,51 +36,57 @@ type evaluatorService struct {
|
||||
evaluate Evaluator
|
||||
}
|
||||
|
||||
// Params is the parameters to be passed by the harness to the evaluator.
|
||||
// - logger:
|
||||
// A logger used to generate error/debug logs
|
||||
// - Matches
|
||||
// Matches to be evaluated
|
||||
type Params struct {
|
||||
Logger *logrus.Entry
|
||||
Matches []*pb.Match
|
||||
}
|
||||
|
||||
// Evaluate is this harness's implementation of the gRPC call defined in
|
||||
// api/evaluator.proto.
|
||||
func (s *evaluatorService) Evaluate(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())
|
||||
}
|
||||
g, ctx := errgroup.WithContext(stream.Context())
|
||||
|
||||
// Run the customized evaluator!
|
||||
results, err := s.evaluate(&Params{
|
||||
Logger: logrus.WithFields(logrus.Fields{
|
||||
"app": "openmatch",
|
||||
"component": "evaluator.implementation",
|
||||
}),
|
||||
Matches: matches,
|
||||
in := make(chan *pb.Match)
|
||||
out := make(chan string)
|
||||
|
||||
g.Go(func() error {
|
||||
defer close(in)
|
||||
count := 0
|
||||
for {
|
||||
req, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case in <- req.Match:
|
||||
count++
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
stats.Record(ctx, matchesPerEvaluateRequest.M(int64(count)))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return status.Error(codes.Aborted, err.Error())
|
||||
}
|
||||
g.Go(func() error {
|
||||
defer close(out)
|
||||
return s.evaluate(ctx, in, out)
|
||||
})
|
||||
g.Go(func() error {
|
||||
defer func() {
|
||||
for range out {
|
||||
}
|
||||
}()
|
||||
|
||||
for _, result := range results {
|
||||
if err := stream.Send(&pb.EvaluateResponse{MatchId: result}); err != nil {
|
||||
return err
|
||||
count := 0
|
||||
for id := range out {
|
||||
err := stream.Send(&pb.EvaluateResponse{MatchId: id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
stats.Record(ctx, matchesPerEvaluateResponse.M(int64(count)))
|
||||
return nil
|
||||
})
|
||||
|
||||
logger.WithFields(logrus.Fields{
|
||||
"results": results,
|
||||
}).Debug("matches accepted by the evaluator")
|
||||
return nil
|
||||
err := g.Wait()
|
||||
return errors.Wrap(err, "Error in evaluator.Evaluate")
|
||||
}
|
||||
|
@ -15,24 +15,63 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"google.golang.org/grpc"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
var (
|
||||
totalBytesPerTicket = stats.Int64("open-match.dev/frontend/total_bytes_per_ticket", "Total bytes per ticket", stats.UnitBytes)
|
||||
searchFieldsPerTicket = stats.Int64("open-match.dev/frontend/searchfields_per_ticket", "Searchfields per ticket", stats.UnitDimensionless)
|
||||
totalBytesPerBackfill = stats.Int64("open-match.dev/frontend/total_bytes_per_backfill", "Total bytes per backfill", stats.UnitBytes)
|
||||
searchFieldsPerBackfill = stats.Int64("open-match.dev/frontend/searchfields_per_backfill", "Searchfields per backfill", stats.UnitDimensionless)
|
||||
|
||||
totalBytesPerTicketView = &view.View{
|
||||
Measure: totalBytesPerTicket,
|
||||
Name: "open-match.dev/frontend/total_bytes_per_ticket",
|
||||
Description: "Total bytes per ticket",
|
||||
Aggregation: telemetry.DefaultBytesDistribution,
|
||||
}
|
||||
searchFieldsPerTicketView = &view.View{
|
||||
Measure: searchFieldsPerTicket,
|
||||
Name: "open-match.dev/frontend/searchfields_per_ticket",
|
||||
Description: "SearchFields per ticket",
|
||||
Aggregation: telemetry.DefaultCountDistribution,
|
||||
}
|
||||
totalBytesPerBackfillView = &view.View{
|
||||
Measure: totalBytesPerBackfill,
|
||||
Name: "open-match.dev/frontend/total_bytes_per_backfill",
|
||||
Description: "Total bytes per backfill",
|
||||
Aggregation: telemetry.DefaultBytesDistribution,
|
||||
}
|
||||
searchFieldsPerBackfillView = &view.View{
|
||||
Measure: searchFieldsPerBackfill,
|
||||
Name: "open-match.dev/frontend/searchfields_per_backfill",
|
||||
Description: "SearchFields per backfill",
|
||||
Aggregation: telemetry.DefaultCountDistribution,
|
||||
}
|
||||
)
|
||||
|
||||
// BindService creates the frontend service and binds it to the serving harness.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View) error {
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
service := &frontendService{
|
||||
cfg: cfg,
|
||||
store: statestore.New(cfg),
|
||||
cfg: p.Config(),
|
||||
store: statestore.New(p.Config()),
|
||||
}
|
||||
|
||||
p.AddHealthCheckFunc(service.store.HealthCheck)
|
||||
p.AddHandleFunc(func(s *grpc.Server) {
|
||||
b.AddHealthCheckFunc(service.store.HealthCheck)
|
||||
b.AddHandleFunc(func(s *grpc.Server) {
|
||||
pb.RegisterFrontendServiceServer(s, service)
|
||||
}, pb.RegisterFrontendServiceHandlerFromEndpoint)
|
||||
|
||||
b.RegisterViews(
|
||||
totalBytesPerTicketView,
|
||||
searchFieldsPerTicketView,
|
||||
totalBytesPerBackfillView,
|
||||
searchFieldsPerBackfillView,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ import (
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/rs/xid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
"open-match.dev/open-match/internal/telemetry"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
@ -43,10 +43,6 @@ var (
|
||||
"app": "openmatch",
|
||||
"component": "app.frontend",
|
||||
})
|
||||
mTicketsCreated = telemetry.Counter("frontend/tickets_created", "tickets created")
|
||||
mTicketsDeleted = telemetry.Counter("frontend/tickets_deleted", "tickets deleted")
|
||||
mTicketsRetrieved = telemetry.Counter("frontend/tickets_retrieved", "tickets retrieved")
|
||||
mTicketAssignmentsRetrieved = telemetry.Counter("frontend/tickets_assignments_retrieved", "ticket assignments retrieved")
|
||||
)
|
||||
|
||||
// CreateTicket assigns an unique TicketId to the input Ticket and record it in state storage.
|
||||
@ -77,28 +73,157 @@ func doCreateTicket(ctx context.Context, req *pb.CreateTicketRequest, store stat
|
||||
|
||||
ticket.Id = xid.New().String()
|
||||
ticket.CreateTime = ptypes.TimestampNow()
|
||||
|
||||
sfCount := 0
|
||||
sfCount += len(ticket.GetSearchFields().GetDoubleArgs())
|
||||
sfCount += len(ticket.GetSearchFields().GetStringArgs())
|
||||
sfCount += len(ticket.GetSearchFields().GetTags())
|
||||
stats.Record(ctx, searchFieldsPerTicket.M(int64(sfCount)))
|
||||
stats.Record(ctx, totalBytesPerTicket.M(int64(proto.Size(ticket))))
|
||||
|
||||
err := store.CreateTicket(ctx, ticket)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"ticket": ticket,
|
||||
}).Error("failed to create the ticket")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = store.IndexTicket(ctx, ticket)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"ticket": ticket,
|
||||
}).Error("failed to index the ticket")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketsCreated)
|
||||
return ticket, nil
|
||||
}
|
||||
|
||||
// CreateBackfill creates a new Backfill object.
|
||||
// it assigns an unique Id to the input Backfill and record it in state storage.
|
||||
// Set initial LastAcknowledge time for this Backfill.
|
||||
// A Backfill is considered as ready for matchmaking once it is created.
|
||||
// - If SearchFields exist in a Backfill, CreateBackfill will also index these fields such that one can query the ticket with query.QueryBackfills function.
|
||||
func (s *frontendService) CreateBackfill(ctx context.Context, req *pb.CreateBackfillRequest) (*pb.Backfill, error) {
|
||||
// Perform input validation.
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Backfill == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, ".backfill is required")
|
||||
}
|
||||
if req.Backfill.CreateTime != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "backfills cannot be created with create time set")
|
||||
}
|
||||
|
||||
return doCreateBackfill(ctx, req, s.store)
|
||||
}
|
||||
|
||||
func doCreateBackfill(ctx context.Context, req *pb.CreateBackfillRequest, store statestore.Service) (*pb.Backfill, error) {
|
||||
// Generate an id and create a Backfill in state storage
|
||||
backfill, ok := proto.Clone(req.Backfill).(*pb.Backfill)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "failed to clone input ticket proto")
|
||||
}
|
||||
|
||||
backfill.Id = xid.New().String()
|
||||
backfill.CreateTime = ptypes.TimestampNow()
|
||||
backfill.Generation = 1
|
||||
|
||||
sfCount := 0
|
||||
sfCount += len(backfill.GetSearchFields().GetDoubleArgs())
|
||||
sfCount += len(backfill.GetSearchFields().GetStringArgs())
|
||||
sfCount += len(backfill.GetSearchFields().GetTags())
|
||||
stats.Record(ctx, searchFieldsPerBackfill.M(int64(sfCount)))
|
||||
stats.Record(ctx, totalBytesPerBackfill.M(int64(proto.Size(backfill))))
|
||||
|
||||
err := store.CreateBackfill(ctx, backfill, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = store.IndexBackfill(ctx, backfill)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return backfill, nil
|
||||
}
|
||||
|
||||
// UpdateBackfill updates a Backfill object, if present.
|
||||
// Update would increment generation in Redis.
|
||||
// Only Extensions and SearchFields would be updated.
|
||||
// CreateTime is not changed on Update
|
||||
func (s *frontendService) UpdateBackfill(ctx context.Context, req *pb.UpdateBackfillRequest) (*pb.Backfill, error) {
|
||||
if req == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "request is nil")
|
||||
}
|
||||
if req.Backfill == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, ".backfill is required")
|
||||
}
|
||||
|
||||
backfill, ok := proto.Clone(req.Backfill).(*pb.Backfill)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "failed to clone input backfill proto")
|
||||
}
|
||||
|
||||
bfID := backfill.Id
|
||||
if bfID == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "backfill ID should exist")
|
||||
}
|
||||
m := s.store.NewMutex(bfID)
|
||||
|
||||
err := m.Lock(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if _, err = m.Unlock(ctx); err != nil {
|
||||
logger.WithError(err).Error("error on mutex unlock")
|
||||
}
|
||||
}()
|
||||
bfStored, associatedTickets, err := s.store.GetBackfill(ctx, bfID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update generation here, because Frontend is used by GameServer only
|
||||
bfStored.SearchFields = backfill.SearchFields
|
||||
bfStored.Extensions = backfill.Extensions
|
||||
// Autoincrement generation, input backfill generation validation is performed
|
||||
// on Backend only (after MMF round)
|
||||
bfStored.Generation++
|
||||
err = s.store.UpdateBackfill(ctx, bfStored, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.store.DeleteTicketsFromPendingRelease(ctx, associatedTickets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.store.IndexBackfill(ctx, bfStored)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"id": bfStored.Id,
|
||||
}).Error("failed to index the backfill")
|
||||
return nil, err
|
||||
}
|
||||
return bfStored, nil
|
||||
}
|
||||
|
||||
// DeleteBackfill deletes a Backfill by its ID.
|
||||
func (s *frontendService) DeleteBackfill(ctx context.Context, req *pb.DeleteBackfillRequest) (*empty.Empty, error) {
|
||||
bfID := req.GetBackfillId()
|
||||
if bfID == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, ".BackfillId is required")
|
||||
}
|
||||
|
||||
err := s.store.DeleteBackfillCompletely(ctx, bfID)
|
||||
// Deleting of Backfill is inevitable when it is expired, so we don't worry about error here
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
}).Error("error on DeleteBackfill")
|
||||
}
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
// DeleteTicket immediately stops Open Match from using the Ticket for matchmaking and removes the Ticket from state storage.
|
||||
// The client must delete the Ticket when finished matchmaking with it.
|
||||
// - If SearchFields exist in a Ticket, DeleteTicket will deindex the fields lazily.
|
||||
@ -108,7 +233,6 @@ func (s *frontendService) DeleteTicket(ctx context.Context, req *pb.DeleteTicket
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketsDeleted)
|
||||
return &empty.Empty{}, nil
|
||||
}
|
||||
|
||||
@ -116,10 +240,6 @@ func doDeleteTicket(ctx context.Context, id string, store statestore.Service) er
|
||||
// Deindex this Ticket to remove it from matchmaking pool.
|
||||
err := store.DeindexTicket(ctx, id)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"id": id,
|
||||
}).Error("failed to deindex the ticket")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -135,12 +255,12 @@ func doDeleteTicket(ctx context.Context, id string, store statestore.Service) er
|
||||
"id": id,
|
||||
}).Error("failed to delete the ticket")
|
||||
}
|
||||
err = store.DeleteTicketsFromIgnoreList(ctx, []string{id})
|
||||
err = store.DeleteTicketsFromPendingRelease(ctx, []string{id})
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"id": id,
|
||||
}).Error("failed to delete the ticket from ignorelist")
|
||||
}).Error("failed to delete the ticket from pendingRelease")
|
||||
}
|
||||
// TODO: If other redis queues are implemented or we have custom index fields
|
||||
// created by Open Match, those need to be cleaned up here.
|
||||
@ -150,21 +270,7 @@ func doDeleteTicket(ctx context.Context, id string, store statestore.Service) er
|
||||
|
||||
// GetTicket get the Ticket associated with the specified TicketId.
|
||||
func (s *frontendService) GetTicket(ctx context.Context, req *pb.GetTicketRequest) (*pb.Ticket, error) {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketsRetrieved)
|
||||
return doGetTickets(ctx, req.GetTicketId(), s.store)
|
||||
}
|
||||
|
||||
func doGetTickets(ctx context.Context, id string, store statestore.Service) (*pb.Ticket, error) {
|
||||
ticket, err := store.GetTicket(ctx, id)
|
||||
if err != nil {
|
||||
logger.WithFields(logrus.Fields{
|
||||
"error": err.Error(),
|
||||
"id": id,
|
||||
}).Error("failed to get the ticket")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ticket, nil
|
||||
return s.store.GetTicket(ctx, req.GetTicketId())
|
||||
}
|
||||
|
||||
// WatchAssignments stream back Assignment of the specified TicketId if it is updated.
|
||||
@ -177,7 +283,6 @@ func (s *frontendService) WatchAssignments(req *pb.WatchAssignmentsRequest, stre
|
||||
return ctx.Err()
|
||||
default:
|
||||
sender := func(assignment *pb.Assignment) error {
|
||||
telemetry.RecordUnitMeasurement(ctx, mTicketAssignmentsRetrieved)
|
||||
return stream.Send(&pb.WatchAssignmentsResponse{Assignment: assignment})
|
||||
}
|
||||
return doWatchAssignments(ctx, req.GetTicketId(), sender, s.store)
|
||||
@ -189,6 +294,10 @@ func doWatchAssignments(ctx context.Context, id string, sender func(*pb.Assignme
|
||||
var currAssignment *pb.Assignment
|
||||
var ok bool
|
||||
callback := func(assignment *pb.Assignment) error {
|
||||
if ctx.Err() != nil {
|
||||
return status.Errorf(codes.Aborted, ctx.Err().Error())
|
||||
}
|
||||
|
||||
if (currAssignment == nil && assignment != nil) || !proto.Equal(currAssignment, assignment) {
|
||||
currAssignment, ok = proto.Clone(assignment).(*pb.Assignment)
|
||||
if !ok {
|
||||
@ -197,7 +306,6 @@ func doWatchAssignments(ctx context.Context, id string, sender func(*pb.Assignme
|
||||
|
||||
err := sender(currAssignment)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("failed to send Redis response to grpc server")
|
||||
return status.Errorf(codes.Aborted, err.Error())
|
||||
}
|
||||
}
|
||||
@ -206,3 +314,78 @@ func doWatchAssignments(ctx context.Context, id string, sender func(*pb.Assignme
|
||||
|
||||
return store.GetAssignments(ctx, id, callback)
|
||||
}
|
||||
|
||||
// AcknowledgeBackfill is used to notify OpenMatch about GameServer connection info.
|
||||
// This triggers an assignment process.
|
||||
func (s *frontendService) AcknowledgeBackfill(ctx context.Context, req *pb.AcknowledgeBackfillRequest) (*pb.AcknowledgeBackfillResponse, error) {
|
||||
if req.GetBackfillId() == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, ".BackfillId is required")
|
||||
}
|
||||
if req.GetAssignment() == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, ".Assignment is required")
|
||||
}
|
||||
|
||||
m := s.store.NewMutex(req.GetBackfillId())
|
||||
|
||||
err := m.Lock(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if _, err = m.Unlock(ctx); err != nil {
|
||||
logger.WithError(err).Error("error on mutex unlock")
|
||||
}
|
||||
}()
|
||||
|
||||
bf, associatedTickets, err := s.store.GetBackfill(ctx, req.GetBackfillId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.store.UpdateAcknowledgmentTimestamp(ctx, req.GetBackfillId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &pb.AcknowledgeBackfillResponse{
|
||||
Backfill: bf,
|
||||
Tickets: make([]*pb.Ticket, 0),
|
||||
}
|
||||
|
||||
if len(associatedTickets) != 0 {
|
||||
setResp, tickets, err := s.store.UpdateAssignments(ctx, &pb.AssignTicketsRequest{
|
||||
Assignments: []*pb.AssignmentGroup{{TicketIds: associatedTickets, Assignment: req.GetAssignment()}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Tickets = tickets
|
||||
|
||||
// log errors returned from UpdateAssignments to track tickets with NotFound errors
|
||||
for _, f := range setResp.Failures {
|
||||
logger.Errorf("failed to assign ticket %s, cause %d", f.TicketId, f.Cause)
|
||||
}
|
||||
for _, id := range associatedTickets {
|
||||
err = s.store.DeindexTicket(ctx, id)
|
||||
// Try to deindex all input tickets. Log without returning an error if the deindexing operation failed.
|
||||
if err != nil {
|
||||
logger.WithError(err).Errorf("failed to deindex ticket %s after updating the assignments", id)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all tickets associated with backfill, because unassigned tickets are not found only
|
||||
err = s.store.UpdateBackfill(ctx, bf, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetBackfill fetches a Backfill object by its ID.
|
||||
func (s *frontendService) GetBackfill(ctx context.Context, req *pb.GetBackfillRequest) (*pb.Backfill, error) {
|
||||
bf, _, err := s.store.GetBackfill(ctx, req.GetBackfillId())
|
||||
return bf, err
|
||||
}
|
||||
|
@ -22,8 +22,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
@ -77,17 +78,194 @@ func TestDoCreateTickets(t *testing.T) {
|
||||
test.preAction(cancel)
|
||||
|
||||
res, err := doCreateTicket(ctx, &pb.CreateTicketRequest{Ticket: test.ticket}, store)
|
||||
assert.Equal(t, test.wantCode, status.Convert(err).Code())
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
if err == nil {
|
||||
matched, err := regexp.MatchString(`[0-9a-v]{20}`, res.GetId())
|
||||
assert.True(t, matched)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.ticket.SearchFields.DoubleArgs["test-arg"], res.SearchFields.DoubleArgs["test-arg"])
|
||||
require.True(t, matched)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.ticket.SearchFields.DoubleArgs["test-arg"], res.SearchFields.DoubleArgs["test-arg"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBackfill(t *testing.T) {
|
||||
cfg := viper.New()
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
defer closer()
|
||||
ctx := utilTesting.NewContext(t)
|
||||
fs := frontendService{cfg, store}
|
||||
var testCases = []struct {
|
||||
description string
|
||||
request *pb.CreateBackfillRequest
|
||||
result *pb.Backfill
|
||||
expectedCode codes.Code
|
||||
expectedMessage string
|
||||
}{
|
||||
{
|
||||
description: "nil request check",
|
||||
request: nil,
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: "request is nil",
|
||||
},
|
||||
{
|
||||
description: "nil backfill - error is returned",
|
||||
request: &pb.CreateBackfillRequest{Backfill: nil},
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: ".backfill is required",
|
||||
},
|
||||
{
|
||||
description: "createTime should not exist in input",
|
||||
request: &pb.CreateBackfillRequest{Backfill: &pb.Backfill{CreateTime: ptypes.TimestampNow()}},
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: "backfills cannot be created with create time set",
|
||||
},
|
||||
{
|
||||
description: "empty Backfill, no errors",
|
||||
request: &pb.CreateBackfillRequest{Backfill: &pb.Backfill{}},
|
||||
expectedCode: codes.OK,
|
||||
expectedMessage: "",
|
||||
},
|
||||
{
|
||||
description: "normal backfill",
|
||||
request: &pb.CreateBackfillRequest{
|
||||
Backfill: &pb.Backfill{
|
||||
SearchFields: &pb.SearchFields{
|
||||
StringArgs: map[string]string{
|
||||
"search": "me",
|
||||
}}}},
|
||||
expectedCode: codes.OK,
|
||||
expectedMessage: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
res, err := fs.CreateBackfill(ctx, tc.request)
|
||||
if tc.expectedCode == codes.OK {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tc.expectedCode.String(), status.Convert(err).Code().String())
|
||||
require.Contains(t, status.Convert(err).Message(), tc.expectedMessage)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// expect error with canceled context
|
||||
store, closer = statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
defer closer()
|
||||
fs = frontendService{cfg, store}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
res, err := fs.CreateBackfill(ctx, &pb.CreateBackfillRequest{Backfill: &pb.Backfill{
|
||||
SearchFields: &pb.SearchFields{
|
||||
DoubleArgs: map[string]float64{
|
||||
"test-arg": 1,
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, codes.Unavailable.String(), status.Convert(err).Code().String())
|
||||
require.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestUpdateBackfill(t *testing.T) {
|
||||
cfg := viper.New()
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
defer closer()
|
||||
ctx := utilTesting.NewContext(t)
|
||||
fs := frontendService{cfg, store}
|
||||
res, err := fs.CreateBackfill(ctx, &pb.CreateBackfillRequest{
|
||||
Backfill: &pb.Backfill{
|
||||
SearchFields: &pb.SearchFields{
|
||||
StringArgs: map[string]string{
|
||||
"search": "me",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
var testCases = []struct {
|
||||
description string
|
||||
request *pb.UpdateBackfillRequest
|
||||
result *pb.Backfill
|
||||
expectedCode codes.Code
|
||||
expectedMessage string
|
||||
}{
|
||||
{
|
||||
description: "nil request check",
|
||||
request: nil,
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: "request is nil",
|
||||
},
|
||||
{
|
||||
description: "nil backfill - error is returned",
|
||||
request: &pb.UpdateBackfillRequest{Backfill: nil},
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: ".backfill is required",
|
||||
},
|
||||
{
|
||||
description: "empty Backfill, error with no backfill ID",
|
||||
request: &pb.UpdateBackfillRequest{Backfill: &pb.Backfill{}},
|
||||
expectedCode: codes.InvalidArgument,
|
||||
expectedMessage: "backfill ID should exist",
|
||||
},
|
||||
{
|
||||
description: "normal backfill",
|
||||
request: &pb.UpdateBackfillRequest{
|
||||
Backfill: &pb.Backfill{
|
||||
Id: res.Id,
|
||||
SearchFields: &pb.SearchFields{
|
||||
StringArgs: map[string]string{
|
||||
"search": "me",
|
||||
}}}},
|
||||
expectedCode: codes.OK,
|
||||
expectedMessage: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
res, err = fs.UpdateBackfill(ctx, tc.request)
|
||||
if tc.expectedCode == codes.OK {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
require.Equal(t, tc.request.Backfill.SearchFields.DoubleArgs, res.SearchFields.DoubleArgs)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tc.expectedCode.String(), status.Convert(err).Code().String())
|
||||
require.Contains(t, status.Convert(err).Message(), tc.expectedMessage)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// expect error with canceled context
|
||||
store, closer = statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
fs = frontendService{cfg, store}
|
||||
defer closer()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
res, err = fs.UpdateBackfill(ctx, &pb.UpdateBackfillRequest{Backfill: &pb.Backfill{
|
||||
Id: res.Id,
|
||||
SearchFields: &pb.SearchFields{
|
||||
DoubleArgs: map[string]float64{
|
||||
"test-arg": 1,
|
||||
},
|
||||
},
|
||||
}})
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, codes.Unknown.String(), status.Convert(err).Code().String())
|
||||
require.Nil(t, res)
|
||||
}
|
||||
|
||||
func TestDoWatchAssignments(t *testing.T) {
|
||||
testTicket := &pb.Ticket{
|
||||
Id: "test-id",
|
||||
@ -118,12 +296,12 @@ func TestDoWatchAssignments(t *testing.T) {
|
||||
{
|
||||
description: "expect two assignment reads from preAction writes and fail in grpc aborted code",
|
||||
preAction: func(ctx context.Context, t *testing.T, store statestore.Service, wantAssignments []*pb.Assignment, wg *sync.WaitGroup) {
|
||||
assert.Nil(t, store.CreateTicket(ctx, testTicket))
|
||||
require.Nil(t, store.CreateTicket(ctx, testTicket))
|
||||
|
||||
go func(wg *sync.WaitGroup) {
|
||||
for i := 0; i < len(wantAssignments); i++ {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
_, err := store.UpdateAssignments(ctx, &pb.AssignTicketsRequest{
|
||||
_, _, err := store.UpdateAssignments(ctx, &pb.AssignTicketsRequest{
|
||||
Assignments: []*pb.AssignmentGroup{
|
||||
{
|
||||
TicketIds: []string{testTicket.GetId()},
|
||||
@ -131,7 +309,7 @@ func TestDoWatchAssignments(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
wg.Done()
|
||||
}
|
||||
}(wg)
|
||||
@ -155,16 +333,99 @@ func TestDoWatchAssignments(t *testing.T) {
|
||||
|
||||
test.preAction(ctx, t, store, test.wantAssignments, &wg)
|
||||
err := doWatchAssignments(ctx, testTicket.GetId(), senderGenerator(gotAssignments, len(test.wantAssignments)), store)
|
||||
assert.Equal(t, test.wantCode, status.Convert(err).Code())
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
|
||||
wg.Wait()
|
||||
for i := 0; i < len(gotAssignments); i++ {
|
||||
assert.Equal(t, gotAssignments[i], test.wantAssignments[i])
|
||||
require.Equal(t, gotAssignments[i], test.wantAssignments[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAcknowledgeBackfillValidation - test input validation only
|
||||
func TestAcknowledgeBackfillValidation(t *testing.T) {
|
||||
cfg := viper.New()
|
||||
tests := []struct {
|
||||
description string
|
||||
request *pb.AcknowledgeBackfillRequest
|
||||
expectedMessage string
|
||||
}{
|
||||
{
|
||||
description: "no BackfillId, error is expected",
|
||||
request: &pb.AcknowledgeBackfillRequest{BackfillId: "", Assignment: &pb.Assignment{Connection: "10.0.0.1"}},
|
||||
expectedMessage: ".BackfillId is required",
|
||||
},
|
||||
{
|
||||
description: "no Assignment, error is expected",
|
||||
request: &pb.AcknowledgeBackfillRequest{BackfillId: "1234", Assignment: nil},
|
||||
expectedMessage: ".Assignment is required",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
defer closer()
|
||||
fs := frontendService{cfg, store}
|
||||
bf, err := fs.AcknowledgeBackfill(ctx, test.request)
|
||||
require.Equal(t, codes.InvalidArgument.String(), status.Convert(err).Code().String())
|
||||
require.Equal(t, test.expectedMessage, status.Convert(err).Message())
|
||||
require.Nil(t, bf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAcknowledgeBackfill verifies timestamp part of AcknowledgeBackfill call,
|
||||
// assignment part tested in a corresponding E2E test.
|
||||
// Expired backfill can not be acknowledged
|
||||
func TestAcknowledgeBackfill(t *testing.T) {
|
||||
cfg := viper.New()
|
||||
ctx := context.Background()
|
||||
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, cfg)
|
||||
defer closer()
|
||||
|
||||
fakeBackfill := &pb.Backfill{
|
||||
Id: "1",
|
||||
SearchFields: &pb.SearchFields{
|
||||
DoubleArgs: map[string]float64{
|
||||
"test-arg": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := store.CreateBackfill(ctx, fakeBackfill, []string{})
|
||||
require.NoError(t, err)
|
||||
fs := frontendService{cfg, store}
|
||||
|
||||
resp, err := fs.AcknowledgeBackfill(ctx, &pb.AcknowledgeBackfillRequest{BackfillId: fakeBackfill.Id, Assignment: &pb.Assignment{Connection: "10.0.0.1"}})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.NotNil(t, resp.Backfill)
|
||||
require.NotNil(t, resp.Tickets)
|
||||
|
||||
// Use wrong BackfillID, error is returned
|
||||
resp, err = fs.AcknowledgeBackfill(ctx, &pb.AcknowledgeBackfillRequest{BackfillId: "42", Assignment: &pb.Assignment{Connection: "10.0.0.1"}})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, resp)
|
||||
require.Equal(t, "Backfill id: 42 not found", status.Convert(err).Message())
|
||||
|
||||
time.Sleep(cfg.GetDuration("pendingReleaseTimeout"))
|
||||
ids, err := store.GetExpiredBackfillIDs(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ids, 1)
|
||||
|
||||
resp, err = fs.AcknowledgeBackfill(ctx, &pb.AcknowledgeBackfillRequest{BackfillId: fakeBackfill.Id, Assignment: &pb.Assignment{Connection: "10.0.0.1"}})
|
||||
require.Nil(t, resp)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, codes.Unavailable.String(), status.Convert(err).Code().String())
|
||||
require.Contains(t, status.Convert(err).Message(), "can not acknowledge an expired backfill, id: 1")
|
||||
|
||||
}
|
||||
|
||||
func TestDoDeleteTicket(t *testing.T) {
|
||||
fakeTicket := &pb.Ticket{
|
||||
Id: "1",
|
||||
@ -211,7 +472,7 @@ func TestDoDeleteTicket(t *testing.T) {
|
||||
test.preAction(ctx, cancel, store)
|
||||
|
||||
err := doDeleteTicket(ctx, fakeTicket.GetId(), store)
|
||||
assert.Equal(t, test.wantCode, status.Convert(err).Code())
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -264,13 +525,119 @@ func TestDoGetTicket(t *testing.T) {
|
||||
|
||||
test.preAction(ctx, cancel, store)
|
||||
|
||||
ticket, err := doGetTickets(ctx, fakeTicket.GetId(), store)
|
||||
assert.Equal(t, test.wantCode, status.Convert(err).Code())
|
||||
ticket, err := store.GetTicket(ctx, fakeTicket.GetId())
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
|
||||
if err == nil {
|
||||
assert.Equal(t, test.wantTicket.GetId(), ticket.GetId())
|
||||
assert.Equal(t, test.wantTicket.SearchFields.DoubleArgs, ticket.SearchFields.DoubleArgs)
|
||||
require.Equal(t, test.wantTicket.GetId(), ticket.GetId())
|
||||
require.Equal(t, test.wantTicket.SearchFields.DoubleArgs, ticket.SearchFields.DoubleArgs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBackfill(t *testing.T) {
|
||||
fakeBackfill := &pb.Backfill{
|
||||
Id: "1",
|
||||
SearchFields: &pb.SearchFields{
|
||||
DoubleArgs: map[string]float64{
|
||||
"test-arg": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg := viper.New()
|
||||
tests := []struct {
|
||||
description string
|
||||
preAction func(context.Context, context.CancelFunc, statestore.Service)
|
||||
wantTicket *pb.Backfill
|
||||
wantCode codes.Code
|
||||
}{
|
||||
{
|
||||
description: "expect unavailable code since context is canceled before being called",
|
||||
preAction: func(_ context.Context, cancel context.CancelFunc, _ statestore.Service) {
|
||||
cancel()
|
||||
},
|
||||
wantCode: codes.Unavailable,
|
||||
},
|
||||
{
|
||||
description: "expect not found code since ticket does not exist",
|
||||
preAction: func(_ context.Context, _ context.CancelFunc, _ statestore.Service) {},
|
||||
wantCode: codes.NotFound,
|
||||
},
|
||||
{
|
||||
description: "expect ok code with output ticket equivalent to fakeBackfill",
|
||||
preAction: func(ctx context.Context, _ context.CancelFunc, store statestore.Service) {
|
||||
store.CreateBackfill(ctx, fakeBackfill, []string{})
|
||||
},
|
||||
wantCode: codes.OK,
|
||||
wantTicket: fakeBackfill,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(utilTesting.NewContext(t))
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, viper.New())
|
||||
defer closer()
|
||||
fs := frontendService{cfg, store}
|
||||
|
||||
test.preAction(ctx, cancel, store)
|
||||
|
||||
backfill, err := fs.GetBackfill(ctx, &pb.GetBackfillRequest{BackfillId: fakeBackfill.GetId()})
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
|
||||
if err == nil {
|
||||
require.Equal(t, test.wantTicket.GetId(), backfill.GetId())
|
||||
require.Equal(t, test.wantTicket.SearchFields.DoubleArgs, backfill.SearchFields.DoubleArgs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoDeleteBackfill(t *testing.T) {
|
||||
fakeBackfill := &pb.Backfill{
|
||||
Id: "1",
|
||||
SearchFields: &pb.SearchFields{
|
||||
DoubleArgs: map[string]float64{
|
||||
"test-arg": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
store, closer := statestoreTesting.NewStoreServiceForTesting(t, viper.New())
|
||||
defer closer()
|
||||
ctx := context.Background()
|
||||
|
||||
err := store.CreateBackfill(ctx, fakeBackfill, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := viper.New()
|
||||
fs := frontendService{cfg, store}
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
id string
|
||||
wantCode codes.Code
|
||||
}{
|
||||
{
|
||||
description: "expect ok code since delete backfill does not care about if backfill exists or not",
|
||||
id: "222",
|
||||
wantCode: codes.OK,
|
||||
},
|
||||
{
|
||||
description: "expect ok code",
|
||||
id: "1",
|
||||
wantCode: codes.OK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
_, err := fs.DeleteBackfill(ctx, &pb.DeleteBackfillRequest{BackfillId: fakeBackfill.GetId()})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.wantCode.String(), status.Convert(err).Code().String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -19,25 +19,24 @@ import (
|
||||
"open-match.dev/open-match/internal/app/frontend"
|
||||
"open-match.dev/open-match/internal/app/query"
|
||||
"open-match.dev/open-match/internal/app/synchronizer"
|
||||
"open-match.dev/open-match/internal/config"
|
||||
"open-match.dev/open-match/internal/rpc"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
)
|
||||
|
||||
// BindService creates the minimatch service to the server Params.
|
||||
func BindService(p *rpc.ServerParams, cfg config.View) error {
|
||||
if err := backend.BindService(p, cfg); err != nil {
|
||||
func BindService(p *appmain.Params, b *appmain.Bindings) error {
|
||||
if err := backend.BindService(p, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := frontend.BindService(p, cfg); err != nil {
|
||||
if err := frontend.BindService(p, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := query.BindService(p, cfg); err != nil {
|
||||
if err := query.BindService(p, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := synchronizer.BindService(p, cfg); err != nil {
|
||||
if err := synchronizer.BindService(p, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
250
internal/app/query/cache.go
Normal file
250
internal/app/query/cache.go
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright 2020 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 query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"open-match.dev/open-match/internal/appmain"
|
||||
"open-match.dev/open-match/internal/statestore"
|
||||
"open-match.dev/open-match/pkg/pb"
|
||||
)
|
||||
|
||||
// cache unifies concurrent requests into a single cache update, and
|
||||
// gives a safe view into that map cache.
|
||||
type cache struct {
|
||||
store statestore.Service
|
||||
requests chan *cacheRequest
|
||||
// Single item buffered channel. Holds a value when runQuery can be safely
|
||||
// started. Basically a channel/select friendly mutex around runQuery
|
||||
// running.
|
||||
startRunRequest chan struct{}
|
||||
wg sync.WaitGroup
|
||||
// Multithreaded unsafe fields, only to be written by update, and read when
|
||||
// request given the ok.
|
||||
value interface{}
|
||||
update func(statestore.Service, interface{}) error
|
||||
err error
|
||||
}
|
||||
|
||||
type cacheRequest struct {
|
||||
ctx context.Context
|
||||
runNow chan struct{}
|
||||
}
|
||||
|
||||
func (c *cache) request(ctx context.Context, f func(interface{})) error {
|
||||
cr := &cacheRequest{
|
||||
ctx: ctx,
|
||||
runNow: make(chan struct{}),
|
||||
}
|
||||
|
||||
sendRequest:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err(), "cache request canceled before request sent.")
|
||||
case <-c.startRunRequest:
|
||||
go c.runRequest()
|
||||
case c.requests <- cr:
|
||||
break sendRequest
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err(), "cache request canceled waiting for access.")
|
||||
case <-cr.runNow:
|
||||
defer c.wg.Done()
|
||||
}
|
||||
|
||||
if c.err != nil {
|
||||
return c.err
|
||||
}
|
||||
|
||||
f(c.value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cache) runRequest() {
|
||||
defer func() {
|
||||
c.startRunRequest <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait for first query request.
|
||||
reqs := []*cacheRequest{<-c.requests}
|
||||
|
||||
// Collect all waiting queries.
|
||||
collectAllWaiting:
|
||||
for {
|
||||
select {
|
||||
case req := <-c.requests:
|
||||
reqs = append(reqs, req)
|
||||
default:
|
||||
break collectAllWaiting
|
||||
}
|
||||
}
|
||||
|
||||
c.err = c.update(c.store, c.value)
|
||||
stats.Record(context.Background(), cacheWaitingQueries.M(int64(len(reqs))))
|
||||
|
||||
// Send WaitGroup to query calls, letting them run their query on the cache.
|
||||
for _, req := range reqs {
|
||||
c.wg.Add(1)
|
||||
select {
|
||||
case req.runNow <- struct{}{}:
|
||||
case <-req.ctx.Done():
|
||||
c.wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// wait for requests to finish using cache.
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
func newTicketCache(b *appmain.Bindings, store statestore.Service) *cache {
|
||||
c := &cache{
|
||||
store: store,
|
||||
requests: make(chan *cacheRequest),
|
||||
startRunRequest: make(chan struct{}, 1),
|
||||
value: make(map[string]*pb.Ticket),
|
||||
update: updateTicketCache,
|
||||
}
|
||||
|
||||
c.startRunRequest <- struct{}{}
|
||||
b.AddHealthCheckFunc(c.store.HealthCheck)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func updateTicketCache(store statestore.Service, value interface{}) error {
|
||||
if value == nil {
|
||||
return status.Error(codes.InvalidArgument, "value is required")
|
||||
}
|
||||
|
||||
tickets, ok := value.(map[string]*pb.Ticket)
|
||||
if !ok {
|
||||
return status.Errorf(codes.InvalidArgument, "expecting value type map[string]*pb.Ticket, but got: %T", value)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
previousCount := len(tickets)
|
||||
currentAll, err := store.GetIndexedIDSet(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletedCount := 0
|
||||
for id := range tickets {
|
||||
if _, ok := currentAll[id]; !ok {
|
||||
delete(tickets, id)
|
||||
deletedCount++
|
||||
}
|
||||
}
|
||||
|
||||
toFetch := []string{}
|
||||
for id := range currentAll {
|
||||
if _, ok := tickets[id]; !ok {
|
||||
toFetch = append(toFetch, id)
|
||||
}
|
||||
}
|
||||
|
||||
newTickets, err := store.GetTickets(context.Background(), toFetch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, t := range newTickets {
|
||||
tickets[t.Id] = t
|
||||
}
|
||||
|
||||
stats.Record(context.Background(), cacheTotalItems.M(int64(previousCount)))
|
||||
stats.Record(context.Background(), cacheFetchedItems.M(int64(len(toFetch))))
|
||||
stats.Record(context.Background(), cacheUpdateLatency.M(float64(time.Since(t))/float64(time.Millisecond)))
|
||||
|
||||
logger.Debugf("Ticket Cache update: Previous %d, Deleted %d, Fetched %d, Current %d", previousCount, deletedCount, len(toFetch), len(tickets))
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBackfillCache(b *appmain.Bindings, store statestore.Service) *cache {
|
||||
c := &cache{
|
||||
store: store,
|
||||
requests: make(chan *cacheRequest),
|
||||
startRunRequest: make(chan struct{}, 1),
|
||||
value: make(map[string]*pb.Backfill),
|
||||
update: updateBackfillCache,
|
||||
}
|
||||
|
||||
c.startRunRequest <- struct{}{}
|
||||
b.AddHealthCheckFunc(c.store.HealthCheck)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func updateBackfillCache(store statestore.Service, value interface{}) error {
|
||||
if value == nil {
|
||||
return status.Error(codes.InvalidArgument, "value is required")
|
||||
}
|
||||
|
||||
backfills, ok := value.(map[string]*pb.Backfill)
|
||||
if !ok {
|
||||
return status.Errorf(codes.InvalidArgument, "expecting value type map[string]*pb.Backfill, but got: %T", value)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
previousCount := len(backfills)
|
||||
index, err := store.GetIndexedBackfills(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletedCount := 0
|
||||
for id, backfill := range backfills {
|
||||
generation, ok := index[id]
|
||||
if !ok || backfill.Generation < int64(generation) {
|
||||
delete(backfills, id)
|
||||
deletedCount++
|
||||
}
|
||||
}
|
||||
|
||||
toFetch := []string{}
|
||||
for id := range index {
|
||||
if _, ok := backfills[id]; !ok {
|
||||
toFetch = append(toFetch, id)
|
||||
}
|
||||
}
|
||||
|
||||
fetchedBackfills, err := store.GetBackfills(context.Background(), toFetch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, b := range fetchedBackfills {
|
||||
backfills[b.Id] = b
|
||||
}
|
||||
|
||||
stats.Record(context.Background(), cacheTotalItems.M(int64(previousCount)))
|
||||
stats.Record(context.Background(), cacheFetchedItems.M(int64(len(toFetch))))
|
||||
stats.Record(context.Background(), cacheUpdateLatency.M(float64(time.Since(t))/float64(time.Millisecond)))
|
||||
|
||||
logger.Debugf("Backfill Cache update: Previous %d, Deleted %d, Fetched %d, Current %d", previousCount, deletedCount, len(toFetch), len(backfills))
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user