Skip to content

Commit

Permalink
Docs: High Density Integration Pattern
Browse files Browse the repository at this point in the history
Documentation explaining how to use the new advanced allocation features
to run multiple concurrent game sessions in a single `GameServer`
instance.

Closes googleforgames#1239
  • Loading branch information
markmandel committed Sep 23, 2021
1 parent 13729fd commit 6bf0512
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions build/includes/website.mk
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ site-images: $(site_path)/static/diagrams/gameserver-reserved.puml.png
site-images: $(site_path)/static/diagrams/canary-testing.puml.png
site-images: $(site_path)/static/diagrams/allocation-player-capacity.puml.png
site-images: $(site_path)/static/diagrams/reusing-gameservers.puml.png
site-images: $(site_path)/static/diagrams/high-density.puml.png

# generate pngs from dot files
%.dot.png: %.dot
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: "High Density GameServers"
linkTitle: "High Density GameServers"
date: 2021-08-31
weight: 70
description: >
How to run multiple concurrent game sessions in a single GameServer process.
---

{{< alpha title="Allocation Player Filter and Allocation State Filter" gate="PlayerAllocationFilter,StateAllocationFilter" >}}

Depending on the setup and resource requirements of your game server process, sometimes it can be a more economical
use of resources to run multiple concurrent game sessions from within a single `GameServer` instance.

The tradeoff here is that this requires more management on behalf of the integrated game server process and external
systems, since it works around the common Kubernetes and/or Agones container lifecycle.

Utilising the new allocation `gameServerState` filter as well as the existing ability to edit the
`GameServer` Labels at both [allocation time]({{% ref "/docs/Reference/gameserverallocation.md" %}}), and from
within the game server process, [via the SDK]({{% ref "/docs/Guides/Client SDKs/_index.md#setlabelkey-value" %}}),
means Agones is able to atomically remove a `GameServer` from the list of potentially allocatable
`GameServers` at allocation time, and then return it back into the pool of allocatable `GameServers` if and when the
game server process deems that is has room to host another game session.

<a href="../../../diagrams/high-density.puml.png" target="_blank">
<img src="../../../diagrams/high-density.puml.png" alt="High Density Allocation Diagram" />
</a>

{{< alert title="Info" color="info">}}
To watch for Allocation events, there is the initial `GameServer.status.state` change from `Ready` to `Allocated`,
but it is also useful to know that the value of `GameServer.metadata.annotations["agones.dev/last-allocated"]` will
change with each allocation, regardless of if there is a state change or not.
{{< /alert >}}

## Example `GameServerAllocation`

The below `Allocation` will first attempt to find a `GameServer` from the `Fleet` `simple-udp` that is already
Allocated and also has the label `agones.dev/sdk-available` with the value of `true`.

The above condition indicates that the matching game server process behind the matched `GameServer` record is able to
accept another game session at this time.

If the above Allocated `GameServer` does not exist, then allocated a Ready `GameServer` from the `simple-udp` `Fleet`.

Whichever condition is met, once allocation is made against a `GameServer`, its label of `agones.dev/sdk-available`
will be set to the value of `false`, thereby removing it from any future allocations with the below schema.

It will then be up to the game server process to decide on if and when it is appropriate to set the
`agones.dev/sdk-available` value back to `true`, thereby indicating that it can accept another concurrent
gameplay session.

```yaml
apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
selectors:
- matchLabels:
agones.dev/fleet: simple-udp
agones.dev/sdk-available: "true" # this is important
gameServerState: Allocated # new state filter: allocate from Allocated servers
- matchLabels:
agones.dev/fleet: simple-udp
gameServerState: Ready # Allocate out of the Ready Pool (which would be default, so backward compatible)
metadata:
labels:
agones.dev/sdk-available: "false" # this removes it from the pool
```
{{< alert title="Info" color="info">}}
It's important to note that the labels that the `GameServer` process use to add itself back into the pool of
allocatable instances, must start with the prefix `agones.dev/sdk-`, since only labels that have this prefix are
available to be [updated from the SDK]({{% ref "/docs/Guides/Client SDKs/_index.md#setlabelkey-value" %}}).
{{< /alert >}}

## Next Steps

* View the details about [using the SDK]({{% ref "/docs/Guides/Client SDKs/_index.md#setlabelkey-value" %}}) to set
labels on the `GameServer`.
* Check all the options available on [`GameServerAllocation`]({{% ref "/docs/Reference/gameserverallocation.md" %}}).
108 changes: 108 additions & 0 deletions site/static/diagrams/high-density.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
@startuml
participant Matchmaker
participant Agones
participant "Game Server\nProcess" as Binary
participant SDK
participant "GameServer\nResource" as GameServer
box "Game Server Pod"
participant Binary
participant SDK
end box

== GameServer Start ==

Agones -> GameServer: GameServer created through\na <i>Fleet</i> configuration
activate GameServer
GameServer -> Binary: Agones creates a Pod with the\nconfigured Game Server Container
activate Binary
activate SDK
Binary -> SDK: SDK.WatchGameServer()
note right
Use the SDK Watch function
to watch and react to allocation
events
end note

Binary -> SDK: SDK.Ready()
note right
Call <i>Ready()</i> when the
Game Server can take player
connections and is able to
be allocated.
end note
GameServer <-- SDK: Update to <i>Ready</i> State

== No allocated <i>GameServers</i> ==

Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
The <i>GameServerAllocation</i> is implemented to
optionally select an already allocated <i>GameServer</i>
with <i>metadata["agones.dev/sdk-available"] = "true"</i>
if one exists. At this stage, one does not, so
Agones will allocate a <i>Ready</i> <i>GameServer</i>.
end note
Agones -> GameServer: Finds a <i>Ready</i> <i>GameServer</i>,\nsets it to <i>Allocated</i> State\nand <i>metadata["agones.dev/sdk-available"] = "false"</i>
note left
By setting the label "agones.dev/last-allocated" to "false"
this remove the `GameServer` from possibly being
re-allocated until it knows it can handle
another session.
end note
Matchmaker <-- Agones : <i>GameServerAllocation</i> is returned\nwith <i>GameServer</i> details\nincluding IP and port to connect to.

SDK --> Binary: Sends SDK.WatchGameServer()\nevent for Allocation.
Binary -> SDK: SDK.SetLabel("available", "true")
note right
Since this game process can handle <i>n</i>
concurrent sessions, set this label to match
optional allocation label selectors, so it can be
re-allocated.
(See example below for more details)
end note
SDK --> GameServer: Sets <i>metadata["agones.dev/sdk-available"] = "true"</i>

== Allocated <i>GameServers</i> with room for more sessions ==

Matchmaker -> Agones: Create: <i>GameServerAllocation</i>
note left
The <i>GameServerAllocation</i> will this time
find the Allocated <i>GameServer</i> with the label
<i>metadata["agones.dev/sdk-available"] = "true"</i>,
indicating that it can accept more
concurrent game sessions.
end note
Agones -> GameServer: Finds the Allocated <i>GameServer</i>\nwith <i>metadata["agones.dev/sdk-available"] = "true"</i>\n and set <i>metadata["agones.dev/sdk-available"] = "false"</i>.
note right
This is the a <i>GameServer</i> that has room
for another concurrent game session.
end note
Matchmaker <-- Agones: returns <i>Allocated GameServer</i> record

SDK --> Binary: Sends SDK.WatchGameServer()\nevent for Allocation.

alt <i>GameServer</i> can accept more concurrent sessions
Binary -> SDK: SDK.SetLabel("available", "true")
SDK --> GameServer: Sets <i>metadata["agones.dev/sdk-available"] = "true"</i>.
end alt
note right
If the <i>GameServer</i> can accept
more concurrent sessions, reset the
<i>"agones.dev/sdk-available"</i>
back to "true"
end note

== <i>GameServer</i> has completed <i>n</i> number of complete sessions ==

Binary -> SDK: SDK.Shutdown()
note left
The <i>GameServer</i> process tracks the number of sessions
that it hosts, and after <i>n</i> number, calls <i>Shutdown()</i>
to delete the <i>GameServer</i> resource and backing Pod.
end note
SDK --> GameServer: Update to <i>Shutdown</i> state.
Agones -> GameServer: Deletes GameServer resource\n and backing Pod.
destroy Binary
destroy SDK
destroy GameServer
@enduml
Binary file added site/static/diagrams/high-density.puml.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6bf0512

Please sign in to comment.