├── 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 | 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: <?php echo h($page_title) ?> 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 | 85 |
86 |
87 | 93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /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 |
27 | 28 | 29 | Debug arguments | 30 | 31 | Options | 32 | Environment | 33 | Backtrace | 34 | [ ↑ ] 35 |
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 | 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 | 18 | {{ end }} 19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
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 | 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 |
24 |

25 | ―――円 26 |

27 |
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 | 19 | <% end %> 20 | 21 |
22 |
23 |
24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 |
32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
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 | 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 |
25 |

26 | ―――円 27 |

28 |
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 | 20 | : } 21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | : } 44 | -------------------------------------------------------------------------------- /qualifier/webapp/perl/views/mypage.tx: -------------------------------------------------------------------------------- 1 | : cascade base 2 | : around content -> { 3 | 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 |
26 |

27 | ―――円 28 |

29 |
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: <?php echo h($page_title) ?> 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 | 85 |
86 |
87 | 93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /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 |
27 | 28 | 29 | Debug arguments | 30 | 31 | Options | 32 | Environment | 33 | Backtrace | 34 | [ ↑ ] 35 |
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 | 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 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 | -------------------------------------------------------------------------------- /qualifier/webapp/php/src/views/mypage.html.php: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 |
前回ログイン
8 |
9 |
最終ログインIPアドレス
10 |
11 |
12 | 13 |
14 |
15 | お客様ご契約ID: 様の代表口座 16 |
17 |
18 |
19 |
20 | 普通預金
21 | 東京支店 1111111111
22 |
23 |
24 |

25 | ―――円 26 |

27 |
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 | 22 | {% endif %} 23 | {% endwith %} 24 | 25 |
26 |
27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /qualifier/webapp/python/templates/mypage.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | 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 |
27 |

28 | ―――円 29 |

30 |
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 | 18 | <% end %> 19 | 20 |
21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /qualifier/webapp/ruby/views/mypage.erb: -------------------------------------------------------------------------------- 1 | 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 |
24 |

25 | ―――円 26 |

27 |
28 | 29 | 35 | 36 | 39 |
40 |
41 |
42 | --------------------------------------------------------------------------------