-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
close #1991 add virtual waiting room
- Loading branch information
Showing
20 changed files
with
438 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
app/controllers/concerns/spree_cm_commissioner/waiting_room_authorization.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
module SpreeCmCommissioner | ||
module WaitingRoomAuthorization | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
rescue_from JWT::ExpiredSignature, with: :handle_waiting_room_session_token_error | ||
rescue_from JWT::VerificationError, with: :handle_waiting_room_session_token_error | ||
rescue_from JWT::DecodeError, with: :handle_waiting_room_session_token_error | ||
|
||
before_action :ensure_waiting_room_session_token | ||
end | ||
|
||
def ensure_waiting_room_session_token | ||
return if disabled_authorization? | ||
|
||
JWT.decode(params[:waiting_room_session_token], ENV.fetch('WAITING_ROOM_SIGNATURE'), true, { algorithm: 'HS256' }) | ||
end | ||
|
||
def disabled_authorization? | ||
ENV['DISABLE_WAITING_ROOM'] == 'yes' | ||
end | ||
|
||
def handle_waiting_room_session_token_error | ||
render_error_payload(exception.message, 400) | ||
end | ||
end | ||
end |
23 changes: 23 additions & 0 deletions
23
app/controllers/spree/api/v2/storefront/waiting_room_sessions_controller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# We use lambda instead of following API to generate token. Following are just sample. | ||
# | ||
# POST: /api/v2/storefront/waiting_room_sesions | ||
# Response: { token: token } | ||
module Spree | ||
module Api | ||
module V2 | ||
module Storefront | ||
class WaitingRoomSessionsController < ::Spree::Api::V2::ResourceController | ||
skip_before_action :ensure_waiting_room_session_token | ||
|
||
def create | ||
context = SpreeCmCommissioner::WaitingRoomSessionCreator.call( | ||
remote_ip: request.remote_ip, | ||
firebase_document_id: params[:firebase_document_id] | ||
) | ||
render json: { token: context.token } | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
33 changes: 33 additions & 0 deletions
33
app/interactors/spree_cm_commissioner/server_instances_bulk_creator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
require 'aws-sdk-ecs' | ||
|
||
# TODO: UI to check all instances, max sessions, active_sessions, and available slots | ||
module SpreeCmCommissioner | ||
class ServerInstancesBulkCreator < BaseInteractor | ||
def call | ||
ecs_client = Aws::ECS::Client.new( | ||
region: ENV.fetch('AWS_REGION'), | ||
access_key_id: ENV.fetch('AWS_ACCESS_KEY_ID'), | ||
secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY') | ||
) | ||
|
||
tasks_response = ecs_client.list_tasks( | ||
cluster: ENV.fetch('AWS_CLUSTER_NAME'), | ||
launch_type: 'FARGATE' | ||
) | ||
|
||
context.servers = [] | ||
context.task_arns = tasks_response.task_arns | ||
context.task_arns.each do |task_arn| | ||
server = SpreeCmCommissioner::ServerInstance.where(instance_id: task_arn).first_or_initialize | ||
server.concurrent_requests ||= 5 | ||
server.status = :active | ||
server.save! if server.changed? | ||
context.servers << server | ||
end | ||
|
||
SpreeCmCommissioner::ServerInstance.where.not(id: context.servers.pluck(:id)).find_each do |server| | ||
server.update!(status: :inactive) | ||
end | ||
end | ||
end | ||
end |
45 changes: 45 additions & 0 deletions
45
app/interactors/spree_cm_commissioner/waiting_guests_caller.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
require 'google/cloud/firestore' | ||
|
||
# TODO: alert when available_slots is negative. | ||
module SpreeCmCommissioner | ||
class WaitingGuestsCaller < BaseInteractor | ||
def call | ||
available_slots = fetch_available_slots | ||
return unless available_slots.positive? | ||
|
||
long_waiting_guests = fetch_long_waiting_guests(available_slots) | ||
calling_all(long_waiting_guests) | ||
end | ||
|
||
def fetch_available_slots | ||
max_sessions = SpreeCmCommissioner::ServerInstance.max_sessions_count_with_min | ||
active_sessions = SpreeCmCommissioner::WaitingRoomSession.active.count | ||
max_sessions - active_sessions | ||
end | ||
|
||
# This query required index. create them in Firebase beforehand. | ||
# Client side must create waiting_guests document with :queued_at & :allow_to_refresh_to_enter_room_at to null to allow fillter & order. | ||
def fetch_long_waiting_guests(available_slots) | ||
firestore = Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account) | ||
firestore.col('waiting_guests') | ||
.where('allow_to_refresh_to_enter_room_at', '==', nil) | ||
.order('queued_at') | ||
.limit(available_slots) | ||
.get | ||
end | ||
|
||
# For alert waiting guests to enter room, we just update :allow_to_refresh_to_enter_room_at. | ||
# App will listen to firebase & start refresh session token to enter room. | ||
def calling_all(waiting_guests) | ||
waiting_guests.each do |document| | ||
data = document.get.data.dup | ||
data[:allow_to_refresh_to_enter_room_at] = Time.utc.now | ||
document.update(data) | ||
end | ||
end | ||
|
||
def service_account | ||
Rails.application.credentials.cloud_firestore_service_account | ||
end | ||
end | ||
end |
44 changes: 44 additions & 0 deletions
44
app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
require 'google/cloud/firestore' | ||
|
||
module SpreeCmCommissioner | ||
class WaitingRoomSessionCreator < BaseInteractor | ||
delegate :remote_ip, :firebase_document_id, to: :context | ||
|
||
def call | ||
return context.fail!(message: 'must_provide_firebase_document_id') if firebase_document_id.blank? | ||
return context.fail!(message: 'must_provide_remote_ip') if remote_ip.blank? | ||
|
||
generate_jwt_token | ||
assign_token_and_create_session_to_db | ||
log_to_firebase | ||
end | ||
|
||
def generate_jwt_token | ||
payload = { exp: expired_at.to_i } | ||
context.jwt_token = JWT.encode(payload, ENV.fetch('WAITING_ROOM_SIGNATURE'), 'HS256') | ||
end | ||
|
||
def assign_token_and_create_session_to_db | ||
context.room_sesion = SpreeCmCommissioner::WaitingRoomSession.create!(token: context.jwt_token, expired_at: expired_at, remote_ip: remote_ip) | ||
end | ||
|
||
def log_to_firebase | ||
firestore = Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account) | ||
document = firestore.col('waiting_guests').doc(firebase_document_id) | ||
|
||
data = document.get.data.dup | ||
data[:entered_room_at] = Time.zone.now | ||
|
||
document.update(data) | ||
end | ||
|
||
def expired_at | ||
expired_duration = ENV['WAITING_ROOM_SESSION_EXPIRE_DURATION_IN_SECOND']&.presence&.to_i || (60 * 5) | ||
context.expired_at ||= expired_duration.seconds.from_now | ||
end | ||
|
||
def service_account | ||
Rails.application.credentials.cloud_firestore_service_account | ||
end | ||
end | ||
end |
12 changes: 12 additions & 0 deletions
12
app/jobs/spree_cm_commissioner/server_instances_bulk_creator_job.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# server_instances_bulk_creator: | ||
# cron: cron: "*/30 * * * *" # Every 30 minute | ||
# class: SpreeCmCommissioner::ServerInstanceBulkCreatorJob | ||
module SpreeCmCommissioner | ||
class ServerInstancesBulkCreatorJob < ApplicationJob | ||
def perform | ||
return if ENV['DISABLE_WAITING_ROOM'] == 'yes' | ||
|
||
SpreeCmCommissioner::ServerInstancesBulkCreator.call | ||
end | ||
end | ||
end |
12 changes: 12 additions & 0 deletions
12
app/jobs/spree_cm_commissioner/waiting_room_sessions_releaser_job.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# waiting_room_sessions_releaser: | ||
# cron: cron: "*/1 * * * *" # Every minute | ||
# class: SpreeCmCommissioner::WaitingRoomSessionsReleaserJob | ||
module SpreeCmCommissioner | ||
class WaitingRoomSessionsReleaserJob < ApplicationJob | ||
def perform | ||
return if ENV['DISABLE_WAITING_ROOM'] == 'yes' | ||
|
||
SpreeCmCommissioner::WaitingRoomSessionsReleaser.call | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module SpreeCmCommissioner | ||
class ServerInstance < Base | ||
validates :instance_id, presence: true | ||
validates :concurrent_requests, presence: true | ||
|
||
enum status: { active: 0, inactive: 1 } | ||
|
||
def self.max_sessions_count | ||
ServerInstance.active.select('SUM(concurrent_requests * multiply_by) AS total').take.total || 0 | ||
end | ||
|
||
def self.min_session_count | ||
ENV.fetch('MIN_SESSION_COUNT', '5').to_i | ||
end | ||
|
||
def self.max_sessions_count_with_min | ||
[max_sessions_count, min_session_count].min | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module SpreeCmCommissioner | ||
class WaitingRoomSession < Base | ||
validates :token, presence: true | ||
validates :expired_at, presence: true | ||
validates :ip, presence: true | ||
|
||
scope :active, -> { SpreeCmCommissioner::WaitingRoomSession.where('expired_at > ?', Time.current) } | ||
|
||
def expired? | ||
expired_at < Time.current | ||
end | ||
end | ||
end |
14 changes: 14 additions & 0 deletions
14
db/migrate/20241119032456_create_cm_waiting_room_sessions.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class CreateCmWaitingRoomSessions < ActiveRecord::Migration[7.0] | ||
def change | ||
create_table :cm_waiting_room_sessions, if_not_exists: true do |t| | ||
t.string :token, null: false | ||
t.string :remote_ip | ||
t.datetime :expired_at, null: false | ||
|
||
t.timestamps | ||
end | ||
|
||
add_index :cm_waiting_room_sessions, :expired_at, if_not_exists: true | ||
add_index :cm_waiting_room_sessions, :token, if_not_exists: true | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
class CreateCmServerInstances < ActiveRecord::Migration[7.0] | ||
def change | ||
create_table :cm_server_instances, if_not_exists: true do |t| | ||
t.string :instance_id, null: false | ||
t.integer :concurrent_requests, default: 5, null: false | ||
t.integer :multiply_by, default: 1, null: false | ||
t.integer :status, default: 0, null: false | ||
|
||
t.timestamps | ||
end | ||
|
||
add_index :cm_server_instances, :instance_id, if_not_exists: true | ||
add_index :cm_server_instances, :status, if_not_exists: true | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# recommend to be used in schedule.yml & manually access in /sidekiq/cron | ||
namespace :spree_cm_commissioner do | ||
desc 'Create services instances by fetch from AWS' | ||
task server_instances_bulk_creator: :environment do | ||
ServerInstancesBulkCreatorJob.perform_now | ||
end | ||
end |
52 changes: 52 additions & 0 deletions
52
...tancesBulkCreator/_call/request_all_tasks_create_active_servers_inactive_all_services.yml
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.