Skip to content

Commit

Permalink
Merge pull request #13 from k5342/fix_4
Browse files Browse the repository at this point in the history
O(1)のget_events_from_idsをつくった
  • Loading branch information
euglena1215 committed Sep 19, 2018
2 parents cd2d2a2 + a485ce4 commit 2efd71b
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 23 deletions.
4 changes: 0 additions & 4 deletions torb/db/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,5 @@ if [ ! -f "$DB_DIR/isucon8q-initial-dataset.sql.gz" ]; then
fi

mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations DROP KEY event_id_and_sheet_id_idx'
mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations DROP KEY reserved_at'
mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations DROP KEY event_id_and_canceled_at'
gzip -dc "$DB_DIR/isucon8q-initial-dataset.sql.gz" | mysql -h 172.16.195.1 -uisucon torb -pisucon
mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations ADD KEY event_id_and_sheet_id_idx (event_id, sheet_id)'
mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations ADD KEY reserved_at (reserved_at)'
mysql -h 172.16.195.1 -uisucon -pisucon torb -e 'ALTER TABLE reservations ADD KEY event_id_and_canceled_at(event_id, canceled_at)'
4 changes: 3 additions & 1 deletion torb/db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ CREATE TABLE IF NOT EXISTS reservations (
user_id INTEGER UNSIGNED NOT NULL,
reserved_at DATETIME(6) NOT NULL,
canceled_at DATETIME(6) DEFAULT NULL,
KEY event_id_and_sheet_id_idx (event_id, sheet_id)
KEY event_id_and_sheet_id_idx (event_id, sheet_id),
KEY reservec_at (reserved_at),
KEY event_id_and_canceled_at (event_id, canceled_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE IF NOT EXISTS administrators (
Expand Down
1 change: 1 addition & 0 deletions torb/webapp/ruby/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ gem 'sinatra'
gem 'erubi'
gem 'mysql2'
gem 'mysql2-cs-bind'
gem 'pry'

group :development do
gem 'sinatra-contrib'
Expand Down
6 changes: 6 additions & 0 deletions torb/webapp/ruby/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ GEM
minitest (~> 5.1)
tzinfo (~> 1.1)
backports (3.11.4)
coderay (1.1.2)
concurrent-ruby (1.0.5)
erubi (1.7.1)
i18n (1.1.0)
concurrent-ruby (~> 1.0)
method_source (0.9.0)
minitest (5.11.3)
multi_json (1.13.1)
mustermann (1.0.3)
mysql2 (0.5.2)
mysql2-cs-bind (0.0.7)
mysql2
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
puma (3.12.0)
rack (2.0.5)
rack-protection (2.0.3)
Expand Down Expand Up @@ -46,6 +51,7 @@ DEPENDENCIES
erubi
mysql2
mysql2-cs-bind
pry
puma
sinatra
sinatra-contrib
Expand Down
148 changes: 130 additions & 18 deletions torb/webapp/ruby/lib/torb/web.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'erubi'
require 'mysql2'
require 'mysql2-cs-bind'
#require 'pry'
require 'pry'

module Torb
class Web < Sinatra::Base
Expand Down Expand Up @@ -61,11 +61,7 @@ def get_events(all: false)
db.query('SELECT * FROM events WHERE public_fg = 1 ORDER BY id ASC')
end.to_a

events = events.map do |event|
event = get_event(nil, event: event)
event['sheets'].each { |sheet| sheet.delete('detail') }
event
end
events = get_events_from_ids(nil, events: events, without_detail: true)

events
end
Expand Down Expand Up @@ -139,6 +135,121 @@ def get_event(event_id, login_user_id = nil, event: nil)
event
end


def get_events_from_ids(event_ids, login_user_id = nil, events: nil, without_detail: false)
events ||= db.xquery("SELECT * FROM events WHERE id IN (#{event_ids.join(", ")}) ORDER BY FIELD(id,#{event_ids.join(", ")})").to_a unless event_ids.nil?

event_ids ||= events.map {|row| row['id']}

# zero fill
events.each do |event|
event['total'] = 0
event['remains'] = 0
event['sheets'] = {}
%w[S A B C].each do |rank|
event['sheets'][rank] = { 'total' => rank_to_count(rank), 'remains' => 0, 'detail' => [], 'price' => 0 }
end
end

sql = <<SQL
SELECT sheets.*, r.event_id, r.user_id, r.reserved_at, r.canceled_at
FROM sheets
LEFT OUTER JOIN (
SELECT *
FROM reservations
WHERE event_id IN (#{event_ids.join(", ")})
AND canceled_at IS NULL
GROUP BY event_id, sheet_id
HAVING reserved_at = MIN(reserved_at)
) as r ON r.sheet_id = sheets.id
ORDER BY sheets.rank
SQL
result_with_event_id = db.query(sql.gsub("\n"," ")).to_a.group_by {|row| row['event_id']}
events.each do |event|
event['total'] = 1000
end

events.each do |event|
event['remains'] = 1000 - (result_with_event_id[event['id']] || {}).select { |row| row['reserved_at'] }.size
end

events.each do |event|
result_with_rank = (result_with_event_id[event['id']] || {}).group_by {|row| row['rank'] }
%w[S A B C].each do |rank|
event['sheets'][rank] = {
'total' => rank_to_count(rank),
'remains' => rank_to_count(rank) - (result_with_rank[rank] || {}).select {|row| row['reserved_at'] }.size,
'price' => event['price'] + rank_to_price(rank),
'detail' => []
}
end
end

if without_detail
events.each do |event|
%w[S A B C].each do |rank|
event['sheets'][rank].delete('detail')
end
end
else
events.each do |event|
result = result_with_event_id[event['id']]

result.each do |row|
row['mine'] = login_user_id == row['user_id']
row['reserved'] = !row['reserved_at'].nil?
row['reserved_at'] = row['reserved_at']&.to_i

row.delete('canceled_at')
row.delete('event_id')
row.delete('id')
#row.delete('price')
row.delete('user_id')
event['sheets'][row['rank']]['detail'] << row
end
end

events.each do |event|
%w[S A B C].each do |rank|
event['sheets'][rank]['detail'].sort_by!{|x| x['num']}
end
end
end

events.each do |event|
event['public'] = event.delete('public_fg')
event['closed'] = event.delete('closed_fg')
end

events
end

def rank_to_count(rank)
case rank
when "S"
50
when "A"
150
when "B"
300
when "C"
500
end
end

def rank_to_price(rank)
case rank
when "S"
5000
when "A"
3000
when "B"
1000
when "C"
0
end
end

def sanitize_event(event)
sanitized = event.dup # shallow clone
sanitized.delete('price')
Expand Down Expand Up @@ -207,7 +318,7 @@ def render_report_csv(reports_body)
user_id = db.last_id
db.query('COMMIT')
rescue => e
warn "rollback by: #{e}"
warn "#{__LINE__} : rollback by: #{e}"
db.query('ROLLBACK')
halt_with_error
end
Expand All @@ -223,9 +334,10 @@ def render_report_csv(reports_body)
end

rows = db.xquery('SELECT r.*, s.rank AS sheet_rank, s.num AS sheet_num FROM reservations r INNER JOIN sheets s ON s.id = r.sheet_id WHERE r.user_id = ? ORDER BY IFNULL(r.canceled_at, r.reserved_at) DESC LIMIT 5', user['id'])
events_with_id = get_events_from_ids(rows.map {|row| row['event_id']}.uniq, without_detail: true).group_by {|row| row['id']}
recent_reservations = rows.map do |row|
event = get_event(row['event_id'])
price = event['sheets'][row['sheet_rank']]['price']
event = events_with_id[row['event_id']].first
price = event['price'] + rank_to_price(row['sheet_rank'])
event.delete('sheets')
event.delete('total')
event.delete('remains')
Expand All @@ -245,11 +357,7 @@ def render_report_csv(reports_body)
user['total_price'] = db.xquery('SELECT IFNULL(SUM(e.price + s.price), 0) AS total_price FROM reservations r INNER JOIN sheets s ON s.id = r.sheet_id INNER JOIN events e ON e.id = r.event_id WHERE r.user_id = ? AND r.canceled_at IS NULL', user['id']).first['total_price']

rows = db.xquery('SELECT event_id FROM reservations WHERE user_id = ? GROUP BY event_id ORDER BY MAX(IFNULL(canceled_at, reserved_at)) DESC LIMIT 5', user['id'])
user['recent_events'] = rows.map do |row|
event = get_event(row['event_id'])
event['sheets'].each { |_, sheet| sheet.delete('detail') }
event
end
user['recent_events'] = get_events_from_ids(rows.map {|row| row['event_id']}, without_detail: true)

user.to_json
end
Expand Down Expand Up @@ -297,16 +405,20 @@ def render_report_csv(reports_body)
sheet = nil
reservation_id = nil
loop do
sheet = db.xquery('SELECT * FROM sheets WHERE id NOT IN (SELECT sheet_id FROM reservations WHERE event_id = ? AND canceled_at IS NULL FOR UPDATE) AND `rank` = ? ORDER BY RAND() LIMIT 1', event['id'], rank).first
halt_with_error 409, 'sold_out' unless sheet
db.query('BEGIN')
sheet = db.xquery('SELECT * FROM sheets WHERE id NOT IN (SELECT sheet_id FROM reservations WHERE event_id = ? AND canceled_at IS NULL FOR UPDATE) AND `rank` = ? ORDER BY RAND() LIMIT 1', event['id'], rank).first
unless sheet
db.query('COMMIT')
halt_with_error 409, 'sold_out' unless sheeta
end
#db.query('BEGIN')
begin
db.xquery('INSERT INTO reservations (event_id, sheet_id, user_id, reserved_at) VALUES (?, ?, ?, ?)', event['id'], sheet['id'], user['id'], Time.now.utc.strftime('%F %T.%6N'))
reservation_id = db.last_id
db.query('COMMIT')
rescue => e
db.query('ROLLBACK')
warn "re-try: rollback by #{e}"
warn "#{__LINE__} : re-try: rollback by #{e}"
next
end

Expand Down Expand Up @@ -431,7 +543,7 @@ def render_report_csv(reports_body)
get '/admin/api/reports/events/:id/sales', admin_login_required: true do |event_id|
event = get_event(event_id)

reservations = db.xquery('SELECT r.*, s.rank AS sheet_rank, s.num AS sheet_num, s.price AS sheet_price, e.price AS event_price FROM reservations r INNER JOIN sheets s ON s.id = r.sheet_id INNER JOIN events e ON e.id = r.event_id WHERE r.event_id = ? ORDER BY reserved_at ASC FOR UPDATE', event['id'])
reservations = db.xquery('SELECT r.*, s.rank AS sheet_rank, s.num AS sheet_num, s.price AS sheet_price, e.price AS event_price FROM reservations r INNER JOIN sheets s ON s.id = r.sheet_id INNER JOIN events e ON e.id = r.event_id WHERE r.event_id = ? ORDER BY reserved_at ASC', event['id'])
keys = %i[reservation_id event_id rank num price user_id sold_at canceled_at]
body = keys.join(',') << "\n"

Expand Down

0 comments on commit 2efd71b

Please sign in to comment.