diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..960f030 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.bundle +vendor/ + diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..197c4d5 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.4.0 diff --git a/app.rb b/app.rb index 9937815..618d7bb 100644 --- a/app.rb +++ b/app.rb @@ -4,12 +4,29 @@ require 'rack-flash' require 'json' + + module Isucon4 class App < Sinatra::Base use Rack::Session::Cookie, secret: ENV['ISU4_SESSION_SECRET'] || 'shirokane' use Rack::Flash set :public_folder, File.expand_path('../../public', __FILE__) + def initialize + super() + setup + end + + def setup + last_succeeds = db.xquery('SELECT user_id, login, MAX(id) AS last_login_id FROM login_log WHERE user_id IS NOT NULL AND succeeded = 1 GROUP BY user_id') + + $last_succeeds_count = {} + last_succeeds.each do |row| + count = db.xquery('SELECT COUNT(id) AS cnt FROM login_log WHERE user_id = ? AND ? < id', row['user_id'], row['last_login_id']).first['cnt'] + $last_succeeds_count[row['user_id'].to_s] = { count: count, login: row['login'] } + end + end + helpers do def config @config ||= { @@ -38,6 +55,16 @@ def login_log(succeeded, login, user_id = nil) " (`created_at`, `user_id`, `login`, `ip`, `succeeded`)" \ " VALUES (?,?,?,?,?)", Time.now, user_id, login, request.ip, succeeded ? 1 : 0) + + if succeeded + $last_succeeds_count.delete(user_id.to_s) + else + if $last_succeeds_count.has_key?(user_id.to_s) + $last_succeeds_count[user_id.to_s][:count] = $last_succeeds_count[user_id.to_s][:count] + 1 + else + $last_succeeds_count[user_id.to_s] = { count: 1, login: login } + end + end end def user_locked?(user) @@ -124,15 +151,7 @@ def locked_users not_succeeded = db.xquery('SELECT user_id, login FROM (SELECT user_id, login, MAX(succeeded) as max_succeeded, COUNT(1) as cnt FROM login_log GROUP BY user_id) AS t0 WHERE t0.user_id IS NOT NULL AND t0.max_succeeded = 0 AND t0.cnt >= ?', threshold) user_ids.concat not_succeeded.each.map { |r| r['login'] } - - last_succeeds = db.xquery('SELECT user_id, login, MAX(id) AS last_login_id FROM login_log WHERE user_id IS NOT NULL AND succeeded = 1 GROUP BY user_id') - - last_succeeds.each do |row| - count = db.xquery('SELECT COUNT(1) AS cnt FROM login_log WHERE user_id = ? AND ? < id', row['user_id'], row['last_login_id']).first['cnt'] - if threshold <= count - user_ids << row['login'] - end - end + user_ids.concat $last_succeeds_count.select { |k,v| v[:count] >= threshold }.map { |k,v| v[:login] } user_ids end