implement alternative match-making approach with Lua scripts in Redis
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
development:
|
||||
adapter: async
|
||||
|
||||
test:
|
||||
adapter: test
|
||||
|
||||
production:
|
||||
adapter: redis
|
||||
url: <%= ENV.fetch("REDIS_URL", "redis://localhost:6380/0") %>
|
||||
channel_prefix: ruby_sea_battle_production
|
||||
@@ -10,15 +10,25 @@ default: &default
|
||||
timeout: 5000
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
database: storage/development.sqlite3
|
||||
primary:
|
||||
<<: *default
|
||||
database: storage/development.sqlite3
|
||||
queue:
|
||||
<<: *default
|
||||
database: storage/development_queue.sqlite3
|
||||
migrations_paths: db/queue_migrate
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
<<: *default
|
||||
database: storage/test.sqlite3
|
||||
primary:
|
||||
<<: *default
|
||||
database: storage/test.sqlite3
|
||||
queue:
|
||||
<<: *default
|
||||
database: storage/test_queue.sqlite3
|
||||
migrations_paths: db/queue_migrate
|
||||
|
||||
# PostgreSQL for production.
|
||||
#
|
||||
@@ -29,11 +39,22 @@ test:
|
||||
# DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT
|
||||
#
|
||||
production:
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||
database: <%= ENV["DB_NAME"] %>
|
||||
username: <%= ENV["DB_USER"] %>
|
||||
password: <%= ENV["DB_PASSWORD"] %>
|
||||
host: <%= ENV["DB_HOST"] %>
|
||||
port: <%= ENV["DB_PORT"] || 5432 %>
|
||||
primary:
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||
database: <%= ENV["DB_NAME"] %>
|
||||
username: <%= ENV["DB_USER"] %>
|
||||
password: <%= ENV["DB_PASSWORD"] %>
|
||||
host: <%= ENV["DB_HOST"] %>
|
||||
port: <%= ENV["DB_PORT"] || 5432 %>
|
||||
queue:
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
||||
database: <%= ENV["QUEUE_DB_NAME"] || ENV["DB_NAME"] %>
|
||||
username: <%= ENV["QUEUE_DB_USER"] || ENV["DB_USER"] %>
|
||||
password: <%= ENV["QUEUE_DB_PASSWORD"] || ENV["DB_PASSWORD"] %>
|
||||
host: <%= ENV["QUEUE_DB_HOST"] || ENV["DB_HOST"] %>
|
||||
port: <%= ENV["QUEUE_DB_PORT"] || ENV["DB_PORT"] || 5432 %>
|
||||
migrations_paths: db/queue_migrate
|
||||
|
||||
@@ -3,6 +3,11 @@ require "active_support/core_ext/integer/time"
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# Run background jobs (e.g. the matchmaking sweeper) through Solid Queue,
|
||||
# supervised by the puma plugin.
|
||||
config.active_job.queue_adapter = :solid_queue
|
||||
config.solid_queue.connects_to = { database: { writing: :queue } }
|
||||
|
||||
# Make code changes take effect immediately without server restart.
|
||||
config.enable_reloading = true
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ Rails.application.configure do
|
||||
config.cache_store = :memory_store
|
||||
|
||||
# Replace the default in-process and non-durable queuing backend for Active Job.
|
||||
config.active_job.queue_adapter = :async
|
||||
config.active_job.queue_adapter = :solid_queue
|
||||
config.solid_queue.connects_to = { database: { writing: :queue } }
|
||||
|
||||
# Ignore bad email addresses and do not raise email delivery errors.
|
||||
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Matchmaking configuration. All values can be overridden via environment variables.
|
||||
mm = Rails.application.config.x.matchmaking = ActiveSupport::OrderedOptions.new
|
||||
|
||||
mm.game_server_url = ENV.fetch("GAME_SERVER_URL", "ws://localhost:8080")
|
||||
mm.default_rating = ENV.fetch("MATCHMAKING_DEFAULT_RATING", 1000).to_i
|
||||
|
||||
# How long a found match (and its game token) stays valid, in seconds.
|
||||
mm.match_ttl = ENV.fetch("MATCHMAKING_MATCH_TTL", 120).to_i
|
||||
mm.token_ttl = ENV.fetch("MATCHMAKING_TOKEN_TTL", 120).to_i
|
||||
|
||||
# How long a player waits in the queue before a timeout, in seconds.
|
||||
mm.queue_ttl = ENV.fetch("MATCHMAKING_QUEUE_TTL", 60).to_i
|
||||
|
||||
# How long a found (but not yet finalized) pair is kept in the "pending"
|
||||
# state before the players are put back into the queue, in seconds.
|
||||
mm.pending_ttl = ENV.fetch("MATCHMAKING_PENDING_TTL", 15).to_i
|
||||
|
||||
# Rating tolerance: players within `base_range` are matched immediately;
|
||||
# the tolerance grows by `range_per_second` of waiting up to `max_range`.
|
||||
mm.base_range = ENV.fetch("MATCHMAKING_BASE_RANGE", 100).to_i
|
||||
mm.range_per_second = ENV.fetch("MATCHMAKING_RANGE_PER_SECOND", 50).to_i
|
||||
mm.max_range = ENV.fetch("MATCHMAKING_MAX_RANGE", 1000).to_i
|
||||
|
||||
# How often the background sweep job re-checks the queue, in seconds.
|
||||
mm.sweep_interval = ENV.fetch("MATCHMAKING_SWEEP_INTERVAL", 2).to_i
|
||||
@@ -2,6 +2,10 @@
|
||||
# Development/test: localhost:6379 (default)
|
||||
# Production: set REDIS_URL env variable, e.g. redis://redis.example.com:6379/0
|
||||
|
||||
# Use an isolated Redis logical database in tests to avoid clobbering dev data.
|
||||
url = ENV.fetch("REDIS_URL", "redis://localhost:6380/0")
|
||||
url = url.sub(%r{/\d+$}, "/15") if Rails.env.test?
|
||||
|
||||
RedisConnection = ConnectionPool.new(size: 5, timeout: 5) do
|
||||
Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6380/0"))
|
||||
Redis.new(url: url)
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ port ENV.fetch("PORT", 3000)
|
||||
plugin :tmp_restart
|
||||
|
||||
# Run the Solid Queue supervisor inside of Puma for single-server deployments.
|
||||
# plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
|
||||
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] || ENV.fetch("RAILS_ENV", "development") == "development"
|
||||
|
||||
# Specify the PID file. Defaults to tmp/pids/server.pid in development.
|
||||
# In other environments, only set the PID file if requested.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
default: &default
|
||||
dispatchers:
|
||||
- polling_interval: 1
|
||||
batch_size: 500
|
||||
workers:
|
||||
- queues: "*"
|
||||
threads: 3
|
||||
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
|
||||
polling_interval: 1
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
|
||||
test:
|
||||
<<: *default
|
||||
|
||||
production:
|
||||
<<: *default
|
||||
@@ -0,0 +1,18 @@
|
||||
# examples:
|
||||
# periodic_cleanup:
|
||||
# class: CleanSoftDeletedRecordsJob
|
||||
# queue: background
|
||||
# args: [ 1000, { batch_size: 500 } ]
|
||||
# schedule: every hour
|
||||
# periodic_cleanup_with_command:
|
||||
# command: "SoftDeletedRecord.due.delete_all"
|
||||
# priority: 2
|
||||
# schedule: at 5am every day
|
||||
|
||||
production:
|
||||
matchmaking_sweep:
|
||||
class: MatchmakingSweepJob
|
||||
schedule: every <%= ENV.fetch("MATCHMAKING_SWEEP_INTERVAL", 2) %> seconds
|
||||
clear_solid_queue_finished_jobs:
|
||||
command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
|
||||
schedule: every hour at minute 12
|
||||
@@ -1,12 +1,16 @@
|
||||
Rails.application.routes.draw do
|
||||
mount ActionCable.server => "/cable"
|
||||
|
||||
namespace :api do
|
||||
post "register", to: "registrations#create"
|
||||
post "login", to: "sessions#create"
|
||||
delete "logout", to: "sessions#destroy"
|
||||
get "me", to: "sessions#me"
|
||||
|
||||
post "matchmaking", to: "matchmaking#create"
|
||||
delete "matchmaking", to: "matchmaking#destroy"
|
||||
|
||||
# Matchmaking starts over the MatchmakingChannel WebSocket
|
||||
# (see app/channels/matchmaking_channel.rb).
|
||||
end
|
||||
|
||||
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
|
||||
|
||||
Reference in New Issue
Block a user