├── README.md
├── final
├── README.md
├── benchmarker
│ ├── ad.go
│ ├── advertiser.go
│ ├── asset.go
│ ├── bb.go
│ ├── bench.go
│ ├── bench_behavior.go
│ ├── cache.go
│ ├── contants.go
│ ├── err.go
│ ├── logger.go
│ ├── main.go
│ ├── master.go
│ ├── queue.go
│ ├── rcmd.go
│ ├── recipe.go
│ ├── recipe_test.go
│ ├── remote.go
│ ├── request.go
│ ├── requester.go
│ ├── schema.go
│ ├── score.go
│ ├── slave.go
│ ├── slot.go
│ ├── team.go
│ ├── team_test.go
│ ├── user.go
│ ├── user_test.go
│ ├── useragents.go
│ ├── util.go
│ └── worker.go
└── webapp
│ ├── go
│ ├── .gitignore
│ ├── app.go
│ └── build.sh
│ ├── perl
│ ├── .gitignore
│ ├── Makefile.PL
│ ├── README.md
│ ├── app.psgi
│ ├── cpanfile
│ ├── cpanfile.snapshot
│ ├── lib
│ │ ├── Isu4Final.pm
│ │ └── Isu4Final
│ │ │ └── Web.pm
│ ├── public
│ ├── t
│ │ └── 00_compile.t
│ └── views
│ │ ├── base.tx
│ │ └── index.tx
│ ├── php
│ ├── php-fpm.conf
│ └── src
│ │ ├── index.php
│ │ ├── info.php
│ │ ├── limonade
│ │ ├── .gitignore
│ │ ├── AUTHORS
│ │ ├── CHANGES
│ │ ├── LICENSE
│ │ ├── LISEZMOI.mkd
│ │ ├── README.mkd
│ │ ├── TODO
│ │ ├── composer.json
│ │ ├── docs
│ │ │ ├── Configuration_and_Options.markdown
│ │ │ ├── Contributing_and_Support.markdown
│ │ │ ├── Examples.markdown
│ │ │ ├── Getting_Started.markdown
│ │ │ ├── Routing.markdown
│ │ │ └── Views_and_Helpers.markdown
│ │ ├── examples
│ │ │ ├── example01
│ │ │ │ ├── index.php
│ │ │ │ └── public
│ │ │ │ │ ├── soda_glass.jpg
│ │ │ │ │ └── soda_glass.thb.jpg
│ │ │ ├── example02
│ │ │ │ └── index.php
│ │ │ ├── example03
│ │ │ │ ├── .htaccess
│ │ │ │ └── index.php
│ │ │ ├── example04
│ │ │ │ └── index.php
│ │ │ ├── example05
│ │ │ │ ├── index.php
│ │ │ │ └── views
│ │ │ │ │ ├── index.html.php
│ │ │ │ │ └── layout.html.php
│ │ │ ├── example06
│ │ │ │ └── index.php
│ │ │ ├── index.php
│ │ │ └── urlrewrite
│ │ │ │ ├── htaccess.conf
│ │ │ │ ├── lighttpd.conf
│ │ │ │ └── nginx.conf
│ │ ├── lib
│ │ │ ├── limonade.php
│ │ │ └── limonade
│ │ │ │ ├── abstract.php
│ │ │ │ ├── assertions.php
│ │ │ │ ├── public
│ │ │ │ ├── css
│ │ │ │ │ └── screen.css
│ │ │ │ └── img
│ │ │ │ │ └── bg_header.png
│ │ │ │ ├── tests.php
│ │ │ │ └── views
│ │ │ │ ├── _debug.html.php
│ │ │ │ ├── _notices.html.php
│ │ │ │ ├── default_layout.php
│ │ │ │ └── error.html.php
│ │ └── tests
│ │ │ ├── all.php
│ │ │ ├── apps
│ │ │ ├── 00-empty.php
│ │ │ ├── 01-hello_world.php
│ │ │ ├── 02-outputs.php
│ │ │ ├── 03-routing.php
│ │ │ ├── 04-errors.php
│ │ │ ├── 05-content_negociation.php
│ │ │ ├── 06-session.php
│ │ │ ├── 07-flash.php
│ │ │ ├── 08-params.php
│ │ │ ├── 09-redirect.php
│ │ │ ├── index.php
│ │ │ └── views
│ │ │ │ ├── hello_world.html.php
│ │ │ │ ├── hello_world_filtered.html.php
│ │ │ │ └── layouts
│ │ │ │ └── default.html.php
│ │ │ ├── config
│ │ │ └── config.php.dist
│ │ │ ├── data
│ │ │ ├── deer.jpg
│ │ │ ├── empty_text_file.txt
│ │ │ └── lib0
│ │ │ │ ├── a.php
│ │ │ │ ├── b.php
│ │ │ │ └── c.php
│ │ │ ├── file.php
│ │ │ ├── functional.php
│ │ │ ├── helpers
│ │ │ └── show_request_uri.php
│ │ │ ├── http.php
│ │ │ ├── main.php
│ │ │ ├── output.php
│ │ │ ├── request.php
│ │ │ ├── router.php
│ │ │ └── tests.php
│ │ └── public
│ ├── public
│ ├── index.html
│ ├── javascripts
│ │ ├── index.js
│ │ ├── isuad.js
│ │ ├── jquery.js
│ │ └── mustache.js
│ ├── stylesheets
│ │ ├── bootflat.min.css
│ │ ├── bootstrap.min.css
│ │ └── index.css
│ └── view.html
│ ├── python
│ ├── .gitignore
│ ├── README.md
│ ├── app.py
│ ├── gunicorn_config.py
│ └── static
│ └── ruby
│ ├── .gitignore
│ ├── Gemfile
│ ├── Gemfile.lock
│ ├── Procfile
│ ├── app.rb
│ ├── config.ru
│ └── unicorn_config.rb
└── qualifier
├── README.md
├── ami
├── .gitignore
├── .rspec
├── Gemfile
├── Gemfile.lock
├── README.md
├── ansible
│ ├── .gitignore
│ ├── 00_devel.yml
│ ├── 01_qualifier.yml
│ ├── 02_supervisord.yml
│ ├── 03_xbuild.yml
│ ├── 04_golang.yml
│ ├── 05_benchmarker.yml
│ └── _cleanup.yml
├── files
│ ├── bashrc
│ ├── env.sh
│ ├── golang.sh
│ ├── my.cnf
│ ├── nginx.conf
│ ├── nginx.php.conf
│ ├── php.ini
│ ├── rsync_exclude.txt
│ ├── supervisord.conf
│ ├── supervisord.init
│ └── user_data_benchmarker.sh
├── pack.rb
└── spec
│ ├── qualifier_spec.rb
│ └── spec_helper.rb
├── benchmarker
├── .gitignore
├── .rsync-filter
├── Gomfile
├── Makefile
├── README.md
├── ec2.go
├── init.sh
├── ip
│ ├── ip.go
│ ├── ip_list.go
│ ├── ip_list_test.go
│ └── ip_test.go
├── main.go
├── md5
├── sql
│ ├── dummy_users.tsv
│ └── dummy_users_used.tsv
├── user
│ ├── dummy_users.go
│ ├── get_dummy_users.go
│ ├── random_string.go
│ ├── user.go
│ └── user_test.go
└── worker
│ ├── login_scenario.go
│ ├── request.go
│ ├── request_test.go
│ ├── scenario.go
│ ├── work.go
│ ├── worker.go
│ └── workers.go
├── gcp
├── .gitignore
├── .rspec
├── Gemfile
├── Gemfile.lock
├── README.md
├── ansible
│ ├── .gitignore
│ ├── 00_devel.yml
│ ├── 01_qualifier.yml
│ ├── 02_supervisord.yml
│ ├── 03_xbuild.yml
│ ├── 04_golang.yml
│ ├── 05_benchmarker.yml
│ └── _cleanup.yml
├── files
│ ├── bashrc
│ ├── env.sh
│ ├── epel.repo
│ ├── golang.sh
│ ├── my.cnf
│ ├── nginx.conf
│ ├── nginx.php.conf
│ ├── php.ini
│ ├── rsync_exclude.txt
│ ├── supervisord.conf
│ ├── supervisord.init
│ └── user_data_benchmarker.sh
├── pack.rb
└── spec
│ ├── qualifier_spec.rb
│ └── spec_helper.rb
├── init.sh
├── sql
├── .gitignore
├── dummy_log.sql
├── dummy_users.sql
├── dummy_users.tsv
├── dummy_users_used.tsv
├── generate_dummy_log.rb
├── generate_users.rb
└── schema.sql
└── webapp
├── go
├── .gitignore
├── README.mkd
├── build.sh
├── db.go
├── main.go
├── templates
│ ├── index.tmpl
│ ├── layout.tmpl
│ └── mypage.tmpl
├── user.go
└── util.go
├── node
├── .gitignore
├── README.mkd
├── app.js
├── package.json
└── views
│ ├── index.ect
│ ├── layout.ect
│ └── mypage.ect
├── perl
├── .gitignore
├── app.psgi
├── cpanfile
├── cpanfile.snapshot
├── lib
│ ├── Isu4Qualifier.pm
│ └── Isu4Qualifier
│ │ └── Web.pm
├── public
└── views
│ ├── base.tx
│ ├── index.tx
│ └── mypage.tx
├── php
├── Procfile
├── README.md
├── nginx.local.conf
├── php-fpm.conf
└── src
│ ├── index.php
│ ├── info.php
│ ├── limonade
│ ├── .gitignore
│ ├── AUTHORS
│ ├── CHANGES
│ ├── LICENSE
│ ├── LISEZMOI.mkd
│ ├── README.mkd
│ ├── TODO
│ ├── composer.json
│ ├── docs
│ │ ├── Configuration_and_Options.markdown
│ │ ├── Contributing_and_Support.markdown
│ │ ├── Examples.markdown
│ │ ├── Getting_Started.markdown
│ │ ├── Routing.markdown
│ │ └── Views_and_Helpers.markdown
│ ├── examples
│ │ ├── example01
│ │ │ ├── index.php
│ │ │ └── public
│ │ │ │ ├── soda_glass.jpg
│ │ │ │ └── soda_glass.thb.jpg
│ │ ├── example02
│ │ │ └── index.php
│ │ ├── example03
│ │ │ ├── .htaccess
│ │ │ └── index.php
│ │ ├── example04
│ │ │ └── index.php
│ │ ├── example05
│ │ │ ├── index.php
│ │ │ └── views
│ │ │ │ ├── index.html.php
│ │ │ │ └── layout.html.php
│ │ ├── example06
│ │ │ └── index.php
│ │ ├── index.php
│ │ └── urlrewrite
│ │ │ ├── htaccess.conf
│ │ │ ├── lighttpd.conf
│ │ │ └── nginx.conf
│ ├── lib
│ │ ├── limonade.php
│ │ └── limonade
│ │ │ ├── abstract.php
│ │ │ ├── assertions.php
│ │ │ ├── public
│ │ │ ├── css
│ │ │ │ └── screen.css
│ │ │ └── img
│ │ │ │ └── bg_header.png
│ │ │ ├── tests.php
│ │ │ └── views
│ │ │ ├── _debug.html.php
│ │ │ ├── _notices.html.php
│ │ │ ├── default_layout.php
│ │ │ └── error.html.php
│ └── tests
│ │ ├── all.php
│ │ ├── apps
│ │ ├── 00-empty.php
│ │ ├── 01-hello_world.php
│ │ ├── 02-outputs.php
│ │ ├── 03-routing.php
│ │ ├── 04-errors.php
│ │ ├── 05-content_negociation.php
│ │ ├── 06-session.php
│ │ ├── 07-flash.php
│ │ ├── 08-params.php
│ │ ├── 09-redirect.php
│ │ ├── index.php
│ │ └── views
│ │ │ ├── hello_world.html.php
│ │ │ ├── hello_world_filtered.html.php
│ │ │ └── layouts
│ │ │ └── default.html.php
│ │ ├── config
│ │ └── config.php.dist
│ │ ├── data
│ │ ├── deer.jpg
│ │ ├── empty_text_file.txt
│ │ └── lib0
│ │ │ ├── a.php
│ │ │ ├── b.php
│ │ │ └── c.php
│ │ ├── file.php
│ │ ├── functional.php
│ │ ├── helpers
│ │ └── show_request_uri.php
│ │ ├── http.php
│ │ ├── main.php
│ │ ├── output.php
│ │ ├── request.php
│ │ ├── router.php
│ │ └── tests.php
│ ├── public
│ └── views
│ ├── base.html.php
│ ├── index.html.php
│ └── mypage.html.php
├── public
├── images
│ └── isucon-bank.png
└── stylesheets
│ ├── bootflat.min.css
│ ├── bootstrap.min.css
│ └── isucon-bank.css
├── python
├── .gitignore
├── README.md
├── app.py
├── gunicorn_config.py
├── static
└── templates
│ ├── base.html
│ ├── index.html
│ └── mypage.html
└── ruby
├── Gemfile
├── Gemfile.lock
├── Procfile
├── app.rb
├── config.ru
├── unicorn_config.rb
└── views
├── base.erb
├── index.erb
└── mypage.erb
/README.md:
--------------------------------------------------------------------------------
1 | # ISUCON4 problems
2 |
3 | - [qualifier](./qualifier)
4 | - [final](./final)
5 |
6 | ## License
7 |
8 | ```
9 | The MIT License (MIT)
10 |
11 | Copyright (c) 2014 Cookpad Inc.
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy
14 | of this software and associated documentation files (the "Software"), to deal
15 | in the Software without restriction, including without limitation the rights
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | copies of the Software, and to permit persons to whom the Software is
18 | furnished to do so, subject to the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be included in
21 | all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 | THE SOFTWARE.
30 | ```
31 |
--------------------------------------------------------------------------------
/final/README.md:
--------------------------------------------------------------------------------
1 | # ISUCON4 本選問題
2 |
3 | - [本選レギュレーション](http://bit.ly/isu4reg)
4 | - [本選当日マニュアル](http://bit.ly/isu4man)
5 | - [本選 AMI マニュアル](https://gist.github.com/mirakui/5e5a75d66bae314555a2)
6 |
7 | ## AMI
8 | - アプリケーション AMI (v1.2)
9 | - ami-86e8e287
10 | - ベンチマーカー AMI (v1.2)
11 | - ami-84e8e285
12 |
--------------------------------------------------------------------------------
/final/benchmarker/ad.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/docker/docker/pkg/namesgenerator"
5 | "strconv"
6 | "sync"
7 | "sync/atomic"
8 | )
9 |
10 | type Ad struct {
11 | *sync.Mutex
12 |
13 | Advertiser *Advertiser
14 | Slot *Slot
15 | Id string
16 | Title string
17 | Destination string
18 | Path string
19 | Asset *Asset
20 | Impression int64
21 | ClickedUsers []*User
22 | }
23 |
24 | func NewAd() *Ad {
25 | return &Ad{
26 | Mutex: new(sync.Mutex),
27 | Title: namesgenerator.GetRandomName(0),
28 | Path: namesgenerator.GetRandomName(0),
29 | Impression: 0,
30 | ClickedUsers: []*User{},
31 | }
32 | }
33 |
34 | func (ad *Ad) IncrImp() {
35 | atomic.AddInt64(&ad.Impression, 1)
36 | }
37 |
38 | func (ad *Ad) Click(u *User) {
39 | ad.Lock()
40 | defer ad.Unlock()
41 |
42 | ad.ClickedUsers = append(ad.ClickedUsers, u)
43 | }
44 |
45 | func (ad *Ad) BreakDown() (agents, gender, generations map[string]float64) {
46 | ad.Lock()
47 | defer ad.Unlock()
48 |
49 | agents = map[string]float64{}
50 | gender = map[string]float64{}
51 | generations = map[string]float64{}
52 |
53 | for _, u := range ad.ClickedUsers {
54 | agents[u.UserAgent]++
55 |
56 | if u.DNT {
57 | gender["unknown"]++
58 | generations["unknown"]++
59 | } else {
60 | sex := "female"
61 | if u.Gender != 0 {
62 | sex = "male"
63 | }
64 | gender[sex]++
65 | generations[strconv.Itoa(int(u.Age/10))]++
66 | }
67 | }
68 |
69 | return
70 | }
71 |
--------------------------------------------------------------------------------
/final/benchmarker/advertiser.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/docker/docker/pkg/namesgenerator"
6 | "net/http"
7 | )
8 |
9 | type Advertiser struct {
10 | Id int
11 | Slots []*Slot
12 | Validated bool
13 | }
14 |
15 | func GetAdvertiser() *Advertiser {
16 | return &Advertiser{
17 | Slots: []*Slot{},
18 | Validated: false,
19 | }
20 | }
21 |
22 | func (ad *Advertiser) Apply(req *http.Request) {
23 | req.Header.Set("X-Advertiser-Id", fmt.Sprintf("%d", ad.Id))
24 | }
25 |
26 | func (ad *Advertiser) NewSlot() *Slot {
27 | s := NewSlot(fmt.Sprintf("%d-%s", ad.Id, namesgenerator.GetRandomName(0)))
28 | s.Advertiser = ad
29 | ad.Slots = append(ad.Slots, s)
30 | return s
31 | }
32 |
33 | func (adr *Advertiser) AllAds() []*Ad {
34 | ads := []*Ad{}
35 |
36 | for _, s := range adr.Slots {
37 | ads = append(ads, s.Ads...)
38 | }
39 |
40 | return ads
41 | }
42 |
--------------------------------------------------------------------------------
/final/benchmarker/asset.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io/ioutil"
5 | "path"
6 | )
7 |
8 | type Asset struct {
9 | Path string
10 | MD5 string
11 | }
12 |
13 | var Assets []*Asset
14 |
15 | func PreloadAssets(dir string) {
16 | if len(Assets) > 0 {
17 | return
18 | }
19 |
20 | Assets = []*Asset{}
21 | files, _ := ioutil.ReadDir(dir)
22 | for _, file := range files {
23 | if file.IsDir() {
24 | continue
25 | }
26 |
27 | fp := path.Join(dir, file.Name())
28 | data, _ := ioutil.ReadFile(fp)
29 | md5 := GetMD5(data)
30 | asset := &Asset{fp, md5}
31 | Assets = append(Assets, asset)
32 | }
33 | }
34 |
35 | func GetAssets(idx int) []*Asset {
36 | l := len(Assets)
37 | max := l / AssetsSparation
38 | if l%AssetsSparation != 0 {
39 | max++
40 | }
41 | for idx >= max {
42 | idx -= max
43 | }
44 | stIdx := (idx * AssetsSparation)
45 | edIdx := stIdx + AssetsSparation
46 | if edIdx > l {
47 | edIdx = l
48 | }
49 |
50 | return Assets[stIdx:edIdx]
51 | }
52 |
--------------------------------------------------------------------------------
/final/benchmarker/bb.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/codegangsta/cli"
5 | "os"
6 | "strings"
7 | )
8 |
9 | var bbFlags = []cli.Flag{
10 | cli.StringFlag{
11 | Name: "hosts, H",
12 | Usage: "ベンチ実行先ホスト。カンマ区切りで複数設定可能。",
13 | EnvVar: "HOSTS",
14 | Value: "127.0.0.1",
15 | },
16 | cli.IntFlag{
17 | Name: "workload, w",
18 | Usage: "並列ワーカー数。1 から 8 の範囲で指定。",
19 | EnvVar: "WORKLOAD",
20 | Value: int(DEFAULT_WORKLOAD),
21 | },
22 | cli.StringFlag{
23 | Name: "api-key",
24 | Value: "None",
25 | },
26 | }
27 |
28 | func BBAction(c *cli.Context) {
29 | recipe := NewRecipe()
30 |
31 | hosts := strings.Split(c.String("hosts"), ",")
32 | for _, host := range hosts {
33 | host = strings.TrimSpace(host)
34 |
35 | if len(host) > 0 {
36 | recipe.Hosts = append(recipe.Hosts, host)
37 | }
38 | }
39 |
40 | if len(recipe.Hosts) < 1 {
41 | defaultLogger.Info("実行先ホストが存在しないためベンチが実行できません")
42 | os.Exit(1)
43 | }
44 |
45 | if c.Int("workload") > 0 && c.Int("workload") <= 8 {
46 | recipe.Workload = c.Int("workload")
47 | }
48 |
49 | if c.Int("workload") >= 8 {
50 | recipe.Workload = 8
51 | }
52 |
53 | ApiKey = c.String("api-key")
54 |
55 | if ApiKey == "None" {
56 | defaultLogger.Info("API-KEY が設定されていません。環境変数 ISUCON_API_KEY を確認するか、運営へご相談ください。")
57 | os.Exit(1)
58 | }
59 |
60 | team := GetTeamByApiKey(ApiKey)
61 | if team == nil {
62 | defaultLogger.Info("チーム情報の取得に失敗しました。API-KEY がおかしい可能性があります。確認の上、運営へご相談ください。 API-KEY: %s", ApiKey)
63 | os.Exit(1)
64 | }
65 |
66 | recipe.ApiKey = ApiKey
67 | recipe.SendScore = true
68 | recipe.NoForce = true
69 |
70 | Bench(recipe, nil)
71 | }
72 |
--------------------------------------------------------------------------------
/final/benchmarker/cache.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "github.com/marcw/cachecontrol"
6 | "io/ioutil"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | type ClosableBuffer struct {
12 | *bytes.Reader
13 | }
14 |
15 | func (b *ClosableBuffer) Close() error {
16 | b.Reader = nil
17 | return nil
18 | }
19 |
20 | type URLCache struct {
21 | LastModified string
22 | Etag string
23 | ExpiresAt time.Time
24 | CacheControl *cachecontrol.CacheControl
25 | CachedResponse *http.Response
26 | CachedBody []byte
27 | MD5 string
28 | }
29 |
30 | func NewCache(res *http.Response) *URLCache {
31 | directive := res.Header.Get("Cache-Control")
32 | cc := cachecontrol.Parse(directive)
33 | noCache, _ := cc.NoCache()
34 |
35 | if len(directive) == 0 || noCache || cc.NoStore() {
36 | return nil
37 | }
38 |
39 | now := time.Now()
40 | lm := res.Header.Get("Last-Modified")
41 | etag := res.Header.Get("ETag")
42 |
43 | body, err := ioutil.ReadAll(res.Body)
44 | if err != nil {
45 | return nil
46 | }
47 | res.Body.Close()
48 | md5 := GetMD5(body)
49 |
50 | if len(body) >= 1024*1024*4 {
51 | body = []byte{}
52 | }
53 |
54 | res.Body = &ClosableBuffer{bytes.NewReader(body)}
55 |
56 | res.Header.Set(CachedHeader, CachedHeaderVal)
57 | res.Header.Set(CachedMD5Header, md5)
58 |
59 | return &URLCache{
60 | LastModified: lm,
61 | Etag: etag,
62 | ExpiresAt: now.Add(cc.MaxAge()),
63 | CacheControl: &cc,
64 | CachedResponse: res,
65 | CachedBody: body,
66 | MD5: md5,
67 | }
68 | }
69 |
70 | func (c *URLCache) Available() bool {
71 | return time.Now().Before(c.ExpiresAt)
72 | }
73 |
74 | func (c *URLCache) Apply(req *http.Request) {
75 | if c.Available() {
76 | if c.LastModified != "" {
77 | req.Header.Add("If-Modified-Since", c.LastModified)
78 | }
79 |
80 | if c.Etag != "" {
81 | req.Header.Add("If-None-Match", c.Etag)
82 | }
83 | }
84 | }
85 |
86 | func (c *URLCache) Restore(res *http.Response) {
87 | res.Status = c.CachedResponse.Status
88 | res.StatusCode = c.CachedResponse.StatusCode
89 | res.Header = c.CachedResponse.Header
90 | res.ContentLength = c.CachedResponse.ContentLength
91 | res.TransferEncoding = c.CachedResponse.TransferEncoding
92 | res.Body = &ClosableBuffer{bytes.NewReader(c.CachedBody)}
93 | res.Header.Set(CachedHeader, CachedHeaderVal)
94 | res.Header.Set(CachedMD5Header, c.MD5)
95 | }
96 |
--------------------------------------------------------------------------------
/final/benchmarker/contants.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "github.com/rosylilly/envdef"
6 | )
7 |
8 | const (
9 | DEFAULT_WORKLOAD = 1
10 | )
11 |
12 | var (
13 | WORKER_RUNNING_DURATION = envdef.Get("HALLEY_RUNNING_DURATION", "1m")
14 | WORKER_TIMEOUT_DURATION = envdef.Get("HALLEY_TIMEOUT_DURATION", "20s")
15 | WORKER_ABORTING_DURATION = envdef.Get("HALLEY_ABORTING_DURATION", "20s")
16 | )
17 |
18 | var (
19 | ErrRequestTimeout = errors.New("Connection timeout")
20 | ErrRequestCanceled = errors.New("Request cancelled because benchmark finished")
21 | )
22 |
23 | var (
24 | GenerateAdvertisersCount = 1000
25 | GenerateUsersCount = 10000
26 | )
27 |
28 | var (
29 | UserUnderAge = 13
30 | UserUpperAge = 80
31 | UserDNTPercentage = 100
32 | )
33 |
34 | const (
35 | AdvertiserWorker WorkerRole = iota
36 | UserWorker
37 | )
38 |
39 | const (
40 | MaxAdvertisersCount = 15
41 | )
42 |
43 | const AssetsSparation = 5
44 |
45 | const (
46 | ValidationHeaderKey = "X-CHOSEN-GREEN-TEA"
47 | ValidationHeaderVal = "AYATAKA"
48 | CachedHeader = "X-Halley-Cached"
49 | CachedHeaderVal = "true"
50 | CachedMD5Header = "X-Halley-MD5"
51 | )
52 |
53 | var ApiKey = envdef.Get("ISUCON_API_KEY", "None")
54 |
55 | const (
56 | TimeFormat = "2006-01-02 15:04:05"
57 | )
58 |
59 | var (
60 | AssetsDir = envdef.Get("HALLEY_ASSETS_DIR", "/home/isucon/creatives")
61 | MasterIP = envdef.Get("HALLEY_MASTER_HOST", "10.11.54.62")
62 | MasterHost = MasterIP + ":9091"
63 | MasterAPIKey = envdef.Get("HALLEY_MASTER_API_KEY", "None")
64 | )
65 |
--------------------------------------------------------------------------------
/final/benchmarker/logger.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "code.google.com/p/go.net/websocket"
5 | "fmt"
6 | "io"
7 | "os"
8 | "time"
9 | )
10 |
11 | type Logger struct {
12 | Stdout io.Writer
13 | Stderr io.Writer
14 | }
15 |
16 | var defaultLogger = &Logger{os.Stdout, os.Stderr}
17 |
18 | func (l *Logger) Write(b []byte) (int, error) {
19 | return l.Stdout.Write(b)
20 | }
21 |
22 | func (l *Logger) Info(msg string, args ...interface{}) {
23 | io.WriteString(
24 | l.Stderr,
25 | fmt.Sprintf(
26 | "[%s] %s\n",
27 | time.Now().Format(TimeFormat),
28 | fmt.Sprintf(msg, args...),
29 | ),
30 | )
31 | }
32 |
33 | type WSLogger struct {
34 | To string
35 | ws *websocket.Conn
36 | }
37 |
38 | func (w *WSLogger) Write(b []byte) (int, error) {
39 | err := websocket.JSON.Send(w.ws, &RemoteCommand{
40 | Name: w.To,
41 | Options: map[string]interface{}{
42 | "body": string(b),
43 | },
44 | })
45 |
46 | return len(b), err
47 | }
48 |
49 | func NewWSLogger(ws *websocket.Conn) *Logger {
50 | return &Logger{
51 | Stdout: &WSLogger{"stdout", ws},
52 | Stderr: &WSLogger{"stderr", ws},
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/final/benchmarker/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/codegangsta/cli"
5 | "os"
6 | )
7 |
8 | var Version string
9 |
10 | func main() {
11 | if Version == "" {
12 | Version = "debug"
13 | }
14 |
15 | app := cli.NewApp()
16 | app.Name = "benchmarker"
17 | app.Version = Version
18 | app.Usage = "For ISUCON4 FINAL"
19 | app.Author = ""
20 | app.Email = ""
21 | app.Commands = []cli.Command{
22 | {
23 | Name: "remote",
24 | Usage: "launch remote benchmark process",
25 | Flags: remoteFlags,
26 | Action: RemoteAction,
27 | },
28 | {
29 | Name: "bench",
30 | Usage: "lanunch standalone benchmark process",
31 | Flags: benchFlags,
32 | Action: BenchAction,
33 | },
34 | }
35 | if MasterAPIKey != "None" {
36 | app.Commands = append(
37 | app.Commands,
38 | cli.Command{
39 | Name: "server",
40 | Usage: "launch server process",
41 | Flags: masterFlags,
42 | Action: MasterAction,
43 | },
44 | )
45 | app.Commands = append(
46 | app.Commands,
47 | cli.Command{
48 | Name: "bb",
49 | Usage: "launch server process",
50 | Flags: bbFlags,
51 | Action: BBAction,
52 | },
53 | )
54 | }
55 |
56 | app.Run(os.Args)
57 | }
58 |
--------------------------------------------------------------------------------
/final/benchmarker/queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "code.google.com/p/go.net/websocket"
5 | "time"
6 | )
7 |
8 | type Queue struct {
9 | ws *websocket.Conn
10 | TeamId int `json:"team_id"`
11 | ApiKey string `json:"-"`
12 | QueuedAt time.Time `json:"queued_at"`
13 | StartedAt *time.Time `json:"started_at"`
14 | Option *QueueOption `json:"options"`
15 | }
16 |
17 | type QueueOption struct {
18 | Hosts string `json:"hosts"`
19 | Workload int `json:"workload"`
20 | }
21 |
--------------------------------------------------------------------------------
/final/benchmarker/rcmd.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "code.google.com/p/go.net/websocket"
5 | "fmt"
6 | "io"
7 | "os"
8 | "time"
9 | )
10 |
11 | func Exists(filename string) bool {
12 | _, err := os.Stat(filename)
13 | return err == nil
14 | }
15 |
16 | type RemoteCommand struct {
17 | Name string `json:"name"`
18 | Options map[string]interface{} `json:"options"`
19 | }
20 |
21 | func (c *RemoteCommand) Execute(ws *websocket.Conn) {
22 | switch c.Name {
23 | case "ping":
24 | websocket.JSON.Send(ws, &RemoteCommand{Name: "pong"})
25 | case "cancel":
26 | ServerMaster.Cancel(c.Options["api-key"].(string))
27 | case "bench":
28 | if Exists("/home/isucon/stop-queue") {
29 | WSInfo(ws, "現在新規キューイング停止中のため追加されません")
30 | ws.Close()
31 | return
32 | }
33 |
34 | queued := ServerMaster.Push(
35 | ws,
36 | int(c.Options["team-id"].(float64)),
37 | c.Options["api-key"].(string),
38 | c.Options["hosts"].(string),
39 | int(c.Options["workload"].(float64)),
40 | )
41 |
42 | if !queued {
43 | WSInfo(ws, "すでに処理待ちキューに追加済みのため、追加されません")
44 | ws.Close()
45 | } else {
46 | WSInfo(ws, "実行待ちキューに追加されました")
47 | }
48 |
49 | case "stdout":
50 | body := c.Options["body"]
51 |
52 | sbody, ok := body.(string)
53 | if ok {
54 | io.WriteString(os.Stdout, sbody)
55 | }
56 | case "stderr":
57 | body := c.Options["body"]
58 |
59 | sbody, ok := body.(string)
60 | if ok {
61 | io.WriteString(os.Stderr, sbody)
62 | }
63 | }
64 | }
65 |
66 | func WSInfo(ws *websocket.Conn, msg string, args ...interface{}) {
67 | websocket.JSON.Send(ws, &RemoteCommand{
68 | Name: "stderr",
69 | Options: map[string]interface{}{
70 | "body": fmt.Sprintf(
71 | "[%s] %s\n",
72 | time.Now().Format(TimeFormat),
73 | fmt.Sprintf(msg, args...),
74 | ),
75 | },
76 | })
77 | }
78 |
--------------------------------------------------------------------------------
/final/benchmarker/recipe_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/kr/pretty"
5 | "testing"
6 | )
7 |
8 | func TestRecipe(t *testing.T) {
9 | br := NewRecipe()
10 | pretty.Printf("%# v\n\n", br.advertisers)
11 | }
12 |
--------------------------------------------------------------------------------
/final/benchmarker/request.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io"
5 | "net/http"
6 | "net/url"
7 | "strings"
8 | "time"
9 | )
10 |
11 | func (w *Worker) NewRequest(method, uri string, body io.Reader) (*http.Request, error) {
12 | parsedURL, err := url.ParseRequestURI(uri)
13 |
14 | if err != nil {
15 | return nil, err
16 | }
17 |
18 | if parsedURL.Scheme == "" {
19 | parsedURL.Scheme = "http"
20 | }
21 |
22 | if parsedURL.Host == "" {
23 | parsedURL.Host = w.Host()
24 | }
25 |
26 | req, err := http.NewRequest(strings.ToUpper(method), parsedURL.String(), body)
27 |
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | return req, err
33 | }
34 |
35 | func (w *Worker) SendRequest(
36 | req *http.Request,
37 | to time.Duration,
38 | ) (resp *http.Response, err error) {
39 | reqCh := make(chan bool)
40 |
41 | cache := w.Recipe.URLCaches[req.URL.String()]
42 | if cache != nil {
43 | cache.Apply(req)
44 | }
45 |
46 | w.nowRequest = req
47 |
48 | req.Header.Set("Connection", "Keep-Alive")
49 |
50 | go func() {
51 | resp, err = w.Client.Do(req)
52 | reqCh <- true
53 | }()
54 |
55 | timeoutTimer := time.After(to)
56 | waitingResponse := true
57 |
58 | for waitingResponse {
59 | select {
60 | case <-reqCh:
61 | waitingResponse = false
62 | case <-timeoutTimer:
63 | w.Transport.CancelRequest(req)
64 | if w.running {
65 | err = ErrRequestTimeout
66 | } else {
67 | err = ErrRequestCanceled
68 | }
69 | waitingResponse = false
70 | }
71 | }
72 |
73 | w.nowRequest = nil
74 |
75 | if err == nil && resp != nil {
76 | if resp.StatusCode == http.StatusNotModified && cache != nil {
77 | cache.Restore(resp)
78 | } else {
79 | if resp.Request.Method == "GET" && resp.StatusCode >= http.StatusOK && resp.StatusCode <= 299 {
80 | cache := NewCache(resp)
81 | if cache != nil && w != nil && w.Recipe != nil && w.Recipe.URLCaches != nil {
82 | w.Recipe.URLCaches[req.URL.String()] = cache
83 | }
84 | }
85 | }
86 | }
87 |
88 | return resp, err
89 | }
90 |
--------------------------------------------------------------------------------
/final/benchmarker/requester.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | type Requester interface {
8 | Apply(*http.Request)
9 | }
10 |
--------------------------------------------------------------------------------
/final/benchmarker/slave.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os/exec"
5 | "strconv"
6 | "time"
7 | )
8 |
9 | type Slave struct {
10 | Master *Master
11 | NowQueue *Queue
12 | }
13 |
14 | func NewSlave(master *Master) *Slave {
15 | return &Slave{
16 | Master: master,
17 | }
18 | }
19 |
20 | func (s *Slave) Waiting() {
21 | defer func() {
22 | if err := recover(); err != nil {
23 | defaultLogger.Info("%s", err)
24 | }
25 | }()
26 |
27 | for {
28 | if queue := s.Master.Pop(); queue != nil {
29 | s.Bench(queue)
30 | }
31 | }
32 | }
33 |
34 | func (s *Slave) Bench(queue *Queue) {
35 | s.NowQueue = queue
36 | now := time.Now()
37 | queue.StartedAt = &now
38 |
39 | logger := NewWSLogger(queue.ws)
40 | defaultLogger.Info("ベンチマーク開始: %s", queue.ApiKey)
41 | logger.Info("ベンチマークを開始します")
42 |
43 | cmd := exec.Command(
44 | "./benchmarker-2", "bb", "--hosts", queue.Option.Hosts, "--workload", strconv.Itoa(queue.Option.Workload), "--api-key", ``+queue.ApiKey+``,
45 | )
46 |
47 | cmd.Stdout = &WSLogger{"stdout", queue.ws}
48 | cmd.Stderr = &WSLogger{"stderr", queue.ws}
49 |
50 | err := cmd.Run()
51 | if err != nil {
52 | defaultLogger.Info("実行エラー: %s", err)
53 | }
54 |
55 | defaultLogger.Info("ベンチマーク完了: %s", queue.ApiKey)
56 |
57 | queue.ws.Close()
58 | s.NowQueue = nil
59 | }
60 |
--------------------------------------------------------------------------------
/final/benchmarker/slot.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type Slot struct {
8 | *sync.Mutex
9 |
10 | Advertiser *Advertiser
11 | Id string
12 | Ads []*Ad
13 | Assets []*Asset
14 | astIdx int
15 | idAndAd map[string]*Ad
16 | pathAndAd map[string]*Ad
17 | }
18 |
19 | func NewSlot(id string) *Slot {
20 | return &Slot{
21 | Mutex: new(sync.Mutex),
22 | Id: id,
23 | idAndAd: map[string]*Ad{},
24 | pathAndAd: map[string]*Ad{},
25 | }
26 | }
27 |
28 | func (s *Slot) NewAd(url string) *Ad {
29 | ad := NewAd()
30 | ad.Advertiser = s.Advertiser
31 | ad.Slot = s
32 | ad.Asset = s.Assets[s.astIdx%len(s.Assets)]
33 | s.astIdx++
34 | ad.Destination = url
35 | return ad
36 | }
37 |
--------------------------------------------------------------------------------
/final/benchmarker/team.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | )
7 |
8 | type Team struct {
9 | Id int `json:"id"`
10 | Name string `json:"name"`
11 | }
12 |
13 | func GetTeamByApiKey(apiKey string) *Team {
14 | var team *Team = nil
15 |
16 | req, err := http.NewRequest("GET", "https://isucon4-portal.herokuapp.com/teams/me", nil)
17 | if err != nil {
18 | return nil
19 | }
20 |
21 | req.Header.Set("Authorization", "isucon "+apiKey)
22 |
23 | res, err := http.DefaultClient.Do(req)
24 | if err != nil {
25 | return nil
26 | }
27 | defer res.Body.Close()
28 |
29 | if res.StatusCode != http.StatusOK {
30 | return nil
31 | }
32 |
33 | err = json.NewDecoder(res.Body).Decode(&team)
34 |
35 | if err != nil {
36 | return nil
37 | }
38 |
39 | return team
40 | }
41 |
--------------------------------------------------------------------------------
/final/benchmarker/team_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestGetTeam(t *testing.T) {
9 | team := GetTeamByApiKey("1--neodu7-335h-82bb96096cb6f76d38073ef54a13d9be3b08b9ba")
10 | fmt.Printf("%# v\n", team)
11 | }
12 |
--------------------------------------------------------------------------------
/final/benchmarker/user.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "net/http"
7 | )
8 |
9 | type User struct {
10 | Gender int
11 | Age int
12 | UserAgent string
13 | DNT bool
14 | }
15 |
16 | func GetRandomUser() *User {
17 | return &User{
18 | Gender: rand.Int() % 2,
19 | Age: UserUnderAge + rand.Intn(UserUpperAge),
20 | UserAgent: GetUserAgent(),
21 | DNT: (rand.Int() % UserDNTPercentage) == 0,
22 | }
23 | }
24 |
25 | func (u *User) Identifier() string {
26 | return fmt.Sprintf("%d/%d", u.Gender, u.Age)
27 | }
28 |
29 | func (u *User) Apply(req *http.Request) {
30 | if !u.DNT {
31 | cookie := &http.Cookie{
32 | Name: "isuad",
33 | Value: u.Identifier(),
34 | Path: "/",
35 | Domain: req.URL.Host,
36 | }
37 | req.AddCookie(cookie)
38 | }
39 | req.Header.Set("User-Agent", u.UserAgent)
40 | }
41 |
--------------------------------------------------------------------------------
/final/benchmarker/user_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
--------------------------------------------------------------------------------
/final/benchmarker/util.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "crypto/md5"
6 | "fmt"
7 | "io"
8 | "io/ioutil"
9 | "mime/multipart"
10 | "net/http"
11 | "os"
12 | "path/filepath"
13 | )
14 |
15 | func GetMD5(data []byte) string {
16 | return fmt.Sprintf("%x", md5.Sum(data))
17 | }
18 |
19 | func GetMD5ByIO(r io.Reader) string {
20 | bytes, _ := ioutil.ReadAll(r)
21 | return GetMD5(bytes)
22 | }
23 |
24 | func NewFileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
25 | file, err := os.Open(path)
26 | if err != nil {
27 | return nil, err
28 | }
29 | defer file.Close()
30 |
31 | body := &bytes.Buffer{}
32 | writer := multipart.NewWriter(body)
33 | part, err := writer.CreateFormFile(paramName, filepath.Base(path))
34 | if err != nil {
35 | return nil, err
36 | }
37 | _, err = io.Copy(part, file)
38 |
39 | for key, val := range params {
40 | _ = writer.WriteField(key, val)
41 | }
42 | writer.WriteField("type", "video/mp4")
43 |
44 | err = writer.Close()
45 | if err != nil {
46 | return nil, err
47 | }
48 |
49 | req, err := http.NewRequest("POST", uri, body)
50 | if err == nil {
51 | req.Header.Add("Content-Type", writer.FormDataContentType())
52 | }
53 |
54 | return req, err
55 | }
56 |
--------------------------------------------------------------------------------
/final/webapp/go/.gitignore:
--------------------------------------------------------------------------------
1 | gin-bin
2 | golang-webapp
3 |
--------------------------------------------------------------------------------
/final/webapp/go/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | go get github.com/go-martini/martini
4 | go get gopkg.in/redis.v3
5 | go get github.com/martini-contrib/render
6 | go get github.com/kr/pretty
7 | go build -o golang-webapp .
8 |
--------------------------------------------------------------------------------
/final/webapp/perl/.gitignore:
--------------------------------------------------------------------------------
1 | local/
2 |
3 |
--------------------------------------------------------------------------------
/final/webapp/perl/Makefile.PL:
--------------------------------------------------------------------------------
1 | use ExtUtils::MakeMaker;
2 |
3 | WriteMakefile(
4 | NAME => 'Isu4Final',
5 | VERSION_FROM => 'lib/Isu4Final.pm',
6 | PREREQ_PM => {
7 | 'Kossy' => '0.38',
8 | },
9 | MIN_PERL_VERSION => '5.008001'
10 | );
11 |
12 |
--------------------------------------------------------------------------------
/final/webapp/perl/README.md:
--------------------------------------------------------------------------------
1 | # Perl Implmentation
2 |
3 | ```
4 | $ carton install
5 | $ carton exec plackup -s Starman -r app.psgi
6 | ```
7 |
--------------------------------------------------------------------------------
/final/webapp/perl/app.psgi:
--------------------------------------------------------------------------------
1 | use FindBin;
2 | use lib "$FindBin::Bin/extlib/lib/perl5";
3 | use lib "$FindBin::Bin/lib";
4 | use File::Basename;
5 | use Plack::Builder;
6 | use Isu4Final::Web;
7 |
8 | my $root_dir = File::Basename::dirname(__FILE__);
9 |
10 | my $app = Isu4Final::Web->psgi($root_dir);
11 | builder {
12 | enable 'ReverseProxy';
13 | enable 'Static',
14 | path => qr!^/javascripts/!,
15 | root => $root_dir . '/public';
16 | $app;
17 | };
18 |
19 |
--------------------------------------------------------------------------------
/final/webapp/perl/cpanfile:
--------------------------------------------------------------------------------
1 | requires "Kossy";
2 | requires "Redis";
3 | requires "Starman";
4 |
--------------------------------------------------------------------------------
/final/webapp/perl/lib/Isu4Final.pm:
--------------------------------------------------------------------------------
1 | package Isu4Final;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 |
7 | our $VERSION = 0.38;
8 |
9 | 1;
10 |
11 |
--------------------------------------------------------------------------------
/final/webapp/perl/public:
--------------------------------------------------------------------------------
1 | ../public
--------------------------------------------------------------------------------
/final/webapp/perl/t/00_compile.t:
--------------------------------------------------------------------------------
1 | use strict;
2 | use warnings;
3 | use Test::More;
4 |
5 | use_ok $_ for qw(
6 | Isu4Final
7 | Isu4Final::Web
8 | );
9 |
10 | done_testing;
11 |
12 |
13 |
--------------------------------------------------------------------------------
/final/webapp/perl/views/base.tx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Isu4Final
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
36 |
37 |
38 |
39 | : block content -> { }
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/final/webapp/perl/views/index.tx:
--------------------------------------------------------------------------------
1 | : cascade base
2 | : around content -> {
3 | <: $greeting :> <: $c.stash.site_name :>
4 | Use this document as a way to quick start any new project. All you get is this message and a barebones HTML document.
5 | : }
6 |
7 |
--------------------------------------------------------------------------------
/final/webapp/php/php-fpm.conf:
--------------------------------------------------------------------------------
1 | daemonize = no
2 |
3 | [www]
4 | ;user = isucon
5 | ;group = isucon
6 | listen = 0.0.0.0:8080
7 |
8 | pm = dynamic
9 | pm.max_children = 10
10 | pm.start_servers = 10
11 | pm.min_spare_servers = 10
12 | pm.max_spare_servers = 10
13 | pm.process_idle_timeout = 10s;
14 | pm.max_requests = 500
15 | pm.status_path = /status
16 | ping.path = /ping
17 |
18 | catch_workers_output = true
19 |
--------------------------------------------------------------------------------
/final/webapp/php/src/info.php:
--------------------------------------------------------------------------------
1 | .
2 | The limonade logo and other design stuffs were created by MiniMau .
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Fabrice Luraine
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sofadesign/limonade",
3 | "description": "a PHP micro-framework",
4 | "homepage": "https://github.com/sofadesign/limonade",
5 | "keywords": ["microframework"],
6 | "autoload": {
7 | "files": ["lib/limonade.php"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Configuration_and_Options.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Configuration_and_Options.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Contributing_and_Support.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Contributing_and_Support.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Examples.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Examples.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Getting_Started.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Getting_Started.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Routing.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Routing.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/docs/Views_and_Helpers.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/docs/Views_and_Helpers.markdown
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example01/public/soda_glass.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/examples/example01/public/soda_glass.jpg
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example01/public/soda_glass.thb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/examples/example01/public/soda_glass.thb.jpg
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example03/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Options +FollowSymlinks
3 | Options +Indexes
4 | RewriteEngine on
5 |
6 |
7 | # test string is a valid files
8 | RewriteCond %{SCRIPT_FILENAME} !-f
9 | # test string is a valid directory
10 | RewriteCond %{SCRIPT_FILENAME} !-d
11 |
12 | RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
13 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example03/index.php:
--------------------------------------------------------------------------------
1 | \n";
25 | $output .= "";
28 | return $output;
29 | }
30 |
31 | # defaults work the same as always...
32 | dispatch('/', 'hello_world');
33 | function hello_world()
34 | {
35 | return "Hello world!";
36 | }
37 |
38 | # able to pass options to routes, which are also available in the 'before' filter in the $route argument
39 | dispatch('/account', 'user_account',
40 | array("authenticate" => TRUE));
41 | function user_account()
42 | {
43 | return "You are authenticated (or rather, you would be if the 'authenticate_user' was real)";
44 | }
45 |
46 | # sometimes there param validation rules that are difficult or impossible to implement as a regex
47 | # Here is an example of attaching a validation function to a route.
48 | # Call this with /validate/1234 to pass, or with any other argument to fail
49 | dispatch('/validate/*', 'validate_test',
50 | array('validation_function' => 'a_silly_validation_function'));
51 | function validate_test(){
52 | return "Yup! You've passed the validation test";
53 | }
54 | run();
55 |
56 | //------------------------------
57 | // Utilities
58 | //------------------------------
59 | function authenticate_user()
60 | {
61 | //auth the user here...
62 | return true;
63 | }
64 |
65 |
66 | function a_silly_validation_function($params)
67 | {
68 | //perhaps this looks something up in the database...
69 | if (isset($params[0]) && $params[0] == "1234") return true;
70 |
71 | return false;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example04/index.php:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
2 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
3 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
4 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
5 | dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
6 | sunt in culpa qui officia deserunt mollit anim id est laborum.
7 |
8 |
9 | This content is available in the layout with the
10 | $sidebar
variable
11 |
12 |
13 | Exeros quisque modiam aliquipsum facincilit min.
14 | Eliquatum amconsendre quatet aciliqu consecte lore.
15 | Dolum feugue faccum.
16 | Eui lan landignibh, feugiamet nullam modit volorerci, eros nosto sequam veliquisit.
17 |
18 |
19 | Enibh ullan utpationse turpis velisim ullut alismolum.
20 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example05/views/layout.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example 05:
6 |
7 |
8 |
9 |
10 |
11 |
Main content
12 |
13 |
14 |
17 |
18 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/example06/index.php:
--------------------------------------------------------------------------------
1 | added before render…";
30 | }
31 |
32 | return array($content_or_func, $layout, $locals, $view_path);
33 | }
34 |
35 | /**
36 | * a filter for rewriting views without short_open_tags
37 | *
38 | * @param string $view_path
39 | * @return string $path to converted view file
40 | */
41 | function render_filter_rewrite_short_tags($view_path)
42 | {
43 | if (option('rewrite_short_tags') == true && (boolean)@ini_get('short_open_tag') === false)
44 | {
45 | # cache path, maybe in tmp/cache/rsot_views/
46 | $cache_path = file_path(option('rewrite_sot_cache_dir'), $view_path);
47 | if(!file_exists($cache_path) || (filemtime($cache_path) != filemtime($view_path)))
48 | {
49 | $view = file_get_contents($view_path);
50 | $transformed_view = preg_replace("/;*\s*\?>/", "; ?>", str_replace('=', 'Hellooo!');
63 | }
64 |
65 | dispatch('/error', 'index_error');
66 | function index_error()
67 | {
68 | return halt('Error!');
69 | }
70 |
71 | run();
72 |
73 | # _INLINE templates___________________________________________________________
74 |
75 | function html_default_layout($vars){ extract($vars);?>
76 |
77 |
78 |
79 |
80 | Before render filter test
81 |
82 |
83 |
84 | =$content;?>
85 |
86 |
87 |
88 | Menu:
89 | Index |
90 | Error
91 |
92 |
93 |
94 |
95 |
96 | };
97 |
98 | ?>
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/index.php:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | Limonade examples
8 |
9 |
10 |
11 |
12 |
13 | Limonade examples
14 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/urlrewrite/htaccess.conf:
--------------------------------------------------------------------------------
1 |
2 | Options +FollowSymlinks
3 | Options +Indexes
4 | RewriteEngine on
5 |
6 | # if your app is in a subfolder
7 | # RewriteBase /my_app/
8 |
9 | # test string is a valid files
10 | RewriteCond %{SCRIPT_FILENAME} !-f
11 | # test string is a valid directory
12 | RewriteCond %{SCRIPT_FILENAME} !-d
13 |
14 | RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
15 | # with QSA flag (query string append),
16 | # forces the rewrite engine to append a query string part of the
17 | # substitution string to the existing string, instead of replacing it.
18 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/urlrewrite/lighttpd.conf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/examples/urlrewrite/lighttpd.conf
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/examples/urlrewrite/nginx.conf:
--------------------------------------------------------------------------------
1 | # If you are using Nginx, add the following to your server declaration,
2 | # and restart your Nginx.
3 |
4 | # ATTENTION:
5 | # Besides editing your Nginx configuation, REMEMBER to
6 | # set the option('base_uri') in your configure() function (for example):
7 | # option('base_uri', '/');
8 | # Then the function url_for() will work properly (for example).
9 | # url_for('one', 'two'); # returns /one/two instead of ?/one/two
10 |
11 | server {
12 | location / {
13 |
14 | try_files $uri $uri/ @rewrite;
15 | }
16 | location @rewrite {
17 | rewrite ^/(.*)$ /index.php?u=$1&$args;
18 | }
19 | }
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/lib/limonade/public/img/bg_header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/lib/limonade/public/img/bg_header.png
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/lib/limonade/views/_debug.html.php:
--------------------------------------------------------------------------------
1 | ENV_PRODUCTION && option('debug')): ?>
2 |
3 | []
4 | (in line )
5 |
6 |
7 |
8 |
9 |
10 | Debug arguments
11 |
12 |
13 |
14 | Options
15 |
16 | [ ↑ ]
17 |
18 | Environment
19 |
20 | [ ↑ ]
21 |
22 | Backtrace
23 |
24 | [ ↑ ]
25 |
26 |
36 |
37 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/lib/limonade/views/_notices.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
→ Notices and warnings
4 |
5 |
6 | []
7 |
8 | in
9 | line
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/lib/limonade/views/default_layout.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Limonade, the fizzy PHP micro-framework
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/lib/limonade/views/error.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/00-empty.php:
--------------------------------------------------------------------------------
1 |
72 |
73 |
74 |
75 |
78 | my content
79 |
80 | sidebar
81 |
82 | HTML" ;
17 | }
18 | else
19 | {
20 | return 'Oops' ;
21 | }
22 | }
23 |
24 | run();
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/06-session.php:
--------------------------------------------------------------------------------
1 | Hellooo!');
14 | }
15 |
16 | dispatch('/two', 'index_two');
17 | function index_two()
18 | {
19 | flash('notice', 'ON DISPLAY 3');
20 | return html('Hellooo!
');
21 | }
22 | dispatch('/three', 'index_three');
23 | function index_three()
24 | {
25 | flash('error', 'ON DISPLAY 4');
26 | return html('Hellooo!
');
27 | }
28 | dispatch('/four', 'index_four');
29 | function index_four()
30 | {
31 | return html('NO FLASH MESSAGE ON NEXT PAGE
');
32 | }
33 | dispatch('/five', 'index_five');
34 | function index_five()
35 | {
36 | flash('error', 'ON DISPLAY 6');
37 | redirect_to('six');
38 | }
39 | dispatch('/six', 'index_six');
40 | function index_six()
41 | {
42 | return html('REDIRECTED FROM INDEX FIVE...
There will be no flash message on next page.
');
43 | }
44 |
45 |
46 |
47 | run();
48 |
49 | # _INLINE templates___________________________________________________________
50 |
51 | function html_default_layout($vars){ extract($vars);?>
52 |
53 |
54 |
55 |
56 | Flash features test
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Current flash messages ( flash_now() / $flash )
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Menu:
74 | One |
75 | Two |
76 | Three |
77 | Four |
78 | Five |
79 | Six
80 |
81 |
82 |
83 |
84 | 1 ? $keyAndValue[1] : '';
13 | }
14 | array_shift($params);
15 |
16 | return redirect_to('/redirected', $params);
17 | }
18 |
19 | dispatch('/redirected', 'redirected');
20 | function redirected()
21 | {
22 | print $_SERVER['QUERY_STRING'];
23 | }
24 |
25 | run();
26 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/views/hello_world.html.php:
--------------------------------------------------------------------------------
1 | Hello World
2 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/views/hello_world_filtered.html.php:
--------------------------------------------------------------------------------
1 | Hello World Filtered
2 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/apps/views/layouts/default.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Page title
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/config/config.php.dist:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/data/deer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/tests/data/deer.jpg
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/data/empty_text_file.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/final/webapp/php/src/limonade/tests/data/empty_text_file.txt
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/data/lib0/a.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/data/lib0/b.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/data/lib0/c.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/helpers/show_request_uri.php:
--------------------------------------------------------------------------------
1 | HTML", $response);
40 |
41 | $response = test_request(TESTS_DOC_ROOT.'05-content_negociation.php', 'GET', false, array(), array("Accept: application/json"));
42 | assert_equal("json", $response);
43 | }
44 |
45 | function test_http_redirect()
46 | {
47 | $url = TESTS_DOC_ROOT.'09-redirect.php?/';
48 |
49 | $response = test_request($url, 'GET');
50 | assert_equal($response, '/redirected');
51 |
52 | $response = test_request($url.'&key1=value1', 'GET');
53 | assert_equal($response, '/redirected&key1=value1');
54 |
55 | $response = test_request($url.'&key1=value1&key2=value2', 'GET');
56 | assert_equal($response, '/redirected&key1=value1&key2=value2');
57 | }
58 |
59 | end_test_case();
60 |
--------------------------------------------------------------------------------
/final/webapp/php/src/limonade/tests/request.php:
--------------------------------------------------------------------------------
1 | ");
10 |
11 | video.attr('src', this.ad.asset);
12 | video.attr('type',this.ad.type);
13 | video.prop('controls', true);
14 |
15 | video.css('cursor', 'pointer');
16 |
17 | video.bind('canplay', this.counterFunc());
18 | video.bind('click', this.videoClickFunc());
19 |
20 | return video;
21 | };
22 |
23 | klass.prototype.videoClickFunc = function() {
24 | var self = this;
25 | return (function(e) {
26 | if (e.target.paused) {
27 | e.target.play();
28 | } else {
29 | e.target.pause();
30 | window.open(self.ad.redirect, '_blank');
31 | }
32 | });
33 | };
34 |
35 | klass.prototype.counterFunc = function() {
36 | var self = this;
37 | return (function(e) {
38 | $.post(self.ad.counter, '', function(d,s,xhr) {
39 | console.log("Counter posted");
40 | });
41 | });
42 | };
43 |
44 | return klass;
45 | })();
46 |
47 | IsuAd.Client = (function() {
48 | var klass = function(slot, endpoint) {
49 | this.endpoint = endpoint || klass.Endpoint;
50 | this.slot = slot;
51 | };
52 |
53 | klass.Endpoint = "";
54 |
55 | klass.prototype.showAd = function(elem, callback) {
56 | var self = this;
57 | var path = self.endpoint + "/slots/" + self.slot + "/ad";
58 |
59 | $.getJSON(path, function(adData) {
60 | console.log("Ad: " + adData.title);
61 |
62 | var ad = new IsuAd.Ad(adData);
63 | console.log(ad);
64 |
65 |
66 | $(elem).html("").append(ad.generateComponent());
67 | if (callback) callback(ad);
68 | }).fail(function() {
69 | if (callback) callback();
70 | });
71 | };
72 |
73 | return klass;
74 | })();
75 |
--------------------------------------------------------------------------------
/final/webapp/public/stylesheets/index.css:
--------------------------------------------------------------------------------
1 | #topbar {
2 | margin: 0 0 20px 0;
3 | padding: 5px 20px 20px 20px;
4 | border-radius: 0 0 4px 4px;
5 | box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
6 | background-color: #efefef;
7 | }
8 |
9 | #topbar h1 {
10 | font-size: 36px;
11 | }
12 |
13 | #navs {
14 | margin-bottom: 20px;
15 | }
16 |
17 | #ad_container {
18 | margin-top: 10px;
19 | text-align: center;
20 | }
21 |
22 | #ad_container video {
23 | max-width: 60%;
24 | height: auto;
25 | }
26 |
--------------------------------------------------------------------------------
/final/webapp/python/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/final/webapp/python/README.md:
--------------------------------------------------------------------------------
1 | ```
2 | pip install flask gunicorn redis
3 | ```
4 |
--------------------------------------------------------------------------------
/final/webapp/python/gunicorn_config.py:
--------------------------------------------------------------------------------
1 | bind = '0.0.0.0:8080'
2 | workers = 10
3 |
--------------------------------------------------------------------------------
/final/webapp/python/static:
--------------------------------------------------------------------------------
1 | ../public
--------------------------------------------------------------------------------
/final/webapp/ruby/.gitignore:
--------------------------------------------------------------------------------
1 | ads/
2 | logs/
3 |
--------------------------------------------------------------------------------
/final/webapp/ruby/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'sinatra'
4 | gem 'unicorn'
5 | gem 'foreman'
6 | gem 'redis'
7 |
--------------------------------------------------------------------------------
/final/webapp/ruby/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | dotenv (0.11.1)
5 | dotenv-deployment (~> 0.0.2)
6 | dotenv-deployment (0.0.2)
7 | foreman (0.75.0)
8 | dotenv (~> 0.11.1)
9 | thor (~> 0.19.1)
10 | kgio (2.9.2)
11 | rack (1.5.2)
12 | rack-protection (1.5.3)
13 | rack
14 | raindrops (0.13.0)
15 | redis (3.1.0)
16 | sinatra (1.4.5)
17 | rack (~> 1.4)
18 | rack-protection (~> 1.4)
19 | tilt (~> 1.3, >= 1.3.4)
20 | thor (0.19.1)
21 | tilt (1.4.1)
22 | unicorn (4.8.3)
23 | kgio (~> 2.6)
24 | rack
25 | raindrops (~> 0.7)
26 |
27 | PLATFORMS
28 | ruby
29 |
30 | DEPENDENCIES
31 | foreman
32 | redis
33 | sinatra
34 | unicorn
35 |
--------------------------------------------------------------------------------
/final/webapp/ruby/Procfile:
--------------------------------------------------------------------------------
1 | unicorn: bundle exec unicorn -c unicorn_config.rb -p 8080
2 |
--------------------------------------------------------------------------------
/final/webapp/ruby/config.ru:
--------------------------------------------------------------------------------
1 | require_relative './app.rb'
2 |
3 | run Isucon4::App
4 |
--------------------------------------------------------------------------------
/final/webapp/ruby/unicorn_config.rb:
--------------------------------------------------------------------------------
1 | worker_processes 3
2 | preload_app true
3 |
--------------------------------------------------------------------------------
/qualifier/README.md:
--------------------------------------------------------------------------------
1 | # ISUCON4 オンライン予選問題
2 |
3 | - レギュレーション: [ISUCON4(2014) オンライン予選レギュレーション : ISUCON公式Blog](http://isucon.net/archives/39979344.html)
4 | - 当日マニュアル: [ISUCON4 予選当日マニュアル](https://gist.github.com/mirakui/e394ed543415852d34a6)
5 | - 予選で使われたAMI: `ami-e3577fe2`
6 |
--------------------------------------------------------------------------------
/qualifier/ami/.gitignore:
--------------------------------------------------------------------------------
1 | tmp/
2 |
--------------------------------------------------------------------------------
/qualifier/ami/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 | -I spec
4 |
--------------------------------------------------------------------------------
/qualifier/ami/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'serverspec', '~> 2.0.0.beta19'
4 | gem 'aws-sdk', '~> 1.0'
5 | gem 'thor', '~> 0.19'
6 |
--------------------------------------------------------------------------------
/qualifier/ami/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | aws-sdk (1.52.0)
5 | aws-sdk-v1 (= 1.52.0)
6 | aws-sdk-v1 (1.52.0)
7 | json (~> 1.4)
8 | nokogiri (>= 1.4.4)
9 | diff-lcs (1.2.5)
10 | json (1.8.1)
11 | mini_portile (0.6.0)
12 | net-scp (1.2.1)
13 | net-ssh (>= 2.6.5)
14 | net-ssh (2.9.1)
15 | nokogiri (1.6.3.1)
16 | mini_portile (= 0.6.0)
17 | rspec (3.0.0)
18 | rspec-core (~> 3.0.0)
19 | rspec-expectations (~> 3.0.0)
20 | rspec-mocks (~> 3.0.0)
21 | rspec-core (3.0.4)
22 | rspec-support (~> 3.0.0)
23 | rspec-expectations (3.0.4)
24 | diff-lcs (>= 1.2.0, < 2.0)
25 | rspec-support (~> 3.0.0)
26 | rspec-its (1.0.1)
27 | rspec-core (>= 2.99.0.beta1)
28 | rspec-expectations (>= 2.99.0.beta1)
29 | rspec-mocks (3.0.4)
30 | rspec-support (~> 3.0.0)
31 | rspec-support (3.0.4)
32 | serverspec (2.0.0.beta20)
33 | rspec (~> 3.0.0)
34 | rspec-its
35 | specinfra (~> 2.0.0.beta30)
36 | specinfra (2.0.0.beta46)
37 | net-scp
38 | net-ssh
39 | thor (0.19.1)
40 |
41 | PLATFORMS
42 | ruby
43 |
44 | DEPENDENCIES
45 | aws-sdk (~> 1.0)
46 | serverspec (~> 2.0.0.beta19)
47 | thor (~> 0.19)
48 |
--------------------------------------------------------------------------------
/qualifier/ami/README.md:
--------------------------------------------------------------------------------
1 | # AMI の作り方
2 |
3 | ## インスタンスを立てる
4 |
5 | 事前に AWS\_ACCESS\_KEY\_ID と AWS\_SECRET\_ACCESS\_KEY 環境変数をセットしておくこと。
6 |
7 | ```sh
8 | ./pack.rb run_instance -s [security_group_name] -k [key_name]
9 | ```
10 |
11 | このとき `tmp/instance_id` に instance_id が吐かれる。移行のコマンドはデフォルトではこれがインスタンスIDとして使われる。`-i` でも指定可能。
12 |
13 | ## セットアップ
14 |
15 | ```sh
16 | ./pack.rb provision
17 | ```
18 | Ansible で benchmarker 以外がプロビジョンされる。
19 |
20 | ```sh
21 | ./pack.rb build_benchmarker
22 | ```
23 | これで benchmarker がビルドされて `/home/isucon` に置かれる。
24 |
25 | benchmarker のビルドは重いので `provision` と分けている。
26 |
27 | ## テスト
28 |
29 | ```sh
30 | ./pack.rb spec
31 | ```
32 |
33 | Serverspec が走る。
34 |
35 | ## インスタンスに SSH で入りたいとき
36 |
37 | ```sh
38 | ./pack.rb ssh
39 | ```
40 |
41 | ## AMI を作る
42 |
43 | ```sh
44 | ./pack.rb create_image
45 | ```
46 |
47 | ## インスタンスを消す
48 |
49 | ```sh
50 | ./pack.rb terminate
51 | ```
52 |
53 | `tmp/instance_id` も消える。
54 |
55 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/.gitignore:
--------------------------------------------------------------------------------
1 | hosts
2 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/00_devel.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - yum: pkg=git state=installed
7 | - yum: pkg=gcc state=installed
8 | - yum: pkg=patch state=installed
9 | - yum: pkg=openssl-devel state=installed
10 | - yum: pkg=mysql-devel state=installed
11 | - yum: pkg=libcurl state=installed
12 | - yum: pkg=libcurl-devel state=installed
13 | - yum: pkg=bison state=installed
14 | - yum: pkg=bison-devel state=installed
15 | - yum: pkg=libjpeg-turbo state=installed
16 | - yum: pkg=libjpeg-turbo-devel state=installed
17 | - yum: pkg=libpng state=installed
18 | - yum: pkg=libpng-devel state=installed
19 | - yum: pkg=libmcrypt state=installed
20 | - yum: pkg=libmcrypt-devel state=installed
21 | - yum: pkg=readline state=installed
22 | - yum: pkg=readline-devel state=installed
23 | - yum: pkg=libtidy state=installed
24 | - yum: pkg=libtidy-devel state=installed
25 | - yum: pkg=autoconf state=installed
26 | - yum: pkg=automake state=installed
27 | - yum: pkg=libxml2-devel state=installed
28 | - yum: pkg=libxslt-devel state=installed
29 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/01_qualifier.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - user: name=isucon groups=wheel
7 | - lineinfile: dest=/etc/sudoers state=present regexp='^%wheel ALL\=' line='%wheel ALL=(ALL) NOPASSWD:ALL' validate='visudo -cf %s'
8 | - command: yum update -y
9 | - yum: pkg=nginx state=installed
10 | - service: name=nginx enabled=true
11 | - yum: pkg=mysql-server state=installed
12 | - service: name=mysqld state=running enabled=true
13 | - yum: pkg=MySQL-python state=installed
14 | - copy: src=../files/nginx.conf dest=/etc/nginx/nginx.conf owner=root mode=644
15 | notify:
16 | - reload nginx
17 | - copy: src=../files/nginx.php.conf dest=/etc/nginx/nginx.php.conf owner=root mode=644
18 | - name: copy my.cnf
19 | copy: src=../files/my.cnf dest=/etc/my.cnf owner=root mode=644
20 | notify:
21 | - restart mysqld
22 | - command: rm -rf /tmp/isucon
23 | args:
24 | removes: /tmp/isucon
25 | - synchronize: src=../../sql dest=/tmp/isucon/ recursive=yes delete=yes
26 | sudo: no
27 | - synchronize: src=../../webapp dest=/tmp/isucon/ recursive=yes delete=yes
28 | sudo: no
29 | - copy: src=../../init.sh dest=/tmp/isucon/init.sh owner=isucon mode=755
30 | - copy: src=../files/env.sh dest=/tmp/isucon/env.sh owner=isucon mode=755
31 | - copy: src=../files/bashrc dest=/home/isucon/.bashrc owner=isucon mode=755
32 | - copy: src=../files/rsync_exclude.txt dest=/tmp/rsync_exclude.txt owner=isucon mode=755
33 | - command: chown -R isucon:isucon /tmp/isucon
34 | - command: rsync -avz --delete --exclude-from=/tmp/rsync_exclude.txt /tmp/isucon/ /home/isucon/
35 | sudo: yes
36 | post_tasks:
37 | - command: ./init.sh
38 | sudo_user: isucon
39 | args:
40 | chdir: /home/isucon
41 | - mysql_user: name=isucon password=isucon priv=*.*:ALL state=present
42 | handlers:
43 | - name: restart mysqld
44 | action: service name=mysqld state=restarted
45 | - name: reload nginx
46 | action: service name=nginx state=restarted
47 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/02_supervisord.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - command: easy_install pip
7 | args:
8 | creates: /usr/bin/pip
9 | - pip: name=supervisor version=3.1.1
10 | - copy: src=../files/supervisord.init dest=/etc/init.d/supervisord owner=root mode=755
11 | - service: name=supervisord enabled=true
12 | - copy: src=../files/supervisord.conf dest=/etc/supervisord.conf owner=root mode=644
13 | notify:
14 | - restart supervisord
15 | handlers:
16 | - name: restart supervisord
17 | action: service name=supervisord state=restarted
18 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/03_xbuild.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo_user: isucon
5 | sudo: yes
6 | tasks:
7 | - command: git clone https://github.com/tagomoris/xbuild.git /home/isucon/.xbuild
8 | args:
9 | creates: /home/isucon/.xbuild/ruby-install
10 |
11 | # ruby
12 | - command: /home/isucon/.xbuild/ruby-install 2.1.3 /home/isucon/.local/ruby
13 | args:
14 | creates: /home/isucon/.local/ruby/bin/ruby
15 | - command: /home/isucon/env.sh gem install --no-rdoc --no-ri foreman
16 | args:
17 | creates: /home/isucon/.local/ruby/bin/foreman
18 | - command: /home/isucon/env.sh bundle install
19 | args:
20 | chdir: /home/isucon/webapp/ruby
21 | # node
22 | - command: /home/isucon/.xbuild/node-install v0.10.31 /home/isucon/.local/node
23 | args:
24 | creates: /home/isucon/.local/node/bin/node
25 | - command: /home/isucon/env.sh npm install
26 | args:
27 | chdir: /home/isucon/webapp/node
28 | # python
29 | - command: /home/isucon/.xbuild/python-install 2.7.8 /home/isucon/.local/python
30 | args:
31 | creates: /home/isucon/.local/python/bin/python
32 | - command: /home/isucon/env.sh pip install gunicorn Flask MySQL-python
33 | args:
34 | creates: /home/isucon/.local/python/bin/gunicorn
35 | # perl
36 | - shell: /home/isucon/.xbuild/perl-install 5.20.0 /home/isucon/.local/perl; test -x /home/isucon/.local/perl/bin/perl
37 | args:
38 | creates: /home/isucon/.local/perl/bin/perl
39 | - command: /home/isucon/env.sh carton install
40 | args:
41 | chdir: /home/isucon/webapp/perl
42 |
43 | # php
44 | - command: /home/isucon/.xbuild/php-install 5.6.0 /home/isucon/.local/php
45 | args:
46 | creates: /home/isucon/.local/php/bin/php
47 | - copy: src=../files/php.ini dest=/home/isucon/.local/php/etc/php.ini owner=isucon mode=644
48 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/04_golang.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - yum: pkg=git state=installed
7 | - yum: pkg=mercurial state=installed
8 | - yum: pkg=bzr state=installed
9 | - command: "curl -L -O http://golang.org/dl/go1.3.linux-amd64.tar.gz"
10 | args:
11 | chdir: /tmp
12 | creates: /usr/local/go
13 | - command: "tar -C /usr/local -xzf go1.3.linux-amd64.tar.gz"
14 | args:
15 | chdir: /tmp
16 | creates: /usr/local/go
17 | - copy: src=../files/golang.sh dest=/etc/profile.d/golang.sh mode=644 owner=root group=root
18 | - command: "/usr/bin/gem install --no-rdoc --no-ri gondler -v 0.2.0"
19 | args:
20 | creates: /usr/local/bin/gondler
21 | - command: /home/isucon/env.sh ./build.sh
22 | args:
23 | chdir: /home/isucon/webapp/go
24 | - command: chown -R isucon:isucon /home/isucon/gocode
25 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/05_benchmarker.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - command: rm -rf /tmp/benchmarker
7 | args:
8 | removes: /tmp/benchmarker
9 | - synchronize: src=../../benchmarker dest=/tmp/ recursive=yes delete=yes copy_links=yes
10 | sudo: no
11 | - command: chown -R isucon:isucon /tmp/benchmarker
12 | - name: build benchmarker
13 | command: /home/isucon/env.sh make release
14 | sudo_user: isucon
15 | args:
16 | chdir: /tmp/benchmarker
17 | - command: mv /tmp/benchmarker/benchmarker /home/isucon/
18 | sudo_user: isucon
19 |
--------------------------------------------------------------------------------
/qualifier/ami/ansible/_cleanup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - shell: rm -rf /tmp/*; true
7 | - shell: rm -rf /var/log/nginx/*.log
8 | args:
9 | removes: /var/log/nginx/access.log
10 | - file: path=/var/log/mysqld.log state=absent
11 | - file: path=/home/ec2-user/.ansible state=absent
12 | - file: path=/home/ec2-user/.bash_history state=absent
13 | - file: path=/home/ec2-user/.viminfo state=absent
14 | - file: path=/home/isucon/.mysql_history state=absent
15 | - file: path=/home/isucon/.bash_history state=absent
16 |
17 | - shell: rm -r /home/isucon/sql/*.old
18 | args:
19 | removes: /home/isucon/sql/dummy_users_used.tsv.old
20 |
21 | - shell: rm -r /home/isucon/sql/*.rb
22 | args:
23 | removes: /home/isucon/sql/generate_users.rb
24 |
25 | - shell: find /home/isucon/webapp -maxdepth 2 -name 'README.*' -delete
26 |
27 | - file: path=/home/isucon/php/nginx.local.conf state=absent
28 | - file: path=/home/isucon/php/Procfile state=absent
29 |
--------------------------------------------------------------------------------
/qualifier/ami/files/bashrc:
--------------------------------------------------------------------------------
1 | if [ -f /etc/bashrc ]; then
2 | . /etc/bashrc
3 | fi
4 |
5 | export PATH=/usr/local/bin:$PATH
6 | export PATH=/usr/local/go/bin:$PATH
7 | export PATH=/home/isucon/.local/ruby/bin:$PATH
8 | export PATH=/home/isucon/.local/node/bin:$PATH
9 | export PATH=/home/isucon/.local/python/bin:$PATH
10 | export PATH=/home/isucon/.local/perl/bin:$PATH
11 | export PATH=/home/isucon/.local/php/bin:$PATH
12 | export PATH=/home/isucon/.local/php/sbin:$PATH
13 | export GOPATH=/home/isucon/gocode
14 |
--------------------------------------------------------------------------------
/qualifier/ami/files/env.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export PATH=/usr/local/bin:$PATH
4 | export PATH=/usr/local/go/bin:$PATH
5 | export PATH=/home/isucon/.local/ruby/bin:$PATH
6 | export PATH=/home/isucon/.local/node/bin:$PATH
7 | export PATH=/home/isucon/.local/python/bin:$PATH
8 | export PATH=/home/isucon/.local/perl/bin:$PATH
9 | export PATH=/home/isucon/.local/php/bin:$PATH
10 | export PATH=/home/isucon/.local/php/sbin:$PATH
11 | export GOPATH=/home/isucon/gocode
12 | [ ! -d $GOPATH/src ] && mkdir -p $GOPATH/src
13 |
14 | export ISU4_SESSION_SECRET=27a4909d7cc3da7a5a07a925fbbb4d4e2b44db5b
15 | export ISU4_USER_LOCK_THRESHOLD=3
16 | export ISU4_IP_BAN_THRESHOLD=10
17 | export ISU4_DB_HOST=localhost
18 | export ISU4_DB_PORT=3306
19 | export ISU4_DB_USER=isucon
20 | export ISU4_DB_PASSWORD=isucon
21 | export ISU4_DB_NAME=isu4_qualifier
22 |
23 | exec $*
24 |
--------------------------------------------------------------------------------
/qualifier/ami/files/golang.sh:
--------------------------------------------------------------------------------
1 | export PATH=/usr/local/go/bin:$PATH
2 |
--------------------------------------------------------------------------------
/qualifier/ami/files/my.cnf:
--------------------------------------------------------------------------------
1 | [mysqld]
2 | datadir=/var/lib/mysql
3 | socket=/var/lib/mysql/mysql.sock
4 | symbolic-links=0
5 |
6 | max_allowed_packet=300M
7 |
8 | [mysqld_safe]
9 | log-error=/var/log/mysqld.log
10 | pid-file=/var/run/mysqld/mysqld.pid
11 |
--------------------------------------------------------------------------------
/qualifier/ami/files/nginx.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 |
3 | events {
4 | worker_connections 1024;
5 | }
6 |
7 | http {
8 | upstream app {
9 | server 127.0.0.1:8080;
10 | }
11 |
12 | server {
13 | location / {
14 | proxy_pass http://app;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/qualifier/ami/files/nginx.php.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 |
3 | events {
4 | worker_connections 1024;
5 | }
6 |
7 | http {
8 | include /etc/nginx/mime.types;
9 |
10 | upstream php-fpm {
11 | server 127.0.0.1:8080;
12 | }
13 |
14 | server {
15 | location ~ ^/(images|stylesheets) {
16 | root /home/isucon/webapp/public;
17 | }
18 |
19 | location / {
20 | root /home/isucon/webapp/php/src;
21 |
22 | fastcgi_pass php-fpm;
23 | fastcgi_index index.php;
24 | fastcgi_read_timeout 120;
25 |
26 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
27 | fastcgi_param QUERY_STRING $query_string;
28 | fastcgi_param REQUEST_METHOD $request_method;
29 | fastcgi_param CONTENT_TYPE $content_type;
30 | fastcgi_param CONTENT_LENGTH $content_length;
31 |
32 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
33 | fastcgi_param REQUEST_URI $request_uri;
34 | fastcgi_param DOCUMENT_URI $document_uri;
35 | fastcgi_param DOCUMENT_ROOT $document_root;
36 | fastcgi_param SERVER_PROTOCOL $server_protocol;
37 | fastcgi_param HTTPS $https if_not_empty;
38 |
39 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
40 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
41 |
42 | fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
43 | fastcgi_param REMOTE_PORT $remote_port;
44 | fastcgi_param SERVER_ADDR $server_addr;
45 | fastcgi_param SERVER_PORT $server_port;
46 | fastcgi_param SERVER_NAME $server_name;
47 |
48 | fastcgi_param REDIRECT_STATUS 200;
49 |
50 | rewrite ^(.*)$ /index.php?$1 break;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/qualifier/ami/files/rsync_exclude.txt:
--------------------------------------------------------------------------------
1 | .*
2 | gocode
3 | benchmarker
4 | webapp/perl/local
5 | webapp/go/golang-webapp
6 | *.pyc
7 |
--------------------------------------------------------------------------------
/qualifier/ami/files/supervisord.conf:
--------------------------------------------------------------------------------
1 | [unix_http_server]
2 | file=/tmp/supervisor.sock
3 | chown=root:wheel
4 | chmod=0770
5 |
6 | [supervisorctl]
7 | serverurl=unix:///tmp/supervisor.sock
8 |
9 | [supervisord]
10 | logfile=/tmp/supervisord.log
11 | loglevel=info
12 | pidfile=/var/run/supervisord.pid
13 | nodaemon=false
14 | minfds=1024
15 | minprocs=200
16 |
17 | [rpcinterface:supervisor]
18 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
19 |
20 | [program:isucon_ruby]
21 | directory=/home/isucon/webapp/ruby
22 | command=/home/isucon/env.sh foreman start
23 | user=isucon
24 | stdout_logfile=/tmp/isucon.ruby.log
25 | stderr_logfile=/tmp/isucon.ruby.log
26 | autostart=true
27 |
28 | [program:isucon_python]
29 | directory=/home/isucon/webapp/python
30 | command=/home/isucon/env.sh gunicorn -c gunicorn_config.py app:app
31 | user=isucon
32 | stdout_logfile=/tmp/isucon.python.log
33 | stderr_logfile=/tmp/isucon.python.log
34 | autostart=false
35 |
36 | [program:isucon_php]
37 | directory=/home/isucon/webapp/php
38 | command=/home/isucon/env.sh php-fpm -y /home/isucon/webapp/php/php-fpm.conf
39 | user=isucon
40 | stdout_logfile=/tmp/isucon.php.log
41 | stderr_logfile=/tmp/isucon.php.log
42 | autostart=false
43 |
44 | [program:isucon_perl]
45 | directory=/home/isucon/webapp/perl
46 | command=/home/isucon/env.sh carton exec plackup -s Starman --host localhost:8080 -E prod app.psgi
47 | user=isucon
48 | stdout_logfile=/tmp/isucon.perl.log
49 | stderr_logfile=/tmp/isucon.perl.log
50 | autostart=false
51 |
52 | [program:isucon_node]
53 | directory=/home/isucon/webapp/node
54 | command=/home/isucon/env.sh node app.js
55 | user=isucon
56 | stdout_logfile=/tmp/isucon.node.log
57 | stderr_logfile=/tmp/isucon.node.log
58 | autostart=false
59 |
60 | [program:isucon_go]
61 | directory=/home/isucon/webapp/go
62 | command=/home/isucon/env.sh ./golang-webapp
63 | user=isucon
64 | stdout_logfile=/tmp/isucon.go.log
65 | stderr_logfile=/tmp/isucon.go.log
66 | autostart=false
67 |
--------------------------------------------------------------------------------
/qualifier/ami/files/supervisord.init:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # supervisord This scripts turns supervisord on
4 | #
5 | # Author: Mike McGrath (based off yumupdatesd)
6 | #
7 | # chkconfig: - 95 04
8 | #
9 | # description: supervisor is a process control utility. It has a web based
10 | # xmlrpc interface as well as a few other nifty features.
11 | # processname: supervisord
12 | # config: /etc/supervisord.conf
13 | # pidfile: /var/run/supervisord.pid
14 | #
15 |
16 | # source function library
17 | . /etc/rc.d/init.d/functions
18 |
19 | RETVAL=0
20 | SUPERVISORD_CONF=/etc/supervisord.conf
21 |
22 | start() {
23 | echo -n $"Starting supervisord: "
24 | daemon supervisord -c $SUPERVISORD_CONF
25 | RETVAL=$?
26 | echo
27 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/supervisord
28 | }
29 |
30 | stop() {
31 | echo -n $"Stopping supervisord: "
32 | killproc supervisord
33 | echo
34 | [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/supervisord
35 | }
36 |
37 | restart() {
38 | stop
39 | start
40 | }
41 |
42 | case "$1" in
43 | start)
44 | start
45 | ;;
46 | stop)
47 | stop
48 | ;;
49 | restart|force-reload|reload)
50 | restart
51 | ;;
52 | condrestart)
53 | [ -f /var/lock/subsys/supervisord ] && restart
54 | ;;
55 | status)
56 | status supervisord
57 | RETVAL=$?
58 | ;;
59 | *)
60 | echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
61 | exit 1
62 | esac
63 |
64 | exit $RETVAL
65 |
--------------------------------------------------------------------------------
/qualifier/ami/files/user_data_benchmarker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BENCH_OPTION='--workload 2'
4 | START_WAIT=60
5 |
6 | echo > /tmp/init_log.txt
7 |
8 | (
9 | echo '---start---'
10 | date
11 | echo '---env---'
12 | env
13 | echo '---cpuinfo---'
14 | cat /proc/cpuinfo
15 | echo '---ps---'
16 | ps auxf
17 |
18 | cd /home/isucon;
19 |
20 | for i in {1..3}; do
21 | echo "---waiting $START_WAIT sec---"
22 | sleep $START_WAIT
23 |
24 | echo "---benchmarker ${i}---"
25 | /sbin/runuser -l isucon -c "./benchmarker bench $BENCH_OPTION" > /tmp/bench${i}.log
26 | done
27 |
28 | echo '---finished---'
29 | date
30 | ) >> /tmp/init_log.txt 2>&1
31 |
32 |
--------------------------------------------------------------------------------
/qualifier/ami/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'serverspec'
2 | require 'net/ssh'
3 |
4 | set :backend, :ssh
5 |
6 | if ENV['ASK_SUDO_PASSWORD']
7 | begin
8 | require 'highline/import'
9 | rescue LoadError
10 | fail "highline is not available. Try installing it."
11 | end
12 | set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
13 | else
14 | set :sudo_password, ENV['SUDO_PASSWORD']
15 | end
16 |
17 | host = ENV['TARGET_HOST']
18 |
19 | set :host, host
20 | set :ssh_options, user: 'ec2-user'
21 |
22 | Specinfra.configuration.request_pty = true
23 |
24 | # Disable sudo
25 | # set :disable_sudo, true
26 |
27 | # Set environment variables
28 | # set :env, :LANG => 'C', :LC_MESSAGES => 'C'
29 |
30 | # Set PATH
31 | # set :path, '/sbin:/usr/local/sbin:$PATH'
32 |
33 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/.gitignore:
--------------------------------------------------------------------------------
1 | benchmarker
2 | .gondler/
3 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/.rsync-filter:
--------------------------------------------------------------------------------
1 | - /.gondler
2 | - /benchmarker
3 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/Gomfile:
--------------------------------------------------------------------------------
1 | # vim: ft=ruby
2 | itself "github.com/isucon/isucon4/qualifier/benchmarker"
3 | gom 'github.com/codegangsta/cli'
4 | gom 'github.com/moovweb/gokogiri'
5 |
6 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/Makefile:
--------------------------------------------------------------------------------
1 | GIT_COMMIT=$(shell git log --oneline -n 1 . | awk '{print $$1}')
2 | DUTMH=$(shell ./md5 sql/dummy_users.tsv)
3 | DUUTMH=$(shell ./md5 sql/dummy_users_used.tsv)
4 |
5 | debugmode = false
6 | LD_FLAGS = \
7 | -X main.GIT_COMMIT \"${GIT_COMMIT}\" \
8 | -X github.com/isucon/isucon4/qualifier/benchmarker/user.DummyUsersTSVMD5 \"${DUTMH}\" \
9 | -X github.com/isucon/isucon4/qualifier/benchmarker/user.DummyUsersUsedTSVMD5 \"${DUUTMH}\" \
10 | -X github.com/isucon/isucon4/qualifier/benchmarker/user.DebugMode \"${debugmode}\" \
11 | -X main.DebugMode \"${debugmode}\" \
12 | -X main.SkipMetadataMode \"${skipmetadata}\"
13 |
14 | ifeq ($(filter true false,${debugmode}),)
15 | $(error debugmode should be true or false)
16 | endif
17 |
18 |
19 | test:
20 | gondler test -v ./...
21 |
22 | benchmarker: deps user/dummy_users.go
23 | gondler build -ldflags "${LD_FLAGS}"
24 |
25 | ./.gondler: Gomfile
26 | @echo "--> Installing build dependencies"
27 | gondler install
28 | touch .gondler
29 |
30 | deps: Gomfile ./.gondler
31 |
32 | debug: debugmode=true
33 | debug: benchmarker
34 | @tput setaf 3
35 | @echo -n '! '
36 | @tput sgr0
37 | @echo "You have enabled DEBUG mode."
38 |
39 | release: debugmode=false
40 | release: benchmarker
41 | @tput setaf 2
42 | @echo -n '* '
43 | @tput sgr0
44 | @echo "Built for release."
45 |
46 | release-nometadata: skipmetadata=true
47 | release-nometadata: debugmode=false
48 | release-nometadata: benchmarker
49 | @tput setaf 2
50 | @echo -n '* '
51 | @tput sgr0
52 | @echo "Built for release, without metadata check."
53 |
54 | .PHONEY: test release deps
55 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/README.md:
--------------------------------------------------------------------------------
1 | # ISUCON4 Qualifier Benchmarker
2 |
3 | ## Prerequisite
4 |
5 | - Gondler
6 | - `gem install gondler`
7 |
8 | ## Build
9 |
10 | ### Debug
11 |
12 | ```
13 | $ make debug
14 | ```
15 |
16 | Debug build is:
17 |
18 | - Verbose; shows request log, etc
19 | - no require of AWS EC2 environment (`http://169.254.169.254/latest/meta-data/*`)
20 | - Default value of `init.sh` is `./init.sh`
21 | - Find tsv files from `./sql`
22 | - Both depends on cwd
23 |
24 | This build shouldn't be included in AMI.
25 |
26 | ### Release
27 |
28 | ```
29 | $ make release
30 | ```
31 |
32 | Release build is:
33 |
34 | - requires to run on AWS EC2 environment.
35 | - Default value of `init.sh` is `/home/isucon/init.sh`
36 | - Find tsv files from `/home/isucon/sql`
37 |
38 | This build should be included in AMI.
39 |
40 | ## Run
41 |
42 | - Make sure TSV files is available on correct path. (debug: `./sql`, release: `/home/isucon/sql`)
43 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/ec2.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "net/http"
7 | "os"
8 | )
9 |
10 | var (
11 | instanceType string
12 | instanceId string
13 | amiId string
14 | cpuInfo string
15 | )
16 |
17 | const (
18 | infoEndpoint = "http://169.254.169.254"
19 | ExpectedInstanceType = "m3.xlarge"
20 | )
21 |
22 | func getMetaData(key string) string {
23 | res, err := http.Get(infoEndpoint + "/latest/meta-data/" + key)
24 | if err != nil {
25 | logger.Printf("type:fail\treason:meta-data error: %v", err)
26 | os.Exit(1)
27 | }
28 | if res.StatusCode == http.StatusOK {
29 | data, _ := ioutil.ReadAll(res.Body)
30 | return string(data)
31 | }
32 | logger.Printf("type:fail\treason:meta-data error: %v", err)
33 | os.Exit(1)
34 | return ""
35 | }
36 |
37 | func getCpuInfo() string {
38 | b, err := ioutil.ReadFile("/proc/cpuinfo")
39 | if err != nil {
40 | return fmt.Sprintf("%v", err)
41 | }
42 | return string(b)
43 | }
44 |
45 | func checkInstanceMetadata() {
46 | if Debug || SkipMetadata {
47 | instanceType = "local-machine"
48 | instanceId = ""
49 | amiId = ""
50 | cpuInfo = "dummy"
51 | } else {
52 | instanceType = getMetaData("instance-type")
53 | instanceId = getMetaData("instance-id")
54 | amiId = getMetaData("ami-id")
55 | cpuInfo = getCpuInfo()
56 | }
57 |
58 | if !(Debug || SkipMetadata) && instanceType != ExpectedInstanceType {
59 | logger.Printf("type:fail\treason:Instance type is miss match: %s, got: %s", ExpectedInstanceType, instanceType)
60 | os.Exit(1)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd .. && ./init.sh
4 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/ip/ip.go:
--------------------------------------------------------------------------------
1 | package ip
2 |
3 | import (
4 | "net"
5 | "sync"
6 | "sync/atomic"
7 | )
8 |
9 | type IP struct {
10 | *net.IP
11 | *sync.Mutex
12 | Failures uint32
13 | MayIncomplete bool
14 | }
15 |
16 | func NewIP(a, b, c, d byte) *IP {
17 | ip := net.IPv4(a, b, c, d)
18 | return &IP{
19 | IP: &ip,
20 | Mutex: new(sync.Mutex),
21 | Failures: 0,
22 | }
23 | }
24 |
25 | func (ip *IP) D() int {
26 | return int(ip.IP.To4()[3])
27 | }
28 |
29 | func (ip *IP) String() string {
30 | return ip.IP.String()
31 | }
32 |
33 | func (ip *IP) Success() {
34 | if ip.IsBlacklisted() {
35 | return
36 | }
37 |
38 | ip.Lock()
39 | atomic.StoreUint32(&ip.Failures, 0)
40 | ip.Unlock()
41 | }
42 |
43 | func (ip *IP) Fail() {
44 | ip.Lock()
45 | atomic.AddUint32(&ip.Failures, 1)
46 | ip.Unlock()
47 | }
48 |
49 | func (ip *IP) IsBlacklisted() bool {
50 | ip.Lock()
51 | defer ip.Unlock()
52 | return atomic.LoadUint32(&ip.Failures) >= 10
53 | }
54 |
55 | func (ip *IP) FlagIncomplete() {
56 | ip.Lock()
57 | ip.MayIncomplete = true
58 | ip.Unlock()
59 | }
60 |
61 | func (ip *IP) IsIncomplete() bool {
62 | ip.Lock()
63 | defer ip.Unlock()
64 | return ip.MayIncomplete
65 | }
66 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/ip/ip_list.go:
--------------------------------------------------------------------------------
1 | package ip
2 |
3 | import (
4 | "math/rand"
5 | "sync/atomic"
6 | )
7 |
8 | var cNum = 0
9 |
10 | var GeneratedIPList []*IPList
11 |
12 | type IPList struct {
13 | ips []*IP
14 | idx uint32
15 | }
16 |
17 | func NextIPList() *IPList {
18 | ipList := NewIPList(127, 1, byte(cNum))
19 | cNum++
20 | GeneratedIPList = append(GeneratedIPList, ipList)
21 | return ipList
22 | }
23 |
24 | func NewIPList(a, b, c byte) *IPList {
25 | ipList := &IPList{idx: 0}
26 | ipList.ips = make([]*IP, 127)
27 | ipList.idx = rand.Uint32()
28 | realc := c / 2
29 | prefix := 0
30 | if c%2 == 1 {
31 | prefix = 127
32 | }
33 |
34 | for i := 1; i < 128; i++ {
35 | ipList.ips[i-1] = NewIP(a, b, realc, byte(i+prefix))
36 | }
37 |
38 | return ipList
39 | }
40 |
41 | func (ipList *IPList) String() string {
42 | return ipList.ips[0].String() + " ... " + ipList.ips[126].String()
43 | }
44 |
45 | func (ipList *IPList) All() []*IP {
46 | return ipList.ips
47 | }
48 |
49 | func (ipList *IPList) Get() *IP {
50 | idx := int(ipList.idx)
51 | return ipList.ips[idx%len(ipList.ips)]
52 | }
53 |
54 | func (ipList *IPList) Next() *IP {
55 | forward := 1 + (rand.Uint32() % 254)
56 | atomic.AddUint32(&ipList.idx, forward)
57 | return ipList.Get()
58 | }
59 |
60 | func (ipList *IPList) IsAlmostBlacklisted() bool {
61 | blacklisted := 0
62 | notBlacklisted := 0
63 |
64 | for _, ip := range ipList.ips {
65 | if ip.IsBlacklisted() {
66 | blacklisted++
67 | } else {
68 | notBlacklisted++
69 | }
70 | }
71 |
72 | return blacklisted >= notBlacklisted
73 | }
74 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/ip/ip_list_test.go:
--------------------------------------------------------------------------------
1 | package ip
2 |
3 | import (
4 | . "testing"
5 | )
6 |
7 | func TestNewIPList(t *T) {
8 | ipList := NewIPList(127, 0, 1)
9 |
10 | if len(ipList.ips) != 254 {
11 | t.Fatal(ipList)
12 | }
13 |
14 | t.Log(ipList)
15 | }
16 |
17 | func TestIPListIsAlmostBlacklisted(t *T) {
18 | ipList := NewIPList(127, 0, 1)
19 |
20 | for i := 0; i < 127; i++ {
21 | ip := ipList.ips[i]
22 |
23 | for l := 0; l < 10; l++ {
24 | ip.Fail()
25 | }
26 | }
27 |
28 | if !ipList.IsAlmostBlacklisted() {
29 | t.Fatal("Not blacklisted")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/ip/ip_test.go:
--------------------------------------------------------------------------------
1 | package ip
2 |
3 | import (
4 | . "testing"
5 | )
6 |
7 | func TestNewIP(t *T) {
8 | ip := NewIP(127, 0, 0, 1)
9 |
10 | t.Log(ip.IP)
11 | }
12 |
13 | func TestIPD(t *T) {
14 | ip := NewIP(127, 0, 0, 1)
15 |
16 | if ip.D() != 1 {
17 | t.Fatal(ip.D())
18 | }
19 |
20 | ip = NewIP(127, 0, 0, 255)
21 |
22 | if ip.D() != 255 {
23 | t.Fatal(ip.D())
24 | }
25 | }
26 |
27 | func TestIPIsBlackListed(t *T) {
28 | ip := NewIP(127, 0, 0, 1)
29 |
30 | ip.Failures = 9
31 | ip.Success()
32 |
33 | if ip.Failures != 0 {
34 | t.Fatal(ip)
35 | }
36 |
37 | for i := 0; i < 10; i++ {
38 | ip.Fail()
39 | }
40 |
41 | if !ip.IsBlacklisted() {
42 | t.Fatal(ip.Failures)
43 | }
44 |
45 | ip.Success()
46 | if !ip.IsBlacklisted() {
47 | t.Fatal(ip.Failures)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/md5:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | FN=$1
4 |
5 | cat ${FN} | bash -c '(if [ -e /sbin/md5 ]; then md5; else md5sum -; fi)|cut -d" " -f 1'
6 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/sql/dummy_users.tsv:
--------------------------------------------------------------------------------
1 | ../../sql/dummy_users.tsv
--------------------------------------------------------------------------------
/qualifier/benchmarker/sql/dummy_users_used.tsv:
--------------------------------------------------------------------------------
1 | ../../sql/dummy_users_used.tsv
--------------------------------------------------------------------------------
/qualifier/benchmarker/user/dummy_users.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/csv"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strconv"
10 | )
11 |
12 | var (
13 | DummyUsersTSVMD5 string
14 | DummyUsersUsedTSVMD5 string
15 | DebugMode string
16 | )
17 |
18 | var DummyUsers []*User
19 |
20 | func init() {
21 | Debug := DebugMode == "true"
22 | var usersTsvPath string
23 | var usersUsedTsvPath string
24 |
25 | if Debug {
26 | usersTsvPath = "./sql/dummy_users.tsv"
27 | usersUsedTsvPath = "./sql/dummy_users_used.tsv"
28 | } else {
29 | usersTsvPath = "/home/isucon/sql/dummy_users.tsv"
30 | usersUsedTsvPath = "/home/isucon/sql/dummy_users_used.tsv"
31 | }
32 |
33 | duMD5 := getMD5(usersTsvPath)
34 | if duMD5 != DummyUsersTSVMD5 {
35 | panic(fmt.Errorf("Broken %s", usersTsvPath))
36 | }
37 |
38 | duuMD5 := getMD5(usersUsedTsvPath)
39 | if duuMD5 != DummyUsersUsedTSVMD5 {
40 | panic(fmt.Errorf("Broken %s", usersUsedTsvPath))
41 | }
42 |
43 | DummyUsers = make([]*User, 0)
44 | failureMap := map[string]string{}
45 |
46 | fp, err := os.Open(usersUsedTsvPath)
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | reader := csv.NewReader(fp)
52 | reader.Comma = '\t'
53 | reader.LazyQuotes = true
54 |
55 | for {
56 | record, err := reader.Read()
57 | if err == io.EOF {
58 | break
59 | } else if err != nil {
60 | panic(err)
61 | }
62 | failureMap[record[1]] = record[2]
63 | }
64 |
65 | fp, err = os.Open(usersTsvPath)
66 | if err != nil {
67 | panic(err)
68 | }
69 |
70 | reader = csv.NewReader(fp)
71 | reader.Comma = '\t'
72 | reader.LazyQuotes = true
73 |
74 | for {
75 | record, err := reader.Read()
76 | if err == io.EOF {
77 | break
78 | } else if err != nil {
79 | panic(err)
80 | }
81 |
82 | failureCount := 0
83 |
84 | if strcount, ok := failureMap[record[1]]; ok {
85 | failureCount, err = strconv.Atoi(strcount)
86 | if err != nil {
87 | panic(err)
88 | }
89 | }
90 |
91 | DummyUsers = append(DummyUsers, NewUser(record[1], record[2], uint32(failureCount)))
92 | }
93 | }
94 |
95 | func getMD5(filename string) string {
96 | fp, err := os.Open(filename)
97 | if err != nil {
98 | return ""
99 | }
100 |
101 | md5hash := md5.New()
102 | io.Copy(md5hash, fp)
103 | return fmt.Sprintf("%x", md5hash.Sum(nil))
104 | }
105 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/user/get_dummy_users.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "math/rand"
5 | )
6 |
7 | func GetDummyUsers(num int) []*User {
8 | users := []*User{}
9 | prefix := rand.Intn(len(DummyUsers))
10 |
11 | for i := 0; i < num; i++ {
12 | users = append(users, DummyUsers[(i+prefix)%len(DummyUsers)])
13 | }
14 |
15 | return users
16 | }
17 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/user/random_string.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "crypto/rand"
5 | )
6 |
7 | func randomString(str_size int) string {
8 | alphanum := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
9 | var bytes = make([]byte, str_size)
10 | rand.Read(bytes)
11 | for i, b := range bytes {
12 | bytes[i] = alphanum[b%byte(len(alphanum))]
13 | }
14 | return string(bytes)
15 | }
16 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/isucon/isucon4/qualifier/benchmarker/ip"
5 | "sync"
6 | "sync/atomic"
7 | "time"
8 | )
9 |
10 | type User struct {
11 | *sync.Mutex
12 | Name string
13 | RightPassword string
14 | WrongPassword string
15 | Failures uint32
16 | NowInUse bool
17 | MayIncomplete bool
18 |
19 | LastLoginedIP *ip.IP
20 | LastLoginedTime time.Time
21 | }
22 |
23 | func NewUser(name, password string, failures uint32) *User {
24 | return &User{
25 | Mutex: new(sync.Mutex),
26 | Name: name,
27 | RightPassword: password,
28 | WrongPassword: randomString(len(password) + 1),
29 | Failures: failures,
30 | MayIncomplete: false,
31 | NowInUse: false,
32 | }
33 | }
34 |
35 | func (u *User) Fail() {
36 | u.Lock()
37 | atomic.AddUint32(&u.Failures, 1)
38 | u.Unlock()
39 | }
40 |
41 | func (u *User) Success() {
42 | if u.IsBlacklisted() {
43 | return
44 | }
45 |
46 | u.Lock()
47 | atomic.StoreUint32(&u.Failures, 0)
48 | u.Unlock()
49 | }
50 |
51 | func (u *User) IsBlacklisted() bool {
52 | u.Lock()
53 | defer u.Unlock()
54 | return atomic.LoadUint32(&u.Failures) >= 3
55 | }
56 |
57 | func (u *User) InUse() bool {
58 | u.Lock()
59 | defer u.Unlock()
60 | return u.NowInUse
61 | }
62 |
63 | func (u *User) Start() {
64 | u.Lock()
65 | u.NowInUse = true
66 | u.Unlock()
67 | }
68 |
69 | func (u *User) Finish() {
70 | u.Lock()
71 | u.NowInUse = false
72 | u.Unlock()
73 | }
74 |
75 | func (u *User) FlagIncomplete() {
76 | u.Lock()
77 | u.MayIncomplete = true
78 | u.Unlock()
79 | }
80 |
81 | func (u *User) IsIncomplete() bool {
82 | u.Lock()
83 | defer u.Unlock()
84 | return u.MayIncomplete
85 | }
86 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/user/user_test.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | . "testing"
5 | )
6 |
7 | func TestNewUser(t *T) {
8 | user := NewUser("isucon1", "isucon1", 1)
9 |
10 | if user == nil {
11 | t.Fatal(user)
12 | }
13 |
14 | if user.Failures != 1 {
15 | t.Fatal(user)
16 | }
17 |
18 | if len(user.WrongPassword) != 8 {
19 | t.Fatal(user)
20 | }
21 | }
22 |
23 | func TestUserFailAndSuccess(t *T) {
24 | user := NewUser("isucon1", "isucon1", 0)
25 |
26 | user.Fail()
27 | if user.Failures != 1 {
28 | t.Fatal(user)
29 | }
30 | user.Success()
31 | if user.Failures != 0 {
32 | t.Fatal(user)
33 | }
34 | user.Failures = 3
35 | user.Success()
36 | if !user.IsBlacklisted() {
37 | t.Fatal(user)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/worker/request_test.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "net/http/httptest"
9 | . "testing"
10 | "time"
11 | )
12 |
13 | func TestWorkerExecuteRequest(t *T) {
14 | worker := New()
15 |
16 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17 | fmt.Fprintln(w, "Hello, client")
18 | }))
19 | defer ts.Close()
20 |
21 | resp, err := worker.SimpleGet(ts.URL)
22 |
23 | if err != nil {
24 | t.Fatal(err)
25 | }
26 |
27 | responseText, err := ioutil.ReadAll(resp.Body)
28 | resp.Body.Close()
29 |
30 | if err != nil {
31 | t.Fatal(err)
32 | }
33 |
34 | if bytes.Compare(responseText, []byte("Hello, client")) == 0 {
35 | t.Fatal(string(responseText))
36 | }
37 | }
38 |
39 | func TestWorkerRequestWithTimeout(t *T) {
40 | worker := New()
41 |
42 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
43 | time.Sleep(11 * time.Second)
44 | fmt.Fprintln(w, "Client timeout")
45 | }))
46 | defer ts.Close()
47 |
48 | resp, err := worker.SimpleGet(ts.URL)
49 |
50 | if err != ErrRequestTimeout {
51 | t.Fatal(resp, err)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/worker/work.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/isucon/isucon4/qualifier/benchmarker/ip"
5 | "github.com/isucon/isucon4/qualifier/benchmarker/user"
6 | "math/rand"
7 | "strconv"
8 | "time"
9 | )
10 |
11 | func (w *Worker) Work() {
12 | w.Running = true
13 | w.stoppedChan = make(chan struct{})
14 | time.AfterFunc(1*time.Minute, func() {
15 | w.Timeout()
16 | })
17 |
18 | go func() {
19 | checker := 0
20 | for w.Running {
21 | if checker%20 == 0 {
22 | if w.IPList.IsAlmostBlacklisted() {
23 | w.IPList = ip.NextIPList()
24 | }
25 |
26 | if w.IsUsersAlmostBlackListed() {
27 | w.Users = user.GetDummyUsers(100)
28 | }
29 | }
30 |
31 | ip := w.IPList.Next()
32 | user := w.Users[rand.Intn(len(w.Users))]
33 | for user.InUse() {
34 | user = w.Users[rand.Intn(len(w.Users))]
35 | }
36 |
37 | user.Start()
38 | w.Login(ip, user)
39 |
40 | if ! w.Running {
41 | user.FlagIncomplete()
42 | ip.FlagIncomplete()
43 | }
44 |
45 | user.Finish()
46 |
47 | checker++
48 | }
49 |
50 | w.stoppedChan <- struct{}{}
51 | }()
52 | }
53 |
54 | func (w *Worker) Timeout() {
55 | w.Running = false
56 | w.TimeoutDuration = 50 * time.Millisecond
57 |
58 | <-time.After(10 * time.Second)
59 | if w.nowRequest != nil {
60 | w.Transport.CancelRequest(w.nowRequest)
61 | }
62 | }
63 |
64 | func (w *Worker) Stop() {
65 | <-w.stoppedChan
66 | }
67 |
68 | func (w *Worker) IsUsersAlmostBlackListed() bool {
69 | blacklisted := 0
70 |
71 | for _, user := range w.Users {
72 | if user.IsBlacklisted() {
73 | blacklisted++
74 | }
75 | }
76 |
77 | num := len(w.Users) / 5
78 |
79 | return num < blacklisted
80 | }
81 |
82 | func (w *Worker) SendScore(apiKey string, score float64, successes, fails int32, metadata map[string]string) error {
83 | sendResult := NewScenario("POST", "/results")
84 | sendResult.Headers = map[string]string{
85 | "X-API-KEY": apiKey,
86 | }
87 | sendResult.PostData = map[string]string{
88 | "score": strconv.Itoa(int(score)),
89 | "successes": strconv.Itoa(int(successes)),
90 | "fails": strconv.Itoa(int(fails)),
91 | }
92 | for key, val := range metadata {
93 | sendResult.PostData["metadata["+key+"]"] = val
94 | }
95 | sendResult.ExpectedStatusCode = 201
96 | return sendResult.Play(w)
97 | }
98 |
--------------------------------------------------------------------------------
/qualifier/benchmarker/worker/workers.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | type Workers []*Worker
4 |
5 | func (w Workers) Work() {
6 | for _, worker := range w {
7 | worker.Work()
8 | }
9 | }
10 |
11 | func (w Workers) Stop() {
12 | for _, worker := range w {
13 | worker.Stop()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/qualifier/gcp/.gitignore:
--------------------------------------------------------------------------------
1 | tmp/
2 |
--------------------------------------------------------------------------------
/qualifier/gcp/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --format documentation
3 | -I spec
4 |
--------------------------------------------------------------------------------
/qualifier/gcp/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'serverspec'
4 | gem 'thor', '~> 0.19'
5 |
--------------------------------------------------------------------------------
/qualifier/gcp/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | diff-lcs (1.2.5)
5 | multi_json (1.11.2)
6 | net-scp (1.2.1)
7 | net-ssh (>= 2.6.5)
8 | net-ssh (2.9.2)
9 | net-telnet (0.1.1)
10 | rspec (3.3.0)
11 | rspec-core (~> 3.3.0)
12 | rspec-expectations (~> 3.3.0)
13 | rspec-mocks (~> 3.3.0)
14 | rspec-core (3.3.2)
15 | rspec-support (~> 3.3.0)
16 | rspec-expectations (3.3.1)
17 | diff-lcs (>= 1.2.0, < 2.0)
18 | rspec-support (~> 3.3.0)
19 | rspec-its (1.2.0)
20 | rspec-core (>= 3.0.0)
21 | rspec-expectations (>= 3.0.0)
22 | rspec-mocks (3.3.2)
23 | diff-lcs (>= 1.2.0, < 2.0)
24 | rspec-support (~> 3.3.0)
25 | rspec-support (3.3.0)
26 | serverspec (2.21.0)
27 | multi_json
28 | rspec (~> 3.0)
29 | rspec-its
30 | specinfra (~> 2.38)
31 | sfl (2.2)
32 | specinfra (2.40.2)
33 | net-scp
34 | net-ssh (~> 2.7)
35 | net-telnet
36 | sfl
37 | thor (0.19.1)
38 |
39 | PLATFORMS
40 | ruby
41 |
42 | DEPENDENCIES
43 | serverspec
44 | thor (~> 0.19)
45 |
46 | BUNDLED WITH
47 | 1.10.3
48 |
--------------------------------------------------------------------------------
/qualifier/gcp/README.md:
--------------------------------------------------------------------------------
1 | # ISUCON4 qualifier for GCE
2 |
3 | 事前に Google Cloud SDK (`gcloud`) をインストールしておくこと https://cloud.google.com/sdk/
4 |
5 | ## Images (built by @sorah)
6 |
7 | - `gs://shirokanezoo/isucon4/qualifier/v3.tar.gz`
8 | - No warranty and @sorah doesn't provide any supports.
9 |
10 | ## Image creation
11 |
12 | ### インスタンスを立てる
13 |
14 |
15 | ```sh
16 | ./pack.rb run_instance --project PROJECT --name NAME
17 | ```
18 |
19 | このとき `tmp/instance_info` にいろいろ吐かれる。以降のコマンドはデフォルトでこれが作業対象インスタンスの情報として使われる。
20 |
21 | ### セットアップ
22 |
23 | ```sh
24 | ./pack.rb provision
25 | ```
26 |
27 | Ansible で benchmarker 以外がプロビジョンされる。
28 |
29 | ```sh
30 | ./pack.rb build_benchmarker
31 | ```
32 |
33 | これで benchmarker がビルドされて `/home/isucon` に置かれる。
34 |
35 | benchmarker のビルドは重いので `provision` と分けている。
36 |
37 | ### テスト
38 |
39 | ```sh
40 | ./pack.rb spec
41 | ```
42 |
43 | Serverspec が走る。
44 |
45 | ### インスタンスに SSH で入りたいとき
46 |
47 | ```sh
48 | ./pack.rb ssh
49 | ```
50 |
51 | ### イメージを作る
52 |
53 | - https://cloud.google.com/compute/docs/images#export_an_image_to_google_cloud_storage
54 |
55 | ```
56 | $ export CLOUDSDK_CORE_PROJECT=PROJECT
57 |
58 | $ gcloud compute disks create $TEMPORARY_DISK --zone $ZONE
59 |
60 | $ gcloud compute instances attach-disk $YOUR_INSTANCE \
61 | $ --disk $TEMPORARY_DISK \
62 | $ --device-name temporary-disk \
63 | $ --zone $ZONE
64 |
65 | $ gcloud compute ssh $YOUR_INSTANCE
66 | remote $ rm ~/.bash_history
67 | remote $ sudo mkdir /mnt/tmp
68 | remote $ sudo /usr/share/google/safe_format_and_mount -m "mkfs.ext4 -F" /dev/disk/by-id/google-temporary-disk /mnt/tmp
69 | remote $ sudo gcimagebundle -d /dev/sda -o /mnt/tmp/ --log_file=/tmp/gcimagebundle.log
70 | remote $ gsutil cp /mnt/tmp/*.image.tar.gz gs://BUCKET_NAME/path/to/image.tar.gz
71 |
72 | ```
73 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/.gitignore:
--------------------------------------------------------------------------------
1 | hosts
2 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/00_devel.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | sudo: yes
4 | tasks:
5 | - yum: name=libselinux-python state=present
6 | - selinux: state=disabled
7 | - shell: wget http://ftp.riken.jp/Linux/fedora/epel/RPM-GPG-KEY-EPEL-6 && rpm --import RPM-GPG-KEY-EPEL-6 && rm RPM-GPG-KEY-EPEL-6
8 | - copy: src=../files/epel.repo dest=/etc/yum.repos.d/epel.repo owner=root mode=755
9 | - yum: pkg=git state=installed
10 | - yum: pkg=gcc state=installed
11 | - yum: pkg=patch state=installed
12 | - yum: pkg=openssl-devel state=installed
13 | - yum: pkg=mysql-devel state=installed
14 | - yum: pkg=libcurl state=installed
15 | - yum: pkg=libcurl-devel state=installed
16 | - yum: pkg=bison state=installed
17 | - yum: pkg=bison-devel state=installed
18 | - yum: pkg=libjpeg-turbo state=installed
19 | - yum: pkg=libjpeg-turbo-devel state=installed
20 | - yum: pkg=libpng state=installed
21 | - yum: pkg=libpng-devel state=installed
22 | - yum: pkg=libmcrypt state=installed enablerepo=epel
23 | - yum: pkg=libmcrypt-devel state=installed enablerepo=epel
24 | - yum: pkg=readline state=installed
25 | - yum: pkg=readline-devel state=installed
26 | - yum: pkg=libtidy state=installed
27 | - yum: pkg=libtidy-devel state=installed
28 | - yum: pkg=autoconf state=installed
29 | - yum: pkg=automake state=installed
30 | - yum: pkg=libxml2-devel state=installed
31 | - yum: pkg=libxslt-devel state=installed
32 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/02_supervisord.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | sudo: yes
4 | tasks:
5 | - yum: pkg=python-setuptools state=installed
6 | - command: easy_install pip
7 | args:
8 | creates: /usr/bin/pip
9 | # CentOS6 + easy_install is broken, so upgrade it over pip
10 | - command: pip install setuptools --no-use-wheel --upgrade
11 | - pip: name=meld3 version=1.0.2
12 | - pip: name=supervisor version=3.1.1
13 | - copy: src=../files/supervisord.init dest=/etc/init.d/supervisord owner=root mode=755
14 | - service: name=supervisord enabled=true
15 | - copy: src=../files/supervisord.conf dest=/etc/supervisord.conf owner=root mode=644
16 | notify:
17 | - restart supervisord
18 | handlers:
19 | - name: restart supervisord
20 | action: service name=supervisord state=restarted
21 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/03_xbuild.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | sudo: yes
4 | tasks:
5 | - command: su - isucon bash -c 'git clone https://github.com/tagomoris/xbuild.git /home/isucon/.xbuild'
6 | args:
7 | creates: /home/isucon/.xbuild/ruby-install
8 |
9 | # ruby
10 | - command: su - isucon bash -c '/home/isucon/.xbuild/ruby-install 2.1.3 /home/isucon/.local/ruby'
11 | args:
12 | creates: /home/isucon/.local/ruby/bin/ruby
13 | - command: su - isucon bash -c '/home/isucon/env.sh gem install --no-rdoc --no-ri foreman'
14 | args:
15 | creates: /home/isucon/.local/ruby/bin/foreman
16 | - command: su - isucon bash -c 'cd /home/isucon/webapp/ruby; /home/isucon/env.sh bundle install'
17 | # node
18 | - command: su - isucon bash -c '/home/isucon/.xbuild/node-install v0.10.31 /home/isucon/.local/node'
19 | args:
20 | creates: /home/isucon/.local/node/bin/node
21 | - command: su - isucon bash -c 'cd /home/isucon/webapp/node; /home/isucon/env.sh npm install'
22 | # python
23 | - command: su - isucon bash -c '/home/isucon/.xbuild/python-install 2.7.8 /home/isucon/.local/python'
24 | args:
25 | creates: /home/isucon/.local/python/bin/python
26 | - command: su - isucon bash -c '/home/isucon/env.sh pip install gunicorn Flask MySQL-python'
27 | args:
28 | creates: /home/isucon/.local/python/bin/gunicorn
29 | # perl
30 | # - shell: su - isucon bash -c '/home/isucon/.xbuild/perl-install 5.20.0 /home/isucon/.local/perl; test -x /home/isucon/.local/perl/bin/perl'
31 | - command: su - isucon bash -c '/home/isucon/.xbuild/perl-install 5.20.0 /home/isucon/.local/perl'
32 | args:
33 | creates: /home/isucon/.local/perl/bin/perl
34 | - command: su - isucon bash -c 'cd /home/isucon/webapp/perl; /home/isucon/env.sh carton install'
35 |
36 | # php
37 | - command: su - isucon bash -c '/home/isucon/.xbuild/php-install 5.6.0 /home/isucon/.local/php'
38 | args:
39 | creates: /home/isucon/.local/php/bin/php
40 | - copy: src=../files/php.ini dest=/home/isucon/.local/php/etc/php.ini owner=isucon mode=644
41 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/04_golang.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | sudo: yes
4 | tasks:
5 | - yum: pkg=git state=installed
6 | - yum: pkg=mercurial state=installed
7 | - yum: pkg=bzr state=installed
8 | - yum: pkg=ruby state=installed
9 | - yum: pkg=rubygems state=installed
10 | - command: "curl -L -O http://golang.org/dl/go1.3.linux-amd64.tar.gz"
11 | args:
12 | chdir: /tmp
13 | creates: /usr/local/go
14 | - command: "tar -C /usr/local -xzf go1.3.linux-amd64.tar.gz"
15 | args:
16 | chdir: /tmp
17 | creates: /usr/local/go
18 | - copy: src=../files/golang.sh dest=/etc/profile.d/golang.sh mode=644 owner=root group=root
19 | - command: "/usr/bin/gem install --no-rdoc --no-ri gondler -v 0.2.0"
20 | args:
21 | creates: /usr/local/bin/gondler
22 | - command: /home/isucon/env.sh ./build.sh
23 | args:
24 | chdir: /home/isucon/webapp/go
25 | - command: chown -R isucon:isucon /home/isucon/gocode
26 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/05_benchmarker.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | sudo: yes
4 | tasks:
5 | - command: rm -rf /tmp/benchmarker
6 | args:
7 | removes: /tmp/benchmarker
8 | - synchronize: src=../../benchmarker dest=/tmp/ recursive=yes delete=yes copy_links=yes
9 | sudo: no
10 | - command: chown -R isucon:isucon /tmp/benchmarker
11 | - name: build benchmarker
12 | command: su - isucon bash -c 'cd /tmp/benchmarker; /home/isucon/env.sh make release-nometadata'
13 | - command: su - isucon bash -c 'mv /tmp/benchmarker/benchmarker /home/isucon/'
14 |
--------------------------------------------------------------------------------
/qualifier/gcp/ansible/_cleanup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | remote_user: ec2-user
4 | sudo: yes
5 | tasks:
6 | - shell: rm -rf /tmp/*; true
7 | - shell: rm -rf /var/log/nginx/*.log
8 | args:
9 | removes: /var/log/nginx/access.log
10 | - file: path=/var/log/mysqld.log state=absent
11 | - file: path=/home/ec2-user/.ansible state=absent
12 | - file: path=/home/ec2-user/.bash_history state=absent
13 | - file: path=/home/ec2-user/.viminfo state=absent
14 | - file: path=/home/isucon/.mysql_history state=absent
15 | - file: path=/home/isucon/.bash_history state=absent
16 |
17 | - shell: rm -r /home/isucon/sql/*.old
18 | args:
19 | removes: /home/isucon/sql/dummy_users_used.tsv.old
20 |
21 | - shell: rm -r /home/isucon/sql/*.rb
22 | args:
23 | removes: /home/isucon/sql/generate_users.rb
24 |
25 | - shell: find /home/isucon/webapp -maxdepth 2 -name 'README.*' -delete
26 |
27 | - file: path=/home/isucon/php/nginx.local.conf state=absent
28 | - file: path=/home/isucon/php/Procfile state=absent
29 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/bashrc:
--------------------------------------------------------------------------------
1 | if [ -f /etc/bashrc ]; then
2 | . /etc/bashrc
3 | fi
4 |
5 | export PATH=/usr/local/bin:$PATH
6 | export PATH=/usr/local/go/bin:$PATH
7 | export PATH=/home/isucon/.local/ruby/bin:$PATH
8 | export PATH=/home/isucon/.local/node/bin:$PATH
9 | export PATH=/home/isucon/.local/python/bin:$PATH
10 | export PATH=/home/isucon/.local/perl/bin:$PATH
11 | export PATH=/home/isucon/.local/php/bin:$PATH
12 | export PATH=/home/isucon/.local/php/sbin:$PATH
13 | export GOPATH=/home/isucon/gocode
14 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/env.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export PATH=/usr/local/bin:$PATH
4 | export PATH=/usr/local/go/bin:$PATH
5 | export PATH=/home/isucon/.local/ruby/bin:$PATH
6 | export PATH=/home/isucon/.local/node/bin:$PATH
7 | export PATH=/home/isucon/.local/python/bin:$PATH
8 | export PATH=/home/isucon/.local/perl/bin:$PATH
9 | export PATH=/home/isucon/.local/php/bin:$PATH
10 | export PATH=/home/isucon/.local/php/sbin:$PATH
11 | export GOPATH=/home/isucon/gocode
12 | [ ! -d $GOPATH/src ] && mkdir -p $GOPATH/src
13 |
14 | export ISU4_SESSION_SECRET=27a4909d7cc3da7a5a07a925fbbb4d4e2b44db5b
15 | export ISU4_USER_LOCK_THRESHOLD=3
16 | export ISU4_IP_BAN_THRESHOLD=10
17 | export ISU4_DB_HOST=localhost
18 | export ISU4_DB_PORT=3306
19 | export ISU4_DB_USER=isucon
20 | export ISU4_DB_PASSWORD=isucon
21 | export ISU4_DB_NAME=isu4_qualifier
22 |
23 | exec $*
24 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/epel.repo:
--------------------------------------------------------------------------------
1 | [epel]
2 | name=EPEL RPM Repository for Red Hat Enterprise Linux
3 | baseurl=http://ftp.riken.jp/Linux/fedora/epel/6/$basearch/
4 | gpgcheck=1
5 | enabled=0
--------------------------------------------------------------------------------
/qualifier/gcp/files/golang.sh:
--------------------------------------------------------------------------------
1 | export PATH=/usr/local/go/bin:$PATH
2 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/my.cnf:
--------------------------------------------------------------------------------
1 | [mysqld]
2 | datadir=/var/lib/mysql
3 | socket=/var/lib/mysql/mysql.sock
4 | symbolic-links=0
5 |
6 | max_allowed_packet=300M
7 |
8 | [mysqld_safe]
9 | log-error=/var/log/mysqld.log
10 | pid-file=/var/run/mysqld/mysqld.pid
11 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/nginx.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 |
3 | events {
4 | worker_connections 1024;
5 | }
6 |
7 | http {
8 | upstream app {
9 | server 127.0.0.1:8080;
10 | }
11 |
12 | server {
13 | location / {
14 | proxy_pass http://app;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/nginx.php.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 |
3 | events {
4 | worker_connections 1024;
5 | }
6 |
7 | http {
8 | include /etc/nginx/mime.types;
9 |
10 | upstream php-fpm {
11 | server 127.0.0.1:8080;
12 | }
13 |
14 | server {
15 | location ~ ^/(images|stylesheets) {
16 | root /home/isucon/webapp/public;
17 | }
18 |
19 | location / {
20 | root /home/isucon/webapp/php/src;
21 |
22 | fastcgi_pass php-fpm;
23 | fastcgi_index index.php;
24 | fastcgi_read_timeout 120;
25 |
26 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
27 | fastcgi_param QUERY_STRING $query_string;
28 | fastcgi_param REQUEST_METHOD $request_method;
29 | fastcgi_param CONTENT_TYPE $content_type;
30 | fastcgi_param CONTENT_LENGTH $content_length;
31 |
32 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
33 | fastcgi_param REQUEST_URI $request_uri;
34 | fastcgi_param DOCUMENT_URI $document_uri;
35 | fastcgi_param DOCUMENT_ROOT $document_root;
36 | fastcgi_param SERVER_PROTOCOL $server_protocol;
37 | fastcgi_param HTTPS $https if_not_empty;
38 |
39 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
40 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
41 |
42 | fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
43 | fastcgi_param REMOTE_PORT $remote_port;
44 | fastcgi_param SERVER_ADDR $server_addr;
45 | fastcgi_param SERVER_PORT $server_port;
46 | fastcgi_param SERVER_NAME $server_name;
47 |
48 | fastcgi_param REDIRECT_STATUS 200;
49 |
50 | rewrite ^(.*)$ /index.php?$1 break;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/rsync_exclude.txt:
--------------------------------------------------------------------------------
1 | .*
2 | gocode
3 | benchmarker
4 | webapp/perl/local
5 | webapp/go/golang-webapp
6 | *.pyc
7 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/supervisord.conf:
--------------------------------------------------------------------------------
1 | [unix_http_server]
2 | file=/tmp/supervisor.sock
3 | chown=root:wheel
4 | chmod=0770
5 |
6 | [supervisorctl]
7 | serverurl=unix:///tmp/supervisor.sock
8 |
9 | [supervisord]
10 | logfile=/tmp/supervisord.log
11 | loglevel=info
12 | pidfile=/var/run/supervisord.pid
13 | nodaemon=false
14 | minfds=1024
15 | minprocs=200
16 |
17 | [rpcinterface:supervisor]
18 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
19 |
20 | [program:isucon_ruby]
21 | directory=/home/isucon/webapp/ruby
22 | command=/home/isucon/env.sh foreman start
23 | user=isucon
24 | stdout_logfile=/tmp/isucon.ruby.log
25 | stderr_logfile=/tmp/isucon.ruby.log
26 | autostart=true
27 |
28 | [program:isucon_python]
29 | directory=/home/isucon/webapp/python
30 | command=/home/isucon/env.sh gunicorn -c gunicorn_config.py app:app
31 | user=isucon
32 | stdout_logfile=/tmp/isucon.python.log
33 | stderr_logfile=/tmp/isucon.python.log
34 | autostart=false
35 |
36 | [program:isucon_php]
37 | directory=/home/isucon/webapp/php
38 | command=/home/isucon/env.sh php-fpm -y /home/isucon/webapp/php/php-fpm.conf
39 | user=isucon
40 | stdout_logfile=/tmp/isucon.php.log
41 | stderr_logfile=/tmp/isucon.php.log
42 | autostart=false
43 |
44 | [program:isucon_perl]
45 | directory=/home/isucon/webapp/perl
46 | command=/home/isucon/env.sh carton exec plackup -s Starman --host localhost:8080 -E prod app.psgi
47 | user=isucon
48 | stdout_logfile=/tmp/isucon.perl.log
49 | stderr_logfile=/tmp/isucon.perl.log
50 | autostart=false
51 |
52 | [program:isucon_node]
53 | directory=/home/isucon/webapp/node
54 | command=/home/isucon/env.sh node app.js
55 | user=isucon
56 | stdout_logfile=/tmp/isucon.node.log
57 | stderr_logfile=/tmp/isucon.node.log
58 | autostart=false
59 |
60 | [program:isucon_go]
61 | directory=/home/isucon/webapp/go
62 | command=/home/isucon/env.sh ./golang-webapp
63 | user=isucon
64 | stdout_logfile=/tmp/isucon.go.log
65 | stderr_logfile=/tmp/isucon.go.log
66 | autostart=false
67 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/supervisord.init:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # supervisord This scripts turns supervisord on
4 | #
5 | # Author: Mike McGrath (based off yumupdatesd)
6 | #
7 | # chkconfig: - 95 04
8 | #
9 | # description: supervisor is a process control utility. It has a web based
10 | # xmlrpc interface as well as a few other nifty features.
11 | # processname: supervisord
12 | # config: /etc/supervisord.conf
13 | # pidfile: /var/run/supervisord.pid
14 | #
15 |
16 | # source function library
17 | . /etc/rc.d/init.d/functions
18 |
19 | RETVAL=0
20 | SUPERVISORD_CONF=/etc/supervisord.conf
21 |
22 | start() {
23 | echo -n $"Starting supervisord: "
24 | daemon supervisord -c $SUPERVISORD_CONF
25 | RETVAL=$?
26 | echo
27 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/supervisord
28 | }
29 |
30 | stop() {
31 | echo -n $"Stopping supervisord: "
32 | killproc supervisord
33 | echo
34 | [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/supervisord
35 | }
36 |
37 | restart() {
38 | stop
39 | start
40 | }
41 |
42 | case "$1" in
43 | start)
44 | start
45 | ;;
46 | stop)
47 | stop
48 | ;;
49 | restart|force-reload|reload)
50 | restart
51 | ;;
52 | condrestart)
53 | [ -f /var/lock/subsys/supervisord ] && restart
54 | ;;
55 | status)
56 | status supervisord
57 | RETVAL=$?
58 | ;;
59 | *)
60 | echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
61 | exit 1
62 | esac
63 |
64 | exit $RETVAL
65 |
--------------------------------------------------------------------------------
/qualifier/gcp/files/user_data_benchmarker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | BENCH_OPTION='--workload 2'
4 | START_WAIT=60
5 |
6 | echo > /tmp/init_log.txt
7 |
8 | (
9 | echo '---start---'
10 | date
11 | echo '---env---'
12 | env
13 | echo '---cpuinfo---'
14 | cat /proc/cpuinfo
15 | echo '---ps---'
16 | ps auxf
17 |
18 | cd /home/isucon;
19 |
20 | for i in {1..3}; do
21 | echo "---waiting $START_WAIT sec---"
22 | sleep $START_WAIT
23 |
24 | echo "---benchmarker ${i}---"
25 | /sbin/runuser -l isucon -c "./benchmarker bench $BENCH_OPTION" > /tmp/bench${i}.log
26 | done
27 |
28 | echo '---finished---'
29 | date
30 | ) >> /tmp/init_log.txt 2>&1
31 |
32 |
--------------------------------------------------------------------------------
/qualifier/gcp/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'serverspec'
2 |
3 | set :backend, :ssh
4 |
5 | if ENV['ASK_SUDO_PASSWORD']
6 | begin
7 | require 'highline/import'
8 | rescue LoadError
9 | fail "highline is not available. Try installing it."
10 | end
11 | set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
12 | else
13 | set :sudo_password, ENV['SUDO_PASSWORD']
14 | end
15 |
16 | host = ENV['TARGET_HOST']
17 | key = ENV['PRIVATE_KEY_PATH']
18 |
19 | set :host, host
20 | set :ssh_options, user: ENV['USER'], keys: [key], user_known_hosts_file: '/dev/null'
21 |
22 | Specinfra.configuration.request_pty = true
23 |
24 | # Disable sudo
25 | # set :disable_sudo, true
26 |
27 | # Set environment variables
28 | # set :env, :LANG => 'C', :LC_MESSAGES => 'C'
29 |
30 | # Set PATH
31 | # set :path, '/sbin:/usr/local/sbin:$PATH'
32 |
33 |
--------------------------------------------------------------------------------
/qualifier/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -x
3 | set -e
4 | cd $(dirname $0)
5 |
6 | myuser=root
7 | mydb=isu4_qualifier
8 | myhost=127.0.0.1
9 | myport=3306
10 | mysql -h ${myhost} -P ${myport} -u ${myuser} -e "DROP DATABASE IF EXISTS ${mydb}; CREATE DATABASE ${mydb}"
11 | mysql -h ${myhost} -P ${myport} -u ${myuser} ${mydb} < sql/schema.sql
12 | mysql -h ${myhost} -P ${myport} -u ${myuser} ${mydb} < sql/dummy_users.sql
13 | mysql -h ${myhost} -P ${myport} -u ${myuser} ${mydb} < sql/dummy_log.sql
14 |
--------------------------------------------------------------------------------
/qualifier/sql/.gitignore:
--------------------------------------------------------------------------------
1 | *.old
2 |
--------------------------------------------------------------------------------
/qualifier/sql/generate_users.rb:
--------------------------------------------------------------------------------
1 | require 'optparse'
2 | require 'faker'
3 | require 'digest/sha2'
4 |
5 | $count = 20
6 | opt = OptionParser.new(ARGV)
7 | opt.on('--count count') {|x| $count = x.to_i }
8 | opt.parse!
9 |
10 |
11 | usernames = {}
12 |
13 | sql = open(File.join(__dir__, 'dummy_users.sql'), 'w')
14 | tsv = open(File.join(__dir__, 'dummy_users.tsv'), 'w')
15 |
16 | sql.puts "INSERT INTO `users` (`id`, `login`, `password_hash`, `salt`) VALUES"
17 |
18 | (1..$count).each do |i|
19 | if i < 10
20 | user = 'isucon%d' % i
21 | pass = 'isuconpass%d' % i
22 | salt = 'salt%d' % i
23 | else
24 | user = Faker::Internet.user_name
25 | while usernames[user]
26 | user = user.succ
27 | end
28 |
29 | pass = Faker::Internet.password
30 | salt = Faker::Internet.password
31 | end
32 | usernames[user] = true
33 |
34 | hash = Digest::SHA256.hexdigest("#{pass}:#{salt}")
35 |
36 | sql.print ',' if i > 1
37 | sql.puts "(#{i}, '#{user}', SHA2('#{pass}:#{salt}', 256), '#{salt}')"
38 | tsv.puts "#{i}\t#{user}\t#{pass}\t#{salt}\t#{hash}"
39 | end
40 |
41 | sql.close
42 | tsv.close
43 |
--------------------------------------------------------------------------------
/qualifier/sql/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS `users` (
2 | `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
3 | `login` varchar(255) NOT NULL UNIQUE,
4 | `password_hash` varchar(255) NOT NULL,
5 | `salt` varchar(255) NOT NULL
6 | ) DEFAULT CHARSET=utf8;
7 |
8 | CREATE TABLE IF NOT EXISTS `login_log` (
9 | `id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY,
10 | `created_at` datetime NOT NULL,
11 | `user_id` int,
12 | `login` varchar(255) NOT NULL,
13 | `ip` varchar(255) NOT NULL,
14 | `succeeded` tinyint NOT NULL
15 | ) DEFAULT CHARSET=utf8;
16 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/.gitignore:
--------------------------------------------------------------------------------
1 | gin-bin
2 | golang-webapp
3 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/README.mkd:
--------------------------------------------------------------------------------
1 | # ISUCON4 qualifier Golang implementation
2 |
3 | ## How to build and run
4 |
5 | ```shel
6 | $ ./build.sh
7 | $ ./golang-webapp
8 | ```
9 |
10 | ## How to development
11 |
12 | ```shell
13 | $ ./build.sh
14 | $ go get github.com/codegangsta/gin
15 | $ gin
16 | ```
17 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | go get github.com/go-martini/martini
4 | go get github.com/go-sql-driver/mysql
5 | go get github.com/martini-contrib/render
6 | go get github.com/martini-contrib/sessions
7 | go build -o golang-webapp .
8 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/templates/index.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | 偽画面にご注意ください!
4 |
5 |
6 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
7 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
8 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
9 |
10 |
11 |
12 |
15 |
16 | {{ if .Flash }}
17 | {{ .Flash }}
18 | {{ end }}
19 |
20 |
41 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/templates/layout.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | isucon4
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ yield }}
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/templates/mypage.tmpl:
--------------------------------------------------------------------------------
1 |
2 | ログインに成功しました。
3 | 未読のお知らせが0件、残っています。
4 |
5 |
6 |
7 | 前回ログイン
8 | {{ .LastLogin.CreatedAt.Format "2006-01-02 15:04:05" }}
9 | 最終ログインIPアドレス
10 | {{ .LastLogin.IP }}
11 |
12 |
13 |
14 |
15 | お客様ご契約ID:{{ .LastLogin.Login }} 様の代表口座
16 |
17 |
18 |
19 |
20 | 普通預金
21 | 東京支店 1111111111
22 |
23 |
28 |
29 |
35 |
36 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/user.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type User struct {
8 | ID int
9 | Login string
10 | PasswordHash string
11 | Salt string
12 |
13 | LastLogin *LastLogin
14 | }
15 |
16 | type LastLogin struct {
17 | Login string
18 | IP string
19 | CreatedAt time.Time
20 | }
21 |
22 | func (u *User) getLastLogin() *LastLogin {
23 | rows, err := db.Query(
24 | "SELECT login, ip, created_at FROM login_log WHERE succeeded = 1 AND user_id = ? ORDER BY id DESC LIMIT 2",
25 | u.ID,
26 | )
27 |
28 | if err != nil {
29 | return nil
30 | }
31 |
32 | defer rows.Close()
33 | for rows.Next() {
34 | u.LastLogin = &LastLogin{}
35 | err = rows.Scan(&u.LastLogin.Login, &u.LastLogin.IP, &u.LastLogin.CreatedAt)
36 | if err != nil {
37 | u.LastLogin = nil
38 | return nil
39 | }
40 | }
41 |
42 | return u.LastLogin
43 | }
44 |
--------------------------------------------------------------------------------
/qualifier/webapp/go/util.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/sha256"
5 | "fmt"
6 | "github.com/martini-contrib/sessions"
7 | "io"
8 | "os"
9 | )
10 |
11 | func getEnv(key string, def string) string {
12 | v := os.Getenv(key)
13 | if len(v) == 0 {
14 | return def
15 | }
16 |
17 | return v
18 | }
19 |
20 | func getFlash(session sessions.Session, key string) string {
21 | value := session.Get(key)
22 |
23 | if value == nil {
24 | return ""
25 | } else {
26 | session.Delete(key)
27 | return value.(string)
28 | }
29 | }
30 |
31 | func calcPassHash(password, hash string) string {
32 | h := sha256.New()
33 | io.WriteString(h, password)
34 | io.WriteString(h, ":")
35 | io.WriteString(h, hash)
36 |
37 | return fmt.Sprintf("%x", h.Sum(nil))
38 | }
39 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/README.mkd:
--------------------------------------------------------------------------------
1 | # ISUCON4 qualifier Node.js implementation
2 |
3 | ## How to build and run
4 |
5 | ```shel
6 | $ npm install
7 | $ node app.js
8 | ```
9 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "isucon4-node",
3 | "description": "for ISUCON4 qualifier",
4 | "version": "0.0.1",
5 | "private": true,
6 | "dependencies": {
7 | "async": "^0.9.0",
8 | "body-parser": "^1.8.2",
9 | "ect": "^0.5.9",
10 | "express": "4.x",
11 | "express-session": "^1.8.2",
12 | "morgan": "^1.3.1",
13 | "mysql": "^2.5.0",
14 | "strftime": "^0.8.2",
15 | "underscore": "^1.7.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/views/index.ect:
--------------------------------------------------------------------------------
1 | <% extend 'layout' %>
2 |
3 |
4 | 偽画面にご注意ください!
5 |
6 |
7 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
8 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
9 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
10 |
11 |
12 |
13 |
16 |
17 | <% if @notice : %>
18 | <%= @notice %>
19 | <% end %>
20 |
21 |
42 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/views/layout.ect:
--------------------------------------------------------------------------------
1 |
2 | <% @title ||= 'isucon4' %>
3 |
4 |
5 |
6 |
7 |
8 |
9 | <%= @title %>
10 |
11 |
12 |
13 |
14 |
15 |
16 | <% content %>
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/qualifier/webapp/node/views/mypage.ect:
--------------------------------------------------------------------------------
1 | <% extend 'layout' %>
2 |
3 | ログインに成功しました。
4 | 未読のお知らせが0件、残っています。
5 |
6 |
7 |
8 | 前回ログイン
9 | <%= @strftime('%Y-%m-%d %H:%M:%S', @last_login.created_at) %>
10 | 最終ログインIPアドレス
11 | <%= @last_login.ip %>
12 |
13 |
14 |
15 |
16 | お客様ご契約ID:<%= @last_login.login %> 様の代表口座
17 |
18 |
19 |
20 |
21 | 普通預金
22 | 東京支店 1111111111
23 |
24 |
29 |
30 |
36 |
37 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/.gitignore:
--------------------------------------------------------------------------------
1 | local/
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/app.psgi:
--------------------------------------------------------------------------------
1 | use FindBin;
2 | use lib "$FindBin::Bin/extlib/lib/perl5";
3 | use lib "$FindBin::Bin/lib";
4 | use File::Basename;
5 | use Plack::Builder;
6 | use Isu4Qualifier::Web;
7 | use Plack::Session::State::Cookie;
8 | use Plack::Session::Store::File;
9 |
10 | my $root_dir = File::Basename::dirname(__FILE__);
11 | my $session_dir = "/tmp/isu4_session_plack";
12 | mkdir $session_dir;
13 |
14 | my $app = Isu4Qualifier::Web->psgi($root_dir);
15 | builder {
16 | enable 'ReverseProxy';
17 | enable 'Static',
18 | path => qr!^/(?:stylesheets|images)/!,
19 | root => $root_dir . '/public';
20 | enable 'Session',
21 | state => Plack::Session::State::Cookie->new(
22 | httponly => 1,
23 | session_key => "isu4_session",
24 | ),
25 | store => Plack::Session::Store::File->new(
26 | dir => $session_dir,
27 | ),
28 | ;
29 | $app;
30 | };
31 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/cpanfile:
--------------------------------------------------------------------------------
1 | requires "Kossy", 0.19;
2 | requires "DBIx::Sunny";
3 | requires "JSON::XS";
4 | requires "Digest::SHA";
5 | requires "File::Temp";
6 | requires "Time::Piece";
7 |
8 | requires "DBD::mysql";
9 | requires "Starman";
10 | requires "Plack::Session";
11 | requires "Data::Dumper";
12 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/lib/Isu4Qualifier.pm:
--------------------------------------------------------------------------------
1 | package Isu4Qualifier;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 |
7 | our $VERSION = 0.01;
8 |
9 | 1;
10 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/public:
--------------------------------------------------------------------------------
1 | ../public
--------------------------------------------------------------------------------
/qualifier/webapp/perl/views/base.tx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | isucon4
9 |
10 |
11 |
12 |
13 |
14 |
15 | : block content -> { }
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/views/index.tx:
--------------------------------------------------------------------------------
1 | : cascade base
2 | : around content -> {
3 |
4 |
5 | 偽画面にご注意ください!
6 |
7 |
8 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
9 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
10 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
11 |
12 |
13 |
14 |
17 |
18 | : if defined $flash {
19 | <: $flash :>
20 | : }
21 |
22 |
43 | : }
44 |
--------------------------------------------------------------------------------
/qualifier/webapp/perl/views/mypage.tx:
--------------------------------------------------------------------------------
1 | : cascade base
2 | : around content -> {
3 |
4 | ログインに成功しました。
5 | 未読のお知らせが0件、残っています。
6 |
7 |
8 |
9 | 前回ログイン
10 | <: $last_login.created_at :>
11 | 最終ログインIPアドレス
12 | <: $last_login.ip :>
13 |
14 |
15 |
16 |
17 | お客様ご契約ID:<: $last_login.login :> 様の代表口座
18 |
19 |
20 |
21 |
22 | 普通預金
23 | 東京支店 1111111111
24 |
25 |
30 |
31 |
37 |
38 |
41 |
42 |
43 |
44 | : }
45 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/Procfile:
--------------------------------------------------------------------------------
1 | nginx: nginx -p ./ -c ./nginx.local.conf
2 | php: PATH=/usr/local/sbin:$PATH php-fpm -y ./php-fpm.conf
3 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/README.md:
--------------------------------------------------------------------------------
1 | ## Run
2 |
3 | ### on ISUCON AMI
4 |
5 | ```
6 | sudo cp /etc/nginx/nginx.conf{,.orig}
7 | sudo cp ./nginx.conf /etc/nginx/
8 | sudo /etc/init.d/nginx reload
9 | ```
10 |
11 | ### Local
12 |
13 | ```
14 | foreman start
15 | open http://localhost:8080/
16 | ```
17 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/nginx.local.conf:
--------------------------------------------------------------------------------
1 | worker_processes 1;
2 | daemon off;
3 |
4 | events {
5 | worker_connections 1024;
6 | }
7 |
8 | http {
9 | access_log /dev/stdout;
10 | error_log stderr notice;
11 |
12 | upstream php-fpm {
13 | server localhost:8080;
14 | }
15 |
16 | server {
17 | listen 0.0.0.0:8081 default;
18 |
19 | location ~ ^/(images|stylesheets) {
20 | root ../public;
21 | }
22 |
23 | location / {
24 | # set your src path
25 | root /Users/issei-naruta/src/isucon4/qualifier/webapp/php/src;
26 |
27 | fastcgi_pass php-fpm;
28 | fastcgi_index index.php;
29 | fastcgi_read_timeout 120;
30 |
31 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
32 | fastcgi_param QUERY_STRING $query_string;
33 | fastcgi_param REQUEST_METHOD $request_method;
34 | fastcgi_param CONTENT_TYPE $content_type;
35 | fastcgi_param CONTENT_LENGTH $content_length;
36 |
37 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
38 | fastcgi_param REQUEST_URI $request_uri;
39 | fastcgi_param DOCUMENT_URI $document_uri;
40 | fastcgi_param DOCUMENT_ROOT $document_root;
41 | fastcgi_param SERVER_PROTOCOL $server_protocol;
42 | fastcgi_param HTTPS $https if_not_empty;
43 |
44 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
45 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
46 |
47 | fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
48 | fastcgi_param REMOTE_PORT $remote_port;
49 | fastcgi_param SERVER_ADDR $server_addr;
50 | fastcgi_param SERVER_PORT $server_port;
51 | fastcgi_param SERVER_NAME $server_name;
52 |
53 | fastcgi_param REDIRECT_STATUS 200;
54 |
55 |
56 | rewrite ^(.*)$ /index.php?$1 break;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/php-fpm.conf:
--------------------------------------------------------------------------------
1 | daemonize = no
2 |
3 | [www]
4 | ;user = isucon
5 | ;group = isucon
6 | listen = 0.0.0.0:8080
7 |
8 | pm = dynamic
9 | pm.max_children = 10
10 | pm.start_servers = 10
11 | pm.min_spare_servers = 10
12 | pm.max_spare_servers = 10
13 | pm.process_idle_timeout = 10s;
14 | pm.max_requests = 500
15 | pm.status_path = /status
16 | ping.path = /ping
17 |
18 | catch_workers_output = true
19 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/info.php:
--------------------------------------------------------------------------------
1 | .
2 | The limonade logo and other design stuffs were created by MiniMau .
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Fabrice Luraine
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sofadesign/limonade",
3 | "description": "a PHP micro-framework",
4 | "homepage": "https://github.com/sofadesign/limonade",
5 | "keywords": ["microframework"],
6 | "autoload": {
7 | "files": ["lib/limonade.php"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Configuration_and_Options.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Configuration_and_Options.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Contributing_and_Support.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Contributing_and_Support.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Examples.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Examples.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Getting_Started.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Getting_Started.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Routing.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Routing.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/docs/Views_and_Helpers.markdown:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/docs/Views_and_Helpers.markdown
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example01/public/soda_glass.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/examples/example01/public/soda_glass.jpg
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example01/public/soda_glass.thb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/examples/example01/public/soda_glass.thb.jpg
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example03/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | Options +FollowSymlinks
3 | Options +Indexes
4 | RewriteEngine on
5 |
6 |
7 | # test string is a valid files
8 | RewriteCond %{SCRIPT_FILENAME} !-f
9 | # test string is a valid directory
10 | RewriteCond %{SCRIPT_FILENAME} !-d
11 |
12 | RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
13 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example03/index.php:
--------------------------------------------------------------------------------
1 | \n";
25 | $output .= "";
28 | return $output;
29 | }
30 |
31 | # defaults work the same as always...
32 | dispatch('/', 'hello_world');
33 | function hello_world()
34 | {
35 | return "Hello world!";
36 | }
37 |
38 | # able to pass options to routes, which are also available in the 'before' filter in the $route argument
39 | dispatch('/account', 'user_account',
40 | array("authenticate" => TRUE));
41 | function user_account()
42 | {
43 | return "You are authenticated (or rather, you would be if the 'authenticate_user' was real)";
44 | }
45 |
46 | # sometimes there param validation rules that are difficult or impossible to implement as a regex
47 | # Here is an example of attaching a validation function to a route.
48 | # Call this with /validate/1234 to pass, or with any other argument to fail
49 | dispatch('/validate/*', 'validate_test',
50 | array('validation_function' => 'a_silly_validation_function'));
51 | function validate_test(){
52 | return "Yup! You've passed the validation test";
53 | }
54 | run();
55 |
56 | //------------------------------
57 | // Utilities
58 | //------------------------------
59 | function authenticate_user()
60 | {
61 | //auth the user here...
62 | return true;
63 | }
64 |
65 |
66 | function a_silly_validation_function($params)
67 | {
68 | //perhaps this looks something up in the database...
69 | if (isset($params[0]) && $params[0] == "1234") return true;
70 |
71 | return false;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example04/index.php:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
2 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
3 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
4 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
5 | dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
6 | sunt in culpa qui officia deserunt mollit anim id est laborum.
7 |
8 |
9 | This content is available in the layout with the
10 | $sidebar
variable
11 |
12 |
13 | Exeros quisque modiam aliquipsum facincilit min.
14 | Eliquatum amconsendre quatet aciliqu consecte lore.
15 | Dolum feugue faccum.
16 | Eui lan landignibh, feugiamet nullam modit volorerci, eros nosto sequam veliquisit.
17 |
18 |
19 | Enibh ullan utpationse turpis velisim ullut alismolum.
20 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example05/views/layout.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example 05:
6 |
7 |
8 |
9 |
10 |
11 |
Main content
12 |
13 |
14 |
17 |
18 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/example06/index.php:
--------------------------------------------------------------------------------
1 | added before render…";
30 | }
31 |
32 | return array($content_or_func, $layout, $locals, $view_path);
33 | }
34 |
35 | /**
36 | * a filter for rewriting views without short_open_tags
37 | *
38 | * @param string $view_path
39 | * @return string $path to converted view file
40 | */
41 | function render_filter_rewrite_short_tags($view_path)
42 | {
43 | if (option('rewrite_short_tags') == true && (boolean)@ini_get('short_open_tag') === false)
44 | {
45 | # cache path, maybe in tmp/cache/rsot_views/
46 | $cache_path = file_path(option('rewrite_sot_cache_dir'), $view_path);
47 | if(!file_exists($cache_path) || (filemtime($cache_path) != filemtime($view_path)))
48 | {
49 | $view = file_get_contents($view_path);
50 | $transformed_view = preg_replace("/;*\s*\?>/", "; ?>", str_replace('=', 'Hellooo!');
63 | }
64 |
65 | dispatch('/error', 'index_error');
66 | function index_error()
67 | {
68 | return halt('Error!');
69 | }
70 |
71 | run();
72 |
73 | # _INLINE templates___________________________________________________________
74 |
75 | function html_default_layout($vars){ extract($vars);?>
76 |
77 |
78 |
79 |
80 | Before render filter test
81 |
82 |
83 |
84 | =$content;?>
85 |
86 |
87 |
88 | Menu:
89 | Index |
90 | Error
91 |
92 |
93 |
94 |
95 |
96 | };
97 |
98 | ?>
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/index.php:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | Limonade examples
8 |
9 |
10 |
11 |
12 |
13 | Limonade examples
14 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/urlrewrite/htaccess.conf:
--------------------------------------------------------------------------------
1 |
2 | Options +FollowSymlinks
3 | Options +Indexes
4 | RewriteEngine on
5 |
6 | # if your app is in a subfolder
7 | # RewriteBase /my_app/
8 |
9 | # test string is a valid files
10 | RewriteCond %{SCRIPT_FILENAME} !-f
11 | # test string is a valid directory
12 | RewriteCond %{SCRIPT_FILENAME} !-d
13 |
14 | RewriteRule ^(.*)$ index.php?uri=/$1 [NC,L,QSA]
15 | # with QSA flag (query string append),
16 | # forces the rewrite engine to append a query string part of the
17 | # substitution string to the existing string, instead of replacing it.
18 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/urlrewrite/lighttpd.conf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/examples/urlrewrite/lighttpd.conf
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/examples/urlrewrite/nginx.conf:
--------------------------------------------------------------------------------
1 | # If you are using Nginx, add the following to your server declaration,
2 | # and restart your Nginx.
3 |
4 | # ATTENTION:
5 | # Besides editing your Nginx configuation, REMEMBER to
6 | # set the option('base_uri') in your configure() function (for example):
7 | # option('base_uri', '/');
8 | # Then the function url_for() will work properly (for example).
9 | # url_for('one', 'two'); # returns /one/two instead of ?/one/two
10 |
11 | server {
12 | location / {
13 |
14 | try_files $uri $uri/ @rewrite;
15 | }
16 | location @rewrite {
17 | rewrite ^/(.*)$ /index.php?u=$1&$args;
18 | }
19 | }
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/lib/limonade/public/img/bg_header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/lib/limonade/public/img/bg_header.png
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/lib/limonade/views/_debug.html.php:
--------------------------------------------------------------------------------
1 | ENV_PRODUCTION && option('debug')): ?>
2 |
3 | []
4 | (in line )
5 |
6 |
7 |
8 |
9 |
10 | Debug arguments
11 |
12 |
13 |
14 | Options
15 |
16 | [ ↑ ]
17 |
18 | Environment
19 |
20 | [ ↑ ]
21 |
22 | Backtrace
23 |
24 | [ ↑ ]
25 |
26 |
36 |
37 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/lib/limonade/views/_notices.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
→ Notices and warnings
4 |
5 |
6 | []
7 |
8 | in
9 | line
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/lib/limonade/views/default_layout.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Limonade, the fizzy PHP micro-framework
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/lib/limonade/views/error.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/00-empty.php:
--------------------------------------------------------------------------------
1 |
72 |
73 |
74 |
75 |
78 | my content
79 |
80 | sidebar
81 |
82 | HTML" ;
17 | }
18 | else
19 | {
20 | return 'Oops' ;
21 | }
22 | }
23 |
24 | run();
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/06-session.php:
--------------------------------------------------------------------------------
1 | Hellooo!');
14 | }
15 |
16 | dispatch('/two', 'index_two');
17 | function index_two()
18 | {
19 | flash('notice', 'ON DISPLAY 3');
20 | return html('Hellooo!
');
21 | }
22 | dispatch('/three', 'index_three');
23 | function index_three()
24 | {
25 | flash('error', 'ON DISPLAY 4');
26 | return html('Hellooo!
');
27 | }
28 | dispatch('/four', 'index_four');
29 | function index_four()
30 | {
31 | return html('NO FLASH MESSAGE ON NEXT PAGE
');
32 | }
33 | dispatch('/five', 'index_five');
34 | function index_five()
35 | {
36 | flash('error', 'ON DISPLAY 6');
37 | redirect_to('six');
38 | }
39 | dispatch('/six', 'index_six');
40 | function index_six()
41 | {
42 | return html('REDIRECTED FROM INDEX FIVE...
There will be no flash message on next page.
');
43 | }
44 |
45 |
46 |
47 | run();
48 |
49 | # _INLINE templates___________________________________________________________
50 |
51 | function html_default_layout($vars){ extract($vars);?>
52 |
53 |
54 |
55 |
56 | Flash features test
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Current flash messages ( flash_now() / $flash )
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Menu:
74 | One |
75 | Two |
76 | Three |
77 | Four |
78 | Five |
79 | Six
80 |
81 |
82 |
83 |
84 | 1 ? $keyAndValue[1] : '';
13 | }
14 | array_shift($params);
15 |
16 | return redirect_to('/redirected', $params);
17 | }
18 |
19 | dispatch('/redirected', 'redirected');
20 | function redirected()
21 | {
22 | print $_SERVER['QUERY_STRING'];
23 | }
24 |
25 | run();
26 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/views/hello_world.html.php:
--------------------------------------------------------------------------------
1 | Hello World
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/views/hello_world_filtered.html.php:
--------------------------------------------------------------------------------
1 | Hello World Filtered
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/apps/views/layouts/default.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Page title
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/config/config.php.dist:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/data/deer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/tests/data/deer.jpg
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/data/empty_text_file.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/php/src/limonade/tests/data/empty_text_file.txt
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/data/lib0/a.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/data/lib0/b.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/data/lib0/c.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/helpers/show_request_uri.php:
--------------------------------------------------------------------------------
1 | HTML", $response);
40 |
41 | $response = test_request(TESTS_DOC_ROOT.'05-content_negociation.php', 'GET', false, array(), array("Accept: application/json"));
42 | assert_equal("json", $response);
43 | }
44 |
45 | function test_http_redirect()
46 | {
47 | $url = TESTS_DOC_ROOT.'09-redirect.php?/';
48 |
49 | $response = test_request($url, 'GET');
50 | assert_equal($response, '/redirected');
51 |
52 | $response = test_request($url.'&key1=value1', 'GET');
53 | assert_equal($response, '/redirected&key1=value1');
54 |
55 | $response = test_request($url.'&key1=value1&key2=value2', 'GET');
56 | assert_equal($response, '/redirected&key1=value1&key2=value2');
57 | }
58 |
59 | end_test_case();
60 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/limonade/tests/request.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | isucon4
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/views/index.html.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | 偽画面にご注意ください!
4 |
5 |
6 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
7 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
8 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
9 |
10 |
11 |
12 |
15 |
16 |
20 |
21 |
24 |
25 |
46 |
--------------------------------------------------------------------------------
/qualifier/webapp/php/src/views/mypage.html.php:
--------------------------------------------------------------------------------
1 |
2 | ログインに成功しました。
3 | 未読のお知らせが0件、残っています。
4 |
5 |
6 |
7 | 前回ログイン
8 |
9 | 最終ログインIPアドレス
10 |
11 |
12 |
13 |
14 |
15 | お客様ご契約ID: 様の代表口座
16 |
17 |
18 |
19 |
20 | 普通預金
21 | 東京支店 1111111111
22 |
23 |
28 |
29 |
35 |
36 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/qualifier/webapp/public/images/isucon-bank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/isucon/isucon4/018f7c95fa6b8b6ad5f398e63274145bf408c4e6/qualifier/webapp/public/images/isucon-bank.png
--------------------------------------------------------------------------------
/qualifier/webapp/public/stylesheets/isucon-bank.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes blink {
2 | from {
3 | opacity: 1;
4 | }
5 | to {
6 | opacity: 0;
7 | }
8 | }
9 |
10 | .container {
11 | max-width: 630px;
12 | }
13 |
14 | #topbar {
15 | margin: 0 0 20px 0;
16 | padding: 50px 20px 20px 20px;
17 | border-radius: 0 0 4px 4px;
18 | box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
19 | background-color: #efefef;
20 | }
21 |
22 | #be-careful-phising .panel-heading {
23 | font-size: 150%;
24 | }
25 |
26 | .hikaru-mozi {
27 | -webkit-animation-name: blink;
28 | -webkit-animation-duration: 0.5s;
29 | -webkit-animation-iteration-count: infinite;
30 | -webkit-animation-timing-function: linear;
31 | -webkit-animation-direction: alternate;
32 | -webkit-animation-delay: 0s;
33 | }
34 |
35 | #zandaka {
36 | margin: 0;
37 | font-size: 300%;
38 | font-weight: bold;
39 | }
40 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/README.md:
--------------------------------------------------------------------------------
1 | ```
2 | pip install flask gunicorn PyMySQL3
3 | ```
4 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/gunicorn_config.py:
--------------------------------------------------------------------------------
1 | bind = '0.0.0.0:8080'
2 | workers = 10
3 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/static:
--------------------------------------------------------------------------------
1 | ../public
--------------------------------------------------------------------------------
/qualifier/webapp/python/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | isucon4
9 |
10 |
11 |
12 |
13 |
14 |
15 | {% block content %}{% endblock %}
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 |
6 | 偽画面にご注意ください!
7 |
8 |
9 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
10 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
11 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
12 |
13 |
14 |
15 |
18 |
19 | {% with messages = get_flashed_messages() %}
20 | {% if messages %}
21 | {{ messages[0] }}
22 | {% endif %}
23 | {% endwith %}
24 |
25 |
46 | {% endblock %}
47 |
--------------------------------------------------------------------------------
/qualifier/webapp/python/templates/mypage.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 | ログインに成功しました。
6 | 未読のお知らせが0件、残っています。
7 |
8 |
9 |
10 | 前回ログイン
11 | {{ last_login['created_at'].strftime("%Y-%m-%d %H:%M:%S") }}
12 | 最終ログインIPアドレス
13 | {{ last_login['ip'] }}
14 |
15 |
16 |
17 |
18 | お客様ご契約ID:{{ user['login'] }} 様の代表口座
19 |
20 |
21 |
22 |
23 | 普通預金
24 | 東京支店 1111111111
25 |
26 |
31 |
32 |
38 |
39 |
42 |
43 |
44 |
45 | {% endblock %}
46 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/Gemfile:
--------------------------------------------------------------------------------
1 | # A sample Gemfile
2 | source "https://rubygems.org"
3 |
4 | gem "sinatra"
5 | gem "mysql2-cs-bind"
6 | gem "rack-flash3"
7 | gem "unicorn"
8 | gem "foreman"
9 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | dotenv (0.11.1)
5 | dotenv-deployment (~> 0.0.2)
6 | dotenv-deployment (0.0.2)
7 | foreman (0.74.0)
8 | dotenv (~> 0.11.1)
9 | thor (~> 0.19.1)
10 | kgio (2.9.2)
11 | mysql2 (0.3.16)
12 | mysql2-cs-bind (0.0.6)
13 | mysql2
14 | rack (1.5.2)
15 | rack-flash3 (1.0.5)
16 | rack
17 | rack-protection (1.5.3)
18 | rack
19 | raindrops (0.13.0)
20 | sinatra (1.4.5)
21 | rack (~> 1.4)
22 | rack-protection (~> 1.4)
23 | tilt (~> 1.3, >= 1.3.4)
24 | thor (0.19.1)
25 | tilt (1.4.1)
26 | unicorn (4.8.3)
27 | kgio (~> 2.6)
28 | rack
29 | raindrops (~> 0.7)
30 |
31 | PLATFORMS
32 | ruby
33 |
34 | DEPENDENCIES
35 | foreman
36 | mysql2-cs-bind
37 | rack-flash3
38 | sinatra
39 | unicorn
40 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/Procfile:
--------------------------------------------------------------------------------
1 | unicorn: bundle exec unicorn -c unicorn_config.rb -p 8080
2 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/config.ru:
--------------------------------------------------------------------------------
1 | require_relative './app.rb'
2 |
3 | run Isucon4::App
4 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/unicorn_config.rb:
--------------------------------------------------------------------------------
1 | worker_processes 10
2 | preload_app true
3 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/views/base.erb:
--------------------------------------------------------------------------------
1 |
2 | <% title ||= 'isucon4' %>
3 |
4 |
5 |
6 |
7 |
8 |
9 | <%= title %>
10 |
11 |
12 |
13 |
14 |
15 |
16 | <%= yield %>
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/views/index.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 | 偽画面にご注意ください!
4 |
5 |
6 |
偽のログイン画面を表示しお客様の情報を盗み取ろうとする犯罪が多発しています。
7 |
ログイン直後にダウンロード中や、見知らぬウィンドウが開いた場合、 すでにウィルスに感染している場合がございます。即座に取引を中止してください。
8 |
また、残高照会のみなど、必要のない場面で乱数表の入力を求められても、 絶対に入力しないでください。
9 |
10 |
11 |
12 |
15 |
16 | <% if flash[:notice] %>
17 | <%= flash[:notice] %>
18 | <% end %>
19 |
20 |
41 |
--------------------------------------------------------------------------------
/qualifier/webapp/ruby/views/mypage.erb:
--------------------------------------------------------------------------------
1 |
2 | ログインに成功しました。
3 | 未読のお知らせが0件、残っています。
4 |
5 |
6 |
7 | 前回ログイン
8 | <%= last_login['created_at'].strftime("%Y-%m-%d %H:%M:%S") %>
9 | 最終ログインIPアドレス
10 | <%= last_login['ip'] %>
11 |
12 |
13 |
14 |
15 | お客様ご契約ID:<%= last_login['login'] %> 様の代表口座
16 |
17 |
18 |
19 |
20 | 普通預金
21 | 東京支店 1111111111
22 |
23 |
28 |
29 |
35 |
36 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------