├── .github_ └── workflows │ ├── create-ami.yml │ ├── create_base_ami.yml │ ├── go.yml │ ├── node.yml │ ├── perl.yml │ ├── php.yml │ ├── python.yml │ ├── ruby.yml │ └── rust.yml ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── bench ├── .gitignore ├── Dockerfile.dev ├── Dockerfile.prod ├── Makefile ├── assets │ ├── data │ │ └── .gitignore │ ├── load.go │ └── load_test.go ├── bin │ └── .gitignore ├── cmd │ └── bench │ │ ├── bench.go │ │ ├── benchmarker.go │ │ ├── id_ed25519.pub │ │ ├── main.go │ │ ├── reboot.go │ │ ├── s3.go │ │ ├── slack.go │ │ ├── sqs.go │ │ └── supervise.go ├── go.mod ├── go.sum ├── internal │ ├── attacker │ │ └── dns.go │ ├── bencherror │ │ ├── error.go │ │ └── message.go │ ├── benchscore │ │ ├── counter.go │ │ └── profit.go │ ├── config │ │ ├── benchmark.go │ │ ├── config.go │ │ ├── finalcheck.go │ │ ├── initialdata.go │ │ ├── pretest.go │ │ ├── reservation.go │ │ ├── supervise.go │ │ └── webapp.go │ ├── logger │ │ └── logger.go │ ├── pubsub │ │ ├── pubsub.go │ │ └── pubsub_test.go │ ├── resolver │ │ ├── dns.go │ │ └── native.go │ └── scheduler │ │ ├── commit_state.go │ │ ├── icon_scheduler.go │ │ ├── images │ │ ├── 1.jpg │ │ ├── 10.jpg │ │ ├── 100.jpg │ │ ├── 101.jpg │ │ ├── 102.jpg │ │ ├── 103.jpg │ │ ├── 104.jpg │ │ ├── 105.jpg │ │ ├── 106.jpg │ │ ├── 107.jpg │ │ ├── 108.jpg │ │ ├── 109.jpg │ │ ├── 11.jpg │ │ ├── 110.jpg │ │ ├── 111.jpg │ │ ├── 112.jpg │ │ ├── 113.jpg │ │ ├── 114.jpg │ │ ├── 115.jpg │ │ ├── 116.jpg │ │ ├── 117.jpg │ │ ├── 118.jpg │ │ ├── 119.jpg │ │ ├── 12.jpg │ │ ├── 120.jpg │ │ ├── 121.jpg │ │ ├── 122.jpg │ │ ├── 123.jpg │ │ ├── 124.jpg │ │ ├── 125.jpg │ │ ├── 126.jpg │ │ ├── 127.jpg │ │ ├── 128.jpg │ │ ├── 129.jpg │ │ ├── 13.jpg │ │ ├── 130.jpg │ │ ├── 131.jpg │ │ ├── 132.jpg │ │ ├── 133.jpg │ │ ├── 134.jpg │ │ ├── 135.jpg │ │ ├── 136.jpg │ │ ├── 137.jpg │ │ ├── 138.jpg │ │ ├── 139.jpg │ │ ├── 14.jpg │ │ ├── 140.jpg │ │ ├── 141.jpg │ │ ├── 142.jpg │ │ ├── 143.jpg │ │ ├── 144.jpg │ │ ├── 145.jpg │ │ ├── 146.jpg │ │ ├── 147.jpg │ │ ├── 148.jpg │ │ ├── 149.jpg │ │ ├── 15.jpg │ │ ├── 150.jpg │ │ ├── 151.jpg │ │ ├── 152.jpg │ │ ├── 153.jpg │ │ ├── 154.jpg │ │ ├── 155.jpg │ │ ├── 156.jpg │ │ ├── 157.jpg │ │ ├── 158.jpg │ │ ├── 159.jpg │ │ ├── 16.jpg │ │ ├── 160.jpg │ │ ├── 161.jpg │ │ ├── 162.jpg │ │ ├── 163.jpg │ │ ├── 164.jpg │ │ ├── 165.jpg │ │ ├── 166.jpg │ │ ├── 167.jpg │ │ ├── 168.jpg │ │ ├── 169.jpg │ │ ├── 17.jpg │ │ ├── 170.jpg │ │ ├── 171.jpg │ │ ├── 172.jpg │ │ ├── 173.jpg │ │ ├── 174.jpg │ │ ├── 175.jpg │ │ ├── 176.jpg │ │ ├── 177.jpg │ │ ├── 178.jpg │ │ ├── 179.jpg │ │ ├── 18.jpg │ │ ├── 180.jpg │ │ ├── 181.jpg │ │ ├── 182.jpg │ │ ├── 183.jpg │ │ ├── 184.jpg │ │ ├── 185.jpg │ │ ├── 186.jpg │ │ ├── 187.jpg │ │ ├── 188.jpg │ │ ├── 189.jpg │ │ ├── 19.jpg │ │ ├── 190.jpg │ │ ├── 2.jpg │ │ ├── 20.jpg │ │ ├── 21.jpg │ │ ├── 22.jpg │ │ ├── 23.jpg │ │ ├── 24.jpg │ │ ├── 25.jpg │ │ ├── 26.jpg │ │ ├── 27.jpg │ │ ├── 28.jpg │ │ ├── 29.jpg │ │ ├── 3.jpg │ │ ├── 30.jpg │ │ ├── 31.jpg │ │ ├── 32.jpg │ │ ├── 33.jpg │ │ ├── 34.jpg │ │ ├── 35.jpg │ │ ├── 36.jpg │ │ ├── 37.jpg │ │ ├── 38.jpg │ │ ├── 39.jpg │ │ ├── 4.jpg │ │ ├── 40.jpg │ │ ├── 41.jpg │ │ ├── 42.jpg │ │ ├── 43.jpg │ │ ├── 44.jpg │ │ ├── 45.jpg │ │ ├── 46.jpg │ │ ├── 47.jpg │ │ ├── 48.jpg │ │ ├── 49.jpg │ │ ├── 5.jpg │ │ ├── 50.jpg │ │ ├── 51.jpg │ │ ├── 52.jpg │ │ ├── 53.jpg │ │ ├── 54.jpg │ │ ├── 55.jpg │ │ ├── 56.jpg │ │ ├── 57.jpg │ │ ├── 58.jpg │ │ ├── 59.jpg │ │ ├── 6.jpg │ │ ├── 60.jpg │ │ ├── 61.jpg │ │ ├── 62.jpg │ │ ├── 63.jpg │ │ ├── 64.jpg │ │ ├── 65.jpg │ │ ├── 66.jpg │ │ ├── 67.jpg │ │ ├── 68.jpg │ │ ├── 69.jpg │ │ ├── 7.jpg │ │ ├── 70.jpg │ │ ├── 71.jpg │ │ ├── 72.jpg │ │ ├── 73.jpg │ │ ├── 74.jpg │ │ ├── 75.jpg │ │ ├── 76.jpg │ │ ├── 77.jpg │ │ ├── 78.jpg │ │ ├── 79.jpg │ │ ├── 8.jpg │ │ ├── 80.jpg │ │ ├── 81.jpg │ │ ├── 82.jpg │ │ ├── 83.jpg │ │ ├── 84.jpg │ │ ├── 85.jpg │ │ ├── 86.jpg │ │ ├── 87.jpg │ │ ├── 88.jpg │ │ ├── 89.jpg │ │ ├── 9.jpg │ │ ├── 90.jpg │ │ ├── 91.jpg │ │ ├── 92.jpg │ │ ├── 93.jpg │ │ ├── 94.jpg │ │ ├── 95.jpg │ │ ├── 96.jpg │ │ ├── 97.jpg │ │ ├── 98.jpg │ │ └── 99.jpg │ │ ├── initial.go │ │ ├── initial_livecomment_pool.go │ │ ├── initial_ngword_pool.go │ │ ├── initial_reaction_pool.go │ │ ├── interval_temperature.go │ │ ├── interval_temperature_test.go │ │ ├── livecomment_pool.go │ │ ├── livecomment_scheduler.go │ │ ├── livestream_tags_pool.go │ │ ├── livestreams_pool.go │ │ ├── reaction_pool.go │ │ ├── reservation.go │ │ ├── reservation_pool.go │ │ ├── reservation_scheduler.go │ │ ├── reservation_scheduler_test.go │ │ ├── reservation_test.go │ │ ├── stats_scheduler.go │ │ ├── stats_scheduler_test.go │ │ ├── tag_pool.go │ │ ├── user_pool.go │ │ └── user_scheduler.go ├── isupipe │ ├── agent_option.go │ ├── client.go │ ├── client_initialize.go │ ├── client_livecomment.go │ ├── client_livecomment_test.go │ ├── client_livestream.go │ ├── client_livestream_test.go │ ├── client_options.go │ ├── client_payment.go │ ├── client_payment_test.go │ ├── client_reaction.go │ ├── client_reaction_test.go │ ├── client_stats.go │ ├── client_stats_test.go │ ├── client_test.go │ ├── client_top.go │ ├── client_user.go │ ├── client_user_test.go │ ├── main_test.go │ ├── pool.go │ └── validate_response.go └── scenario │ ├── attacker.go │ ├── core_finalcheck.go │ ├── core_pretest.go │ ├── core_pretest_abnormal.go │ ├── core_pretest_calc.go │ ├── core_pretest_dnsrecord.go │ ├── core_pretest_initial.go │ ├── core_pretest_normal.go │ ├── streamer.go │ ├── testdata │ └── NoImage.jpg │ ├── viewer.go │ └── visit_page.go ├── development ├── Makefile ├── docker-compose-common.yml ├── docker-compose-go.yml ├── docker-compose-node.yml ├── docker-compose-perl.yml ├── docker-compose-php.yml ├── docker-compose-python.yml ├── docker-compose-ruby.yml ├── docker-compose-rust.yml ├── etc │ └── nginx │ │ └── conf.d │ │ └── nginx.conf ├── pdns │ └── 20_powerdns_schema.sql └── php │ ├── etc │ └── nginx │ │ └── conf.d │ │ └── nginx.conf │ └── usr │ └── local │ └── etc │ └── php-fpm.d │ └── zz-docker.conf ├── docs ├── cautionary_note.md ├── isupipe.md ├── isupipe.yaml ├── specification.md └── transplant.md ├── envcheck ├── .gitignore ├── Makefile ├── aws.go ├── check.go ├── checker.go ├── data.go ├── go.mod ├── go.sum ├── main.go └── portal.go ├── frontend ├── .eslintrc.yml ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .vscode │ └── settings.json ├── .yarnrc.yml ├── Makefile ├── README.md ├── index.html ├── package.json ├── scripts │ └── openapi │ │ ├── generate-api-client.ts │ │ ├── package.json │ │ └── tsconfig.build.json ├── src │ ├── @types │ │ └── global.d.ts │ ├── App.tsx │ ├── api │ │ ├── apiClient.ts │ │ ├── client.ts │ │ ├── hooks.tsx │ │ ├── icon.ts │ │ ├── types.ts │ │ └── url.ts │ ├── assets │ │ ├── img │ │ │ ├── BBB.webp │ │ │ ├── hiru.webp │ │ │ ├── hkd.webp │ │ │ ├── isucon11_final.webp │ │ │ ├── isucon12_final.webp │ │ │ ├── isucon12_final_live.webp │ │ │ ├── isucon13.webp │ │ │ ├── isucon9.webp │ │ │ ├── timewarp.webp │ │ │ ├── ube.webp │ │ │ └── yoru.webp │ │ └── index.tsx │ ├── components │ │ ├── account │ │ │ └── iconmodal.tsx │ │ ├── console │ │ │ ├── newlive.tsx │ │ │ └── ngword.tsx │ │ ├── layout │ │ │ ├── ISUPipe_yoko_color.png │ │ │ ├── Layout.tsx │ │ │ └── loginmodal.tsx │ │ ├── reaction │ │ │ └── reaction.tsx │ │ ├── toast │ │ │ └── toast.tsx │ │ └── video │ │ │ ├── about.tsx │ │ │ ├── comment.tsx │ │ │ ├── thumbnail.tsx │ │ │ └── video.tsx │ ├── index.css │ ├── main.tsx │ ├── pages │ │ ├── account │ │ │ ├── login.tsx │ │ │ └── signup.tsx │ │ ├── console │ │ │ ├── index.tsx │ │ │ └── live │ │ │ │ └── [id].tsx │ │ ├── index.tsx │ │ ├── search.tsx │ │ ├── user.tsx │ │ └── watch │ │ │ └── [id].tsx │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts ├── vitest.config.ts └── yarn.lock ├── provisioning ├── ansible │ ├── README.md │ ├── ansible.cfg │ ├── application-base.yml │ ├── application-deploy.yml │ ├── application.yml │ ├── benchmark.yml │ ├── inventory │ │ ├── hosts.yaml │ │ ├── localhost │ │ └── sacloud │ ├── make_latest_files.sh │ ├── roles │ │ ├── apt │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── base │ │ │ ├── files │ │ │ │ └── pubkey.conf │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── bench │ │ │ ├── files │ │ │ │ └── .gitignore │ │ │ └── tasks │ │ │ │ └── main.yaml │ │ ├── envcheck │ │ │ ├── files │ │ │ │ ├── .gitignore │ │ │ │ ├── envcheck.service │ │ │ │ ├── run-isucon-env-checker.sh │ │ │ │ └── warmup.sh │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── globalip │ │ │ ├── files │ │ │ │ ├── aws-env-isucon-subdomain-address.service │ │ │ │ └── aws-env-isucon-subdomain-address.sh │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── isuadmin-user │ │ │ ├── files │ │ │ │ └── authorized_keys │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── isucon-user │ │ │ ├── tasks │ │ │ │ └── main.yml │ │ │ └── templates │ │ │ │ └── env.sh │ │ ├── mysql │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── nginx │ │ │ ├── files │ │ │ │ └── etc │ │ │ │ │ └── nginx │ │ │ │ │ ├── sites-available │ │ │ │ │ └── isupipe-php.conf │ │ │ │ │ ├── sites-enabled │ │ │ │ │ └── isupipe.conf │ │ │ │ │ └── tls │ │ │ │ │ ├── _.t.isucon.dev.crt │ │ │ │ │ ├── _.t.isucon.dev.key │ │ │ │ │ ├── _.u.isucon.dev.crt │ │ │ │ │ ├── _.u.isucon.dev.issuer.crt │ │ │ │ │ └── _.u.isucon.dev.key │ │ │ └── tasks │ │ │ │ └── main.yaml │ │ ├── powerdns │ │ │ ├── files │ │ │ │ ├── auth-48 │ │ │ │ ├── pdns.conf │ │ │ │ ├── pdns.d │ │ │ │ │ ├── docker.conf │ │ │ │ │ └── gmysql-host.conf │ │ │ │ ├── pdns.list │ │ │ │ └── resolved.conf │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── webapp │ │ │ ├── files │ │ │ │ ├── .gitignore │ │ │ │ ├── isupipe-go.service │ │ │ │ ├── isupipe-node.service │ │ │ │ ├── isupipe-perl.service │ │ │ │ ├── isupipe-php.service │ │ │ │ ├── isupipe-python.service │ │ │ │ ├── isupipe-ruby.service │ │ │ │ ├── isupipe-rust.service │ │ │ │ └── isupipe.php-fpm.conf │ │ │ └── tasks │ │ │ │ ├── go.yaml │ │ │ │ ├── main.yaml │ │ │ │ ├── node.yaml │ │ │ │ ├── perl.yaml │ │ │ │ ├── php.yaml │ │ │ │ ├── python.yaml │ │ │ │ ├── ruby.yaml │ │ │ │ └── rust.yaml │ │ ├── xbuild │ │ │ ├── files │ │ │ │ ├── .local.env │ │ │ │ └── .x │ │ │ └── tasks │ │ │ │ └── main.yml │ │ └── xbuildwebapp │ │ │ ├── files │ │ │ └── rustup-init.sh │ │ │ └── tasks │ │ │ └── main.yml │ └── sandbox.ini └── packer │ ├── Makefile │ ├── isucon13.pkr.hcl │ └── isucon13_base_image.pkr.hcl ├── scripts ├── Pipfile ├── Pipfile.lock ├── README.md ├── generate_initial_livecomment.py ├── generate_initial_ngwords.py ├── generate_initial_reaction.py ├── generate_livecomment.py ├── generate_livestream_reservation.py ├── generate_livestream_reservation_slot.py ├── generate_livestream_tags.pl ├── generate_users.py └── initial-data │ ├── autogenerated_user_livestream_foreignkey_pairs.json │ ├── bench_dummy_ngwords.txt │ ├── bench_ngwords.txt │ ├── display_names.txt │ ├── emoji.txt │ ├── initial_ngwords.txt │ ├── negative_formats.txt │ └── positive_sentence.txt ├── validated ├── after │ ├── 10.json │ ├── 102.json │ ├── 12.json │ ├── 14.json │ ├── 157.json │ ├── 166.json │ ├── 176.json │ ├── 18.json │ ├── 187.json │ ├── 205.json │ ├── 217.json │ ├── 221.json │ ├── 222.json │ ├── 235.json │ ├── 237.json │ ├── 238.json │ ├── 24.json │ ├── 266.json │ ├── 276.json │ ├── 30.json │ ├── 31.json │ ├── 34.json │ ├── 35.json │ ├── 37.json │ ├── 386.json │ ├── 399.json │ ├── 4.json │ ├── 49.json │ ├── 520.json │ ├── 55.json │ ├── 56.json │ ├── 6.json │ ├── 65.json │ ├── 683.json │ ├── 684.json │ ├── 687.json │ ├── 702.json │ ├── 728.json │ ├── 75.json │ └── 823.json ├── before │ ├── 10.json │ ├── 102.json │ ├── 12.json │ ├── 14.json │ ├── 157.json │ ├── 166.json │ ├── 176.json │ ├── 18.json │ ├── 187.json │ ├── 205.json │ ├── 217.json │ ├── 221.json │ ├── 222.json │ ├── 235.json │ ├── 237.json │ ├── 238.json │ ├── 24.json │ ├── 266.json │ ├── 276.json │ ├── 30.json │ ├── 31.json │ ├── 34.json │ ├── 35.json │ ├── 37.json │ ├── 386.json │ ├── 399.json │ ├── 4.json │ ├── 49.json │ ├── 520.json │ ├── 55.json │ ├── 56.json │ ├── 6.json │ ├── 65.json │ ├── 683.json │ ├── 684.json │ ├── 687.json │ ├── 702.json │ ├── 728.json │ ├── 75.json │ └── 823.json ├── diff │ ├── 102.diff │ ├── 12.diff │ ├── 14.diff │ ├── 157.diff │ ├── 221.diff │ ├── 222.diff │ ├── 235.diff │ ├── 238.diff │ ├── 34.diff │ ├── 35.diff │ ├── 49.diff │ ├── 56.diff │ ├── 702.diff │ └── 728.diff ├── get.sh └── teams.txt └── webapp ├── .gitignore ├── go ├── .gitignore ├── Dockerfile ├── Makefile ├── go.mod ├── go.sum ├── livecomment_handler.go ├── livestream_handler.go ├── main.go ├── payment_handler.go ├── reaction_handler.go ├── stats_handler.go ├── top_handler.go └── user_handler.go ├── img └── NoImage.jpg ├── node ├── .dockerignore ├── .eslintrc ├── .gitignore ├── .prettierrc ├── Dockerfile ├── package-lock.json ├── package.json ├── src │ ├── contants.ts │ ├── handlers │ │ ├── livecomment-handler.ts │ │ ├── livestream-handler.ts │ │ ├── payment-handler.ts │ │ ├── reaction-handler.ts │ │ ├── stats-handler.ts │ │ ├── top-handler.ts │ │ └── user-handler.ts │ ├── main.ts │ ├── middlewares │ │ └── verify-user-session-middleare.ts │ ├── types │ │ ├── application.ts │ │ └── models.ts │ └── utils │ │ ├── fill-livecomment-report-response.ts │ │ ├── fill-livecomment-response.ts │ │ ├── fill-livestream-response.ts │ │ ├── fill-reaction-response.ts │ │ ├── fill-user-response.ts │ │ ├── integer.ts │ │ └── throw-error-with.ts └── tsconfig.json ├── pdns ├── init_zone.sh ├── named.conf └── u.isucon.dev.zone ├── perl ├── .gitignore ├── Dockerfile ├── app.psgi ├── cpanfile └── lib │ └── Isupipe │ ├── App.pm │ ├── Assert.pm │ ├── Entity │ ├── Livecomment.pm │ ├── LivecommentReport.pm │ ├── Livestream.pm │ ├── LivestreamRankingEntry.pm │ ├── LivestreamStatistics.pm │ ├── LivestreamTag.pm │ ├── LivestreamViewer.pm │ ├── NGWord.pm │ ├── Reaction.pm │ ├── ReservationSlot.pm │ ├── Tag.pm │ ├── Theme.pm │ ├── User.pm │ ├── UserRankingEntry.pm │ └── UserStatistics.pm │ ├── FillResponse.pm │ ├── Handler │ ├── LivecommentHandler.pm │ ├── LivestreamHandler.pm │ ├── PaymentHandler.pm │ ├── ReactionHandler.pm │ ├── StatsHandler.pm │ ├── TopHandler.pm │ └── UserHandler.pm │ ├── Icon.pm │ ├── Log.pm │ └── Util.pm ├── php ├── .dockerignore ├── .gitignore ├── Dockerfile ├── app │ ├── Application │ │ ├── Handlers │ │ │ ├── HttpErrorHandler.php │ │ │ └── ShutdownHandler.php │ │ ├── ResponseEmitter │ │ │ └── ResponseEmitter.php │ │ └── Settings │ │ │ ├── Settings.php │ │ │ └── SettingsInterface.php │ ├── dependencies.php │ ├── routes.php │ └── settings.php ├── composer.json ├── composer.lock ├── composer.phar ├── phpcs.xml ├── phpstan.neon.dist ├── public │ └── index.php ├── src │ ├── AbstractHandler.php │ ├── InitializeHandler.php │ ├── InitializeResponse.php │ ├── Livecomment │ │ ├── FillLivecommentReportResponse.php │ │ ├── FillLivecommentResponse.php │ │ ├── Handler.php │ │ ├── Livecomment.php │ │ ├── LivecommentModel.php │ │ ├── LivecommentReport.php │ │ ├── LivecommentReportModel.php │ │ ├── ModerateRequest.php │ │ ├── NGWord.php │ │ └── PostLivecommentRequest.php │ ├── Livestream │ │ ├── FillLivestreamResponse.php │ │ ├── Handler.php │ │ ├── Livestream.php │ │ ├── LivestreamModel.php │ │ ├── LivestreamTagModel.php │ │ ├── LivestreamViewerModel.php │ │ ├── ReservationSlotModel.php │ │ └── ReserveLivestreamRequest.php │ ├── Payment │ │ ├── Handler.php │ │ └── PaymentResult.php │ ├── Reaction │ │ ├── Handler.php │ │ ├── PostReactionRequest.php │ │ ├── Reaction.php │ │ └── ReactionModel.php │ ├── Stats │ │ ├── Handler.php │ │ ├── LivestreamRankingEntry.php │ │ ├── LivestreamStatistics.php │ │ ├── UserRankingEntry.php │ │ └── UserStatistics.php │ ├── Top │ │ ├── Handler.php │ │ ├── Tag.php │ │ ├── TagModel.php │ │ └── TagsResponse.php │ └── User │ │ ├── FillUserResponse.php │ │ ├── Handler.php │ │ ├── LoginRequest.php │ │ ├── PostIconRequest.php │ │ ├── PostIconResponse.php │ │ ├── PostUserRequest.php │ │ ├── PostUserRequestTheme.php │ │ ├── Theme.php │ │ ├── ThemeModel.php │ │ ├── User.php │ │ ├── UserModel.php │ │ └── VerifyUserSession.php └── var │ └── cache │ └── .gitignore ├── python ├── Dockerfile ├── Pipfile ├── Pipfile.lock ├── app.py ├── models.py └── mypy.ini ├── ruby ├── .gitignore ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── app.rb └── config.ru ├── rust ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile └── src │ └── main.rs └── sql ├── init.sh ├── init.sql ├── initdb.d ├── 00_create_database.sql └── 10_schema.sql ├── initial_livecomments.sql ├── initial_livestream_tags.sql ├── initial_livestreams.sql ├── initial_ngwords.sql ├── initial_reactions.sql ├── initial_reservation_slots.sql ├── initial_tags.sql └── initial_users.sql /.github_/workflows/create_base_ami.yml: -------------------------------------------------------------------------------- 1 | name: Build Base Image with Packer 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-ami: 8 | runs-on: [isucon13-ci-packer-base] 9 | name: build isucon13 base image 10 | 11 | steps: 12 | - name: Checkout repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Install unzip 16 | run: | 17 | sudo apt-get update 18 | sudo apt-get install unzip 19 | 20 | - name: Set up Packer 21 | uses: hashicorp/setup-packer@main 22 | with: 23 | version: "1.9.4" 24 | 25 | - name: Set up Python 26 | uses: actions/setup-python@v2 27 | with: 28 | python-version: "3.10" 29 | 30 | - name: Install Ansible 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install ansible 34 | 35 | - name: Build 36 | run: | 37 | cd provisioning/packer 38 | packer init -upgrade isucon13_base_image.pkr.hcl 39 | packer build isucon13_base_image.pkr.hcl 40 | env: 41 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 42 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 43 | AWS_REGION: ap-northeast-1 -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[yaml]": { 3 | "editor.formatOnSave": false 4 | } 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ISUCON13 Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKE=make -C 2 | 3 | DOCKER_BUILD=docker build 4 | DOCKER_BUILD_OPTS=--no-cache 5 | DOCKER_RMI=docker rmi -f 6 | 7 | ISUPIPE_TAG=isupipe:latest 8 | 9 | test: test_benchmarker 10 | .PHONY: test 11 | 12 | test_benchmarker: 13 | $(MAKE) bench test 14 | .PHONY: test_benchmarker 15 | 16 | build_webapp: 17 | $(MAKE) webapp/go docker_image 18 | .PHONY: build_webapp 19 | 20 | -------------------------------------------------------------------------------- /bench/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM golang:1.21.0-bookworm 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y gcc g++ make locales locales-all 8 | 9 | RUN locale-gen en_US.UTF-8 10 | RUN useradd --uid=1001 --create-home benchuser 11 | USER benchuser 12 | 13 | RUN mkdir -p /home/benchuser 14 | WORKDIR /home/benchuser 15 | COPY --chown=benchuser:benchuser ./ /home/benchuser/ 16 | RUN go build -o bench ./cmd/bench 17 | 18 | ENV GOPATH=/home/benchuser/tmp/go 19 | ENV GOCACHE=/home/benchuser/tmp/go/.cache 20 | 21 | ENV LANG en_US.UTF-8 22 | ENV LANGUAGE en_US:en 23 | ENV LC_ALL en_US.UTF-8 24 | 25 | ENTRYPOINT ["/home/benchuser/bench"] 26 | CMD ["supervise"] 27 | -------------------------------------------------------------------------------- /bench/Dockerfile.prod: -------------------------------------------------------------------------------- 1 | FROM golang:1.21.0-bookworm 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y gcc g++ make locales locales-all 8 | 9 | RUN locale-gen en_US.UTF-8 10 | RUN useradd --uid=1001 --create-home benchuser 11 | USER benchuser 12 | 13 | RUN mkdir -p /home/benchuser 14 | WORKDIR /home/benchuser 15 | COPY --chown=benchuser:benchuser ./ /home/benchuser/ 16 | RUN go build -o bench ./cmd/bench 17 | 18 | ENV GOPATH=/home/benchuser/tmp/go 19 | ENV GOCACHE=/home/benchuser/tmp/go/.cache 20 | 21 | ENV LANG en_US.UTF-8 22 | ENV LANGUAGE en_US:en 23 | ENV LC_ALL en_US.UTF-8 24 | 25 | ENTRYPOINT ["/home/benchuser/bench"] 26 | CMD ["supervise", "--production"] 27 | -------------------------------------------------------------------------------- /bench/assets/data/.gitignore: -------------------------------------------------------------------------------- 1 | hash.txt 2 | -------------------------------------------------------------------------------- /bench/assets/load.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "encoding/hex" 7 | "strings" 8 | ) 9 | 10 | //go:embed data/hash.txt 11 | var hashList string 12 | 13 | type Asset struct { 14 | Path string 15 | Hash [32]byte 16 | } 17 | 18 | // Load は静的ファイルのハッシュ値などをローカルファイルから読み出します 19 | func Load() ([]*Asset, error) { 20 | return load(hashList) 21 | } 22 | 23 | func load(hashList string) ([]*Asset, error) { 24 | buff := bytes.NewBufferString(hashList) 25 | 26 | var assets []*Asset 27 | for { 28 | line, err := buff.ReadString('\n') 29 | if err != nil { 30 | break 31 | } 32 | 33 | line = strings.TrimSpace(line) 34 | if line == "" { 35 | continue 36 | } 37 | 38 | parts := strings.SplitN(line, " ", 2) 39 | if len(parts) != 2 { 40 | continue 41 | } 42 | 43 | pathname := strings.TrimPrefix(parts[1], ".") 44 | 45 | // hex to byte 46 | hash, err := hex.DecodeString(parts[0]) 47 | if err != nil { 48 | continue 49 | } 50 | fixed := [32]byte{} 51 | copy(fixed[:], hash) 52 | 53 | assets = append(assets, &Asset{ 54 | Path: pathname, 55 | Hash: fixed, 56 | }) 57 | } 58 | 59 | return assets, nil 60 | } 61 | -------------------------------------------------------------------------------- /bench/bin/.gitignore: -------------------------------------------------------------------------------- 1 | bench_darwin_amd64 2 | bench_darwin_arm64 3 | bench_linux_amd64 4 | bench_linux_arm64 5 | -------------------------------------------------------------------------------- /bench/cmd/bench/id_ed25519.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJNZODRcueyvriMzcYRlAsM2OPJWHUnkBovbHJheuUKJ noname 2 | -------------------------------------------------------------------------------- /bench/cmd/bench/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "os" 7 | "time" 8 | 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | func init() { 13 | time.Local = time.UTC 14 | rand.New(rand.NewSource(time.Now().UnixNano())) 15 | } 16 | 17 | func main() { 18 | os.Exit(cliMain()) 19 | } 20 | 21 | func cliMain() int { 22 | app := cli.NewApp() 23 | app.Name = "isupipebench" 24 | app.Usage = "isupipe ベンチマーカー" 25 | app.Description = "isupipeのベンチマークを実施" 26 | app.HelpName = "isupipebench" 27 | 28 | app.Commands = []cli.Command{ 29 | run, 30 | supervise, 31 | } 32 | 33 | app.Action = func(cliCtx *cli.Context) error { 34 | return cli.ShowAppHelp(cliCtx) 35 | } 36 | 37 | if err := app.Run(os.Args); err != nil { 38 | exitErr := err.(*cli.ExitError) 39 | log.Println(exitErr.Error()) 40 | return exitErr.ExitCode() 41 | } 42 | 43 | return 0 44 | } 45 | -------------------------------------------------------------------------------- /bench/cmd/bench/reboot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "log" 6 | "net" 7 | 8 | "golang.org/x/crypto/ssh" 9 | ) 10 | 11 | func reboot(ip string, signer ssh.Signer) error { 12 | addr := net.JoinHostPort(ip, "22") 13 | log.Printf("addr = %s\n", addr) 14 | log.Printf("signer = %+v\n", signer) 15 | client, err := ssh.Dial("tcp", addr, &ssh.ClientConfig{ 16 | User: "isuadmin", 17 | Auth: []ssh.AuthMethod{ 18 | ssh.PublicKeys(signer), 19 | }, 20 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 21 | }) 22 | if err != nil { 23 | log.Printf("error = %s\n", err.Error()) 24 | return err 25 | } 26 | defer client.Close() 27 | 28 | session, err := client.NewSession() 29 | if err != nil { 30 | return err 31 | } 32 | defer session.Close() 33 | 34 | if err := session.Run("sudo systemctl reboot"); err != nil { 35 | return err 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /bench/cmd/bench/s3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/aws/aws-sdk-go/aws" 8 | "github.com/aws/aws-sdk-go/aws/credentials" 9 | "github.com/aws/aws-sdk-go/aws/session" 10 | "github.com/aws/aws-sdk-go/service/s3" 11 | "github.com/isucon/isucon13/bench/internal/config" 12 | ) 13 | 14 | func UploadFinalcheckResult(bucketName string, jobID, team int) error { 15 | fd, err := os.Open(config.FinalcheckPath) 16 | if err != nil { 17 | return err 18 | } 19 | defer fd.Close() 20 | 21 | sess := session.Must(session.NewSession(&aws.Config{ 22 | Region: aws.String("ap-northeast-1"), 23 | Credentials: credentials.NewStaticCredentials(accessKey, secretAccessKey, ""), 24 | })) 25 | client := s3.New(sess) 26 | 27 | key := fmt.Sprintf("team-%d-job-%d-finalcheck.json", team, jobID) 28 | if _, err := client.PutObject(&s3.PutObjectInput{ 29 | Bucket: aws.String(bucketName), 30 | Key: aws.String(key), 31 | Body: fd, 32 | }); err != nil { 33 | return err 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /bench/internal/benchscore/counter.go: -------------------------------------------------------------------------------- 1 | package benchscore 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/isucon/isucandar/score" 8 | ) 9 | 10 | const ( 11 | DNSResolve score.ScoreTag = "dns-resolve" 12 | DNSFailed score.ScoreTag = "dns-failed" 13 | 14 | TooSlow score.ScoreTag = "too-slow-left" 15 | TooManySpam score.ScoreTag = "too-many-spam" 16 | ) 17 | 18 | var ( 19 | counter *score.Score 20 | doneCounterOnce sync.Once 21 | ) 22 | 23 | func InitCounter(ctx context.Context) { 24 | counter = score.NewScore(ctx) 25 | counter.Set(DNSResolve, 1) 26 | counter.Set(DNSFailed, 1) 27 | counter.Set(TooSlow, 1) 28 | counter.Set(TooManySpam, 1) 29 | } 30 | 31 | func IncResolves() { 32 | counter.Add(DNSResolve) 33 | } 34 | 35 | func NumResolves() int64 { 36 | table := counter.Breakdown() 37 | return table[DNSResolve] 38 | } 39 | 40 | func IncDNSFailed() { 41 | counter.Add(DNSFailed) 42 | } 43 | 44 | func NumDNSFailed() int64 { 45 | table := counter.Breakdown() 46 | return table[DNSFailed] 47 | } 48 | 49 | func GetByTag(tag score.ScoreTag) int64 { 50 | return counter.Breakdown()[tag] 51 | } 52 | 53 | func DoneCounter() { 54 | doneCounterOnce.Do(func() { 55 | counter.Close() 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /bench/internal/benchscore/profit.go: -------------------------------------------------------------------------------- 1 | package benchscore 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | var profit uint64 8 | 9 | func AddTip(tip uint64) { 10 | atomic.AddUint64(&profit, tip) 11 | } 12 | 13 | // GetFinalProfit は、最終売上を返します 14 | // FIXME: finalcheck後にprofitをスコアに加算しないと駄目 15 | func GetTotalProfit() uint64 { 16 | return profit 17 | } 18 | -------------------------------------------------------------------------------- /bench/internal/config/benchmark.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "time" 4 | 5 | // ベンチマーク走行時間タイムアウト 6 | const DefaultBenchmarkTimeout = 60 * time.Second 7 | 8 | // スパム離脱割合 9 | const TooManySpamThresholdPercentage = 30.0 10 | 11 | // 基本となる並列性 12 | // セマフォの重みに使われます 13 | const BaseParallelism = 1 14 | 15 | // 動的に並列度を上げる並列性 16 | // スコアに直結する視聴者シナリオなどのセマフォの重みに使われます 17 | const ChangableParallelism = 100 18 | 19 | // この数値だけは、最初のRegister, Login処理が保証されます 20 | // NOTE: このような保証がないと、登録が一切できず、ベンチ走行までシナリオが全く実行されないケースが出てしまいます 21 | const NumMustTryLogins = 10 22 | 23 | // HTTPクライアント(isucandar/agent) のタイムアウト 24 | const DefaultAgentTimeout = 20 * time.Second 25 | 26 | // POST /api/initialize 時のタイムアウト 27 | const InitializeAgentTimeout = 42 * time.Second 28 | 29 | // SearchLivestreamsのLIMITのデフォルト 30 | const NumSearchLivestreams = 50 31 | 32 | // NOTE: --enable-ssl オプションによって変更されます 33 | var ( 34 | HTTPScheme = "http" 35 | InsecureSkipVerify = true 36 | ) 37 | 38 | const BaseDomain = "u.isucon.dev" 39 | 40 | // 暇になってる接続のタイムアウト 41 | // NOTE: これを設定しないと、keepaliveで繋ぎっぱなしの接続が増え、Nginxでworker_connectionが不十分だというエラーログが出るようになる 42 | const ClientIdleConnTimeout = 5 * time.Second 43 | 44 | const AttackHTTPClientContextKey = "dns-attack-http-realip" 45 | -------------------------------------------------------------------------------- /bench/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | // NOTE: あまり並列度高く長い時間ベンチさせると、ポートが枯渇する 5 | // FIXME: 2以上にすると、シナリオの実装上、ランダムに選ばれたlivestream_idが重複したworker fnが同時に走り、 6 | // 7 | // POST /livestream/:livestream_id/enter が重複した配信に対して行われ、 8 | // テーブルのUNIQUE制約(user_id-livestream_id)を侵す可能性がある 9 | // 対策としては、アルゴリズム側で必ず重複しないように調整するか、 10 | // worker parallelismを1にしつつ、視聴者のシミュレータをgoroutineで吐き出して並行性を担保する 11 | // とりあえず後者で対応 12 | // 13 | // DefaultBenchmarkerParallelism = 5 14 | // シナリオテストのタイムアウト[秒] 15 | // ScenarioTestTimeoutSeconds = 3 16 | ) 17 | 18 | var Language string = "unknown" 19 | -------------------------------------------------------------------------------- /bench/internal/config/finalcheck.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "time" 4 | 5 | const FinalcheckTimeout = 10 * time.Second 6 | -------------------------------------------------------------------------------- /bench/internal/config/initialdata.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const InitialReactionCount = 1001 4 | const InitialNgWords = 14337 5 | -------------------------------------------------------------------------------- /bench/internal/config/reservation.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // 2024-04-01 01:00:00 4 | // NOTE: 2024-04-01 00:00:00 ~ 2024-04-01 01:00:00は初期データで予約済み 5 | const BaseAt = 1700874000 6 | 7 | // 同時配信枠数 8 | // NOTE: ベンチマーカー調整項目 9 | const NumSlots = 5 10 | 11 | // NOTE: 初期データ予約済みの1時間分を引く必要がある 12 | const NumHours = (24 * 365) - 1 13 | 14 | // この時間[h]を超えた配信枠は長時間配信とみなす 15 | const LongHourThreshold = 10 16 | -------------------------------------------------------------------------------- /bench/internal/config/supervise.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // NOTE: ベンチマーカー実行ログを当該パスに書き出す 4 | // 5 | // supervisorがそれを拾い、ポータルにPOSTする 6 | var StaffLogPath string = "/tmp/result.json" 7 | var ContestantLogPath string = "/tmp/staff.log" 8 | var ResultPath string = "/tmp/contestant.log" 9 | var FinalcheckPath string = "/tmp/finalcheck.json" 10 | -------------------------------------------------------------------------------- /bench/internal/config/webapp.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | var ( 9 | TargetBaseURL string = fmt.Sprintf("%s://pipe.%s:%d", HTTPScheme, BaseDomain, TargetPort) 10 | TargetNameserver string = "127.0.0.1" 11 | TargetWebapps []string = []string{} 12 | DNSPort int = 1053 13 | TargetPort int = 8080 14 | ) 15 | 16 | func IsWebappIP(ip net.IP) bool { 17 | for _, s := range TargetWebapps { 18 | if ip.String() == s { 19 | return true 20 | } 21 | } 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /bench/internal/pubsub/pubsub_test.go: -------------------------------------------------------------------------------- 1 | package pubsub 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type Item struct{} 12 | 13 | func TestPubSub(t *testing.T) { 14 | pool := NewPubSub(10) 15 | go pool.Run(context.TODO()) 16 | 17 | pool.Publish(context.TODO(), &Item{}) 18 | pool.Publish(context.TODO(), &Item{}) 19 | pool.Publish(context.TODO(), &Item{}) 20 | pool.Publish(context.TODO(), &Item{}) 21 | pool.Publish(context.TODO(), &Item{}) 22 | 23 | v, err := pool.Subscribe(context.TODO()) 24 | assert.NoError(t, err) 25 | fmt.Println(v) 26 | } 27 | -------------------------------------------------------------------------------- /bench/internal/resolver/native.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/isucon/isucon13/bench/internal/config" 10 | ) 11 | 12 | type NativeDNSResolver struct { 13 | Nameserver string 14 | Timeout time.Duration 15 | } 16 | 17 | func NewNativeDNSResolver() *NativeDNSResolver { 18 | return &NativeDNSResolver{ 19 | Nameserver: net.JoinHostPort(config.TargetNameserver, strconv.Itoa(config.DNSPort)), 20 | Timeout: 10000 * time.Millisecond, 21 | } 22 | } 23 | 24 | func (r *NativeDNSResolver) DialContext(ctx context.Context, network, address string) (net.Conn, error) { 25 | dialer := net.Dialer{ 26 | Timeout: r.Timeout, 27 | Resolver: &net.Resolver{ 28 | PreferGo: true, 29 | Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 30 | dialer := net.Dialer{Timeout: r.Timeout} 31 | return dialer.DialContext(ctx, "udp", r.Nameserver) 32 | }, 33 | }, 34 | } 35 | return dialer.DialContext(ctx, network, address) 36 | } 37 | -------------------------------------------------------------------------------- /bench/internal/scheduler/commit_state.go: -------------------------------------------------------------------------------- 1 | package scheduler 2 | 3 | // 割当のコミット状態を管理 4 | type CommitState int 5 | 6 | const ( 7 | CommitState_None CommitState = iota 8 | CommitState_Inflight 9 | CommitState_Committed 10 | ) 11 | -------------------------------------------------------------------------------- /bench/internal/scheduler/images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/1.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/10.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/100.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/101.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/102.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/103.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/103.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/104.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/104.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/105.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/105.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/106.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/106.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/107.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/107.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/108.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/108.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/109.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/109.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/11.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/110.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/111.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/111.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/112.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/113.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/113.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/114.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/114.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/115.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/115.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/116.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/116.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/117.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/117.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/118.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/118.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/119.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/119.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/12.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/120.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/120.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/121.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/121.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/122.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/122.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/123.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/123.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/124.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/124.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/125.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/126.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/126.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/127.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/127.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/128.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/129.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/129.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/13.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/130.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/130.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/131.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/131.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/132.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/132.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/133.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/133.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/134.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/134.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/135.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/135.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/136.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/137.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/137.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/138.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/138.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/139.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/139.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/14.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/140.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/140.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/141.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/141.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/142.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/142.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/143.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/143.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/144.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/144.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/145.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/145.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/146.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/146.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/147.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/147.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/148.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/148.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/149.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/149.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/15.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/150.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/151.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/151.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/152.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/152.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/153.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/153.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/154.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/154.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/155.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/155.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/156.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/156.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/157.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/157.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/158.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/158.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/159.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/159.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/16.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/160.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/161.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/161.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/162.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/162.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/163.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/163.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/164.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/164.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/165.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/165.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/166.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/166.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/167.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/167.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/168.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/168.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/169.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/169.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/17.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/170.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/170.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/171.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/171.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/172.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/172.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/173.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/173.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/174.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/174.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/175.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/175.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/176.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/177.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/177.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/178.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/178.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/179.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/179.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/18.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/180.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/180.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/181.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/181.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/182.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/182.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/183.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/183.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/184.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/184.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/185.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/185.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/186.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/186.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/187.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/187.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/188.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/188.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/189.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/189.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/19.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/190.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/2.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/20.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/21.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/22.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/23.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/24.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/25.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/26.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/27.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/28.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/29.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/3.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/30.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/31.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/32.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/33.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/34.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/35.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/36.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/37.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/38.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/39.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/4.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/40.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/41.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/42.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/43.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/44.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/45.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/46.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/47.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/48.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/49.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/5.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/50.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/51.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/52.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/53.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/54.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/55.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/56.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/56.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/57.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/57.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/58.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/59.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/6.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/60.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/61.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/61.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/62.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/62.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/63.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/64.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/65.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/65.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/66.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/66.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/67.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/67.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/68.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/69.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/69.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/7.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/70.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/70.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/71.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/71.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/72.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/72.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/73.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/74.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/75.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/76.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/76.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/77.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/77.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/78.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/78.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/79.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/79.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/8.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/80.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/80.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/81.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/81.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/82.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/82.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/83.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/83.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/84.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/84.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/85.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/85.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/86.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/86.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/87.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/87.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/88.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/88.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/89.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/89.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/9.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/90.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/90.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/91.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/91.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/92.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/92.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/93.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/93.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/94.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/94.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/95.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/95.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/96.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/96.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/97.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/97.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/98.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/98.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/images/99.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/internal/scheduler/images/99.jpg -------------------------------------------------------------------------------- /bench/internal/scheduler/initial.go: -------------------------------------------------------------------------------- 1 | package scheduler 2 | 3 | type InitialNgWord struct { 4 | UserID int64 5 | LivestreamID int64 6 | Word string 7 | } 8 | 9 | type InitialLivecomment struct { 10 | UserID int64 11 | LivestreamID int64 12 | Comment string 13 | } 14 | 15 | type InitialReaction struct { 16 | UserID int64 17 | LivestreamID int64 18 | EmojiName string 19 | } 20 | -------------------------------------------------------------------------------- /bench/isupipe/agent_option.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/isucon/isucandar/agent" 7 | ) 8 | 9 | func withClient(c *http.Client) agent.AgentOption { 10 | return func(a *agent.Agent) error { 11 | a.HttpClient = c 12 | 13 | return nil 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /bench/isupipe/client_payment.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type PaymentResult struct { 11 | // NOTE: 売上0を許容 12 | TotalTip int64 `json:"total_tip"` 13 | } 14 | 15 | func (c *Client) GetPaymentResult(ctx context.Context) (*PaymentResult, error) { 16 | req, err := c.agent.NewRequest(http.MethodGet, "/api/payment", nil) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | resp, err := c.agent.Do(ctx, req) 22 | if err != nil { 23 | return nil, err 24 | } 25 | defer func() { 26 | io.Copy(io.Discard, resp.Body) 27 | resp.Body.Close() 28 | }() 29 | 30 | var paymentResp *PaymentResult 31 | if json.NewDecoder(resp.Body).Decode(&paymentResp); err != nil { 32 | return nil, err 33 | } 34 | 35 | if err := ValidateResponse(req, paymentResp); err != nil { 36 | return nil, err 37 | } 38 | 39 | return paymentResp, nil 40 | } 41 | -------------------------------------------------------------------------------- /bench/isupipe/client_payment_test.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/isucon/isucandar/agent" 9 | "github.com/isucon/isucon13/bench/internal/config" 10 | "github.com/isucon/isucon13/bench/internal/logger" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | // FIXME: 変動をテスト 15 | func TestPayment(t *testing.T) { 16 | ctx := context.Background() 17 | 18 | testLogger, err := logger.InitTestLogger() 19 | assert.NoError(t, err) 20 | 21 | client, err := NewClient( 22 | testLogger, 23 | agent.WithBaseURL(config.TargetBaseURL), 24 | agent.WithTimeout(3*time.Second), 25 | ) 26 | assert.NoError(t, err) 27 | 28 | result1, err := client.GetPaymentResult(ctx) 29 | assert.NoError(t, err) 30 | 31 | // 投げ銭投稿 32 | 33 | // 変動チェック 34 | _ = result1 35 | } 36 | -------------------------------------------------------------------------------- /bench/isupipe/client_reaction_test.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | -------------------------------------------------------------------------------- /bench/isupipe/client_test.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | "time" 11 | 12 | "github.com/isucon/isucandar/agent" 13 | "github.com/isucon/isucon13/bench/internal/bencherror" 14 | "github.com/isucon/isucon13/bench/internal/logger" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestClient_Timeout(t *testing.T) { 19 | ctx := context.Background() 20 | 21 | testLogger, err := logger.InitTestLogger() 22 | assert.NoError(t, err) 23 | 24 | h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 25 | time.Sleep(500 * time.Millisecond) 26 | fmt.Fprintln(w, `{"tags": []}`) 27 | }) 28 | ts := httptest.NewServer(h) 29 | defer ts.Close() 30 | 31 | client, err := NewClient(testLogger, agent.WithBaseURL(ts.URL), agent.WithTimeout(1*time.Microsecond)) 32 | assert.NoError(t, err) 33 | 34 | // NOTE: 呼び出すエンドポイントは何でも良い 35 | // タグ取得がパラメータがなく簡単であるためこうしている 36 | _, err = client.GetTags(ctx) 37 | assert.True(t, errors.Is(err, bencherror.ErrTimeout)) 38 | } 39 | -------------------------------------------------------------------------------- /bench/isupipe/main_test.go: -------------------------------------------------------------------------------- 1 | package isupipe 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "testing" 7 | "time" 8 | 9 | "github.com/isucon/isucandar/agent" 10 | "github.com/isucon/isucon13/bench/internal/bencherror" 11 | "github.com/isucon/isucon13/bench/internal/benchscore" 12 | "github.com/isucon/isucon13/bench/internal/config" 13 | "github.com/isucon/isucon13/bench/internal/logger" 14 | ) 15 | 16 | func TestMain(m *testing.M) { 17 | testLogger, err := logger.InitTestLogger() 18 | if err != nil { 19 | log.Fatalln(err) 20 | } 21 | 22 | client, err := NewClient( 23 | testLogger, 24 | agent.WithTimeout(1*time.Minute), 25 | ) 26 | if err != nil { 27 | log.Fatalln(err) 28 | } 29 | config.TargetWebapps = []string{"127.0.0.1"} 30 | if _, err := client.Initialize(context.Background()); err != nil { 31 | log.Fatalln(err) 32 | } 33 | 34 | ctx := context.Background() 35 | benchscore.InitCounter(ctx) 36 | bencherror.InitErrors(ctx) 37 | 38 | m.Run() 39 | } 40 | -------------------------------------------------------------------------------- /bench/scenario/attacker.go: -------------------------------------------------------------------------------- 1 | package scenario 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/isucon/isucon13/bench/internal/attacker" 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | var maxAttackOnEachScenario = 100 12 | 13 | func DnsWaterTortureAttackScenario(ctx context.Context, httpClient *http.Client, loadLimiter *rate.Limiter) error { 14 | 15 | atk := attacker.NewDnsWaterTortureAttacker() 16 | for j := 0; j < maxAttackOnEachScenario; j++ { 17 | if err := loadLimiter.Wait(ctx); err == nil { 18 | atk.Attack(ctx, httpClient) 19 | } 20 | } 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /bench/scenario/core_finalcheck.go: -------------------------------------------------------------------------------- 1 | package scenario 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/isucon/isucandar/agent" 8 | "github.com/isucon/isucon13/bench/internal/config" 9 | "github.com/isucon/isucon13/bench/internal/resolver" 10 | "github.com/isucon/isucon13/bench/isupipe" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | func FinalcheckScenario(ctx context.Context, contestantLogger *zap.Logger, dnsResolver *resolver.DNSResolver) error { 15 | 16 | client, err := isupipe.NewCustomResolverClient( 17 | contestantLogger, 18 | dnsResolver, 19 | agent.WithTimeout(config.FinalcheckTimeout), 20 | ) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | // FIXME: ライブコメント存在チェック 26 | _ = client 27 | 28 | if err := os.WriteFile(config.FinalcheckPath, []byte("{}"), os.ModePerm); err != nil { 29 | return err 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /bench/scenario/core_pretest_dnsrecord.go: -------------------------------------------------------------------------------- 1 | package scenario 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/rand" 7 | "strings" 8 | 9 | "github.com/isucon/isucon13/bench/internal/config" 10 | "github.com/isucon/isucon13/bench/internal/resolver" 11 | "github.com/najeira/randstr" 12 | ) 13 | 14 | func dnsRecordPretest(ctx context.Context, dnsResolver *resolver.DNSResolver) error { 15 | _, err := dnsResolver.Lookup(ctx, "udp", fmt.Sprintf("%s.%s", "pipe", config.BaseDomain)) 16 | if err != nil { 17 | return fmt.Errorf("名前解決エラー: %v", err) 18 | } 19 | for i := 0; i < 10; i++ { 20 | r := config.DefaultDNSRecord[rand.Intn(len(config.DefaultDNSRecord))] 21 | _, err := dnsResolver.Lookup(ctx, "udp", fmt.Sprintf("%s.%s", r, config.BaseDomain)) 22 | if err != nil { 23 | return fmt.Errorf("名前解決エラー: %v", err) 24 | } 25 | } 26 | // 存在しない名前で 27 | for i := 0; i < 3; i++ { 28 | r := strings.ToLower(randstr.String(16)) 29 | _, err = dnsResolver.Lookup(ctx, "udp", fmt.Sprintf("%s.%s", r, config.BaseDomain)) 30 | if err != nil && strings.Contains(err.Error(), "サーバーリストに含まれていません") { 31 | // is not in the server listの時だけerr。それ以外は無視できる 32 | return fmt.Errorf("名前解決エラー: %v", err) 33 | } 34 | } 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /bench/scenario/core_pretest_initial.go: -------------------------------------------------------------------------------- 1 | package scenario 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/isucon/isucandar/agent" 8 | "github.com/isucon/isucon13/bench/internal/config" 9 | "github.com/isucon/isucon13/bench/internal/resolver" 10 | "github.com/isucon/isucon13/bench/isupipe" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | // 初期データpretest 15 | 16 | func normalInitialPaymentPretest(ctx context.Context, contestantLogger *zap.Logger, dnsResolver *resolver.DNSResolver) error { 17 | // 初期状態で0円であるか 18 | client, err := isupipe.NewCustomResolverClient( 19 | contestantLogger, 20 | dnsResolver, 21 | agent.WithTimeout(config.PretestTimeout), 22 | ) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | result, err := client.GetPaymentResult(ctx) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | if result.TotalTip != 0 { 33 | return fmt.Errorf("初期の売上は0ISUでなければなりません") 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /bench/scenario/testdata/NoImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/bench/scenario/testdata/NoImage.jpg -------------------------------------------------------------------------------- /development/Makefile: -------------------------------------------------------------------------------- 1 | up/%: 2 | sudo docker compose -f docker-compose-common.yml -f docker-compose-$*.yml up -d --build 3 | 4 | down/%: 5 | sudo docker compose -f docker-compose-common.yml -f docker-compose-$*.yml down --volumes 6 | 7 | 8 | -------------------------------------------------------------------------------- /development/docker-compose-go.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/go 9 | init: true 10 | working_dir: /home/isucon/webapp/go 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | environment: 19 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 20 | ISUCON13_POWERDNS_HOST: powerdns 21 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 22 | ISUCON13_POWERDNS_DISABLED: true 23 | ports: 24 | - "127.0.0.1:8080:8080" 25 | depends_on: 26 | mysql: 27 | condition: service_healthy 28 | -------------------------------------------------------------------------------- /development/docker-compose-node.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/node 9 | init: true 10 | working_dir: /home/isucon/webapp/node 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | - ../webapp/node/src:/home/isucon/webapp/node/src 19 | environment: 20 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 21 | ISUCON13_POWERDNS_HOST: powerdns 22 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 23 | ISUCON13_POWERDNS_DISABLED: true 24 | ports: 25 | - "127.0.0.1:8080:8080" 26 | depends_on: 27 | mysql: 28 | condition: service_healthy 29 | -------------------------------------------------------------------------------- /development/docker-compose-perl.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/perl 9 | init: true 10 | working_dir: /home/isucon/webapp/perl 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | environment: 19 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 20 | ISUCON13_POWERDNS_HOST: powerdns 21 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 22 | ISUCON13_POWERDNS_DISABLED: true 23 | ports: 24 | - "127.0.0.1:8080:8080" 25 | depends_on: 26 | mysql: 27 | condition: service_healthy 28 | -------------------------------------------------------------------------------- /development/docker-compose-php.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | nginx: 5 | image: nginx:latest 6 | container_name: nginx 7 | volumes: 8 | - ./php/etc/nginx/conf.d:/etc/nginx/conf.d 9 | ports: 10 | - "127.0.0.1:8443:80" 11 | - "127.0.0.1:8080:80" 12 | depends_on: 13 | - webapp 14 | 15 | webapp: 16 | cpus: 2 17 | mem_limit: 4g 18 | build: 19 | context: ../webapp/php 20 | init: true 21 | working_dir: /home/isucon/webapp/php 22 | container_name: webapp 23 | volumes: 24 | - ./php/usr/local/etc/php-fpm.d/zz-docker.conf:/usr/local/etc/php-fpm.d/zz-docker.conf 25 | - ../webapp/sql:/home/isucon/webapp/sql 26 | - ../webapp/pdns:/home/isucon/webapp/pdns 27 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 28 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 29 | - ../webapp/img:/home/isucon/webapp/img 30 | environment: 31 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 32 | ISUCON13_POWERDNS_HOST: powerdns 33 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 34 | ISUCON13_POWERDNS_DISABLED: true 35 | depends_on: 36 | mysql: 37 | condition: service_healthy 38 | -------------------------------------------------------------------------------- /development/docker-compose-python.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/python 9 | init: true 10 | working_dir: /home/isucon/webapp/python 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | environment: 19 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 20 | ISUCON13_POWERDNS_HOST: powerdns 21 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 22 | ISUCON13_POWERDNS_DISABLED: false 23 | ports: 24 | - "127.0.0.1:8080:8080" 25 | depends_on: 26 | mysql: 27 | condition: service_healthy 28 | -------------------------------------------------------------------------------- /development/docker-compose-ruby.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/ruby 9 | init: true 10 | working_dir: /home/isucon/webapp/ruby 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | environment: 19 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 20 | ISUCON13_POWERDNS_HOST: powerdns 21 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 22 | ports: 23 | - "127.0.0.1:8080:8080" 24 | depends_on: 25 | mysql: 26 | condition: service_healthy 27 | -------------------------------------------------------------------------------- /development/docker-compose-rust.yml: -------------------------------------------------------------------------------- 1 | version: '3.0' 2 | 3 | services: 4 | webapp: 5 | cpus: 2 6 | mem_limit: 4g 7 | build: 8 | context: ../webapp/rust 9 | init: true 10 | working_dir: /home/isucon/webapp/rust 11 | container_name: webapp 12 | volumes: 13 | - ../webapp/sql:/home/isucon/webapp/sql 14 | - ../webapp/pdns:/home/isucon/webapp/pdns 15 | - ../provisioning/ansible/roles/powerdns/files/pdns.conf:/etc/powerdns/pdns.conf:ro 16 | - ../provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf:/etc/powerdns/pdns.d/docker.conf:ro 17 | - ../webapp/img:/home/isucon/webapp/img 18 | environment: 19 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS: mysql 20 | ISUCON13_POWERDNS_HOST: powerdns 21 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS: 127.0.0.1 22 | ports: 23 | - "127.0.0.1:8080:8080" 24 | depends_on: 25 | mysql: 26 | condition: service_healthy 27 | -------------------------------------------------------------------------------- /development/etc/nginx/conf.d/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | client_max_body_size 10m; 5 | root /home/isucon/webapp/go/public/; 6 | 7 | location / { 8 | proxy_set_header Host $host; 9 | proxy_pass http://webapp:8080; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /development/php/etc/nginx/conf.d/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | client_max_body_size 10m; 5 | root /home/isucon/webapp/php/public/; 6 | 7 | location / { 8 | try_files $uri /index.php$is_args$args; 9 | } 10 | 11 | location = /index.php { 12 | include fastcgi_params; 13 | fastcgi_index index.php; 14 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 15 | fastcgi_pass webapp:8080; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /development/php/usr/local/etc/php-fpm.d/zz-docker.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | daemonize = no 3 | 4 | [www] 5 | listen = 8080 6 | -------------------------------------------------------------------------------- /envcheck/.gitignore: -------------------------------------------------------------------------------- 1 | isucon-admin 2 | /isucon-env-checker/isucon-env-checker 3 | envcheck 4 | envcheck_darwin -------------------------------------------------------------------------------- /envcheck/Makefile: -------------------------------------------------------------------------------- 1 | DARWIN_TARGET_ENV=GOOS=darwin GOARCH=arm64 2 | LINUX_TARGET_ENV=GOOS=linux GOARCH=amd64 3 | 4 | BUILD=go build 5 | 6 | DESTDIR=. 7 | TAG=envcheck:latest 8 | 9 | .PHONY: build 10 | build: 11 | CGO_ENABLED=0 $(DARWIN_TARGET_ENV) $(BUILD) -o $(DESTDIR)/envcheck_darwin -ldflags "-s -w" 12 | CGO_ENABLED=0 $(LINUX_TARGET_ENV) $(BUILD) -o $(DESTDIR)/envcheck -ldflags "-s -w" 13 | cp -a envcheck ../provisioning/ansible/roles/envcheck/files/ 14 | -------------------------------------------------------------------------------- /envcheck/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/aws/aws-sdk-go/service/ec2" 5 | ) 6 | 7 | type Data struct { 8 | ExpectedAMIID string 9 | ExpectedAZID string 10 | 11 | InstanceVPCID string 12 | 13 | DescribeInstances []*ec2.DescribeInstancesOutput 14 | DescribeVolumes []*ec2.DescribeVolumesOutput 15 | DescribeNetworkInterfaces []*ec2.DescribeNetworkInterfacesOutput 16 | 17 | DescribeAvailabilityZones *ec2.DescribeAvailabilityZonesOutput 18 | } 19 | -------------------------------------------------------------------------------- /envcheck/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/isucon/isucon13-portal/envcheck/isucon-env-checker 2 | 3 | go 1.18 4 | 5 | require github.com/aws/aws-sdk-go v1.44.19 6 | 7 | require github.com/jmespath/go-jmespath v0.4.0 // indirect 8 | -------------------------------------------------------------------------------- /envcheck/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | p, err := LoadPortalCredentials() 10 | if err != nil { 11 | fmt.Println("チェッカーの設定ファイルが読み込めませんでした") 12 | os.Exit(1) 13 | } 14 | 15 | var name string 16 | if len(os.Args) == 2 && os.Args[1] == "boot" { 17 | name = "contest-boot" 18 | } else { 19 | name = "contest-ssh" 20 | fmt.Println("SSH 接続が成功しました") 21 | } 22 | 23 | info, err := p.GetInfo(name) 24 | if err != nil { 25 | fmt.Printf("ポータルから情報の取得に失敗しました: %v\n", err) 26 | os.Exit(1) 27 | } 28 | 29 | fmt.Println("環境をチェックしています...") 30 | result, err := Check(CheckConfig{ 31 | Name: name, 32 | AMI: info.AMI, 33 | AZ: info.AZ, 34 | }) 35 | if err != nil { 36 | fmt.Printf("環境チェックに失敗しました: %v\n", err) 37 | os.Exit(1) 38 | } 39 | 40 | if err := p.SendResult(result); err != nil { 41 | fmt.Printf("チェック結果の送信に失敗しました: %v\n", err) 42 | os.Exit(1) 43 | } 44 | fmt.Println(result.Message) 45 | if !result.Passed { 46 | os.Exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frontend/.gitattributes: -------------------------------------------------------------------------------- 1 | .yarn/* linguist-generated=true 2 | yarn.lock linguist-generated=true 3 | src/api/apiClient.ts linguist-generated=true 4 | src/api/types.ts linguist-generated=true 5 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | !.vscode/extensions.json 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | /.yarn/cache 26 | /.yarn/install-state.gz 27 | -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | node_modules 3 | /dist/ 4 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll": true 4 | }, 5 | "editor.formatOnSave": true, 6 | "editor.defaultFormatter": "esbenp.prettier-vscode", 7 | "files.insertFinalNewline": true 8 | } 9 | -------------------------------------------------------------------------------- /frontend/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /frontend/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all deps js_build hash build 2 | 3 | ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 4 | 5 | all: deps build 6 | 7 | deps: 8 | corepack enable 9 | yarn 10 | 11 | js_build: 12 | yarn build 13 | 14 | hash: 15 | cd $(ROOT_DIR)/dist && sha256sum `find . -type f ` > $(ROOT_DIR)/../bench/assets/data/hash.txt 16 | 17 | build: js_build hash 18 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend 2 | 3 | ## How to build 4 | 5 | Requires Node.js and corepack 6 | 7 | ```bash 8 | corepack enable 9 | yarn 10 | yarn build 11 | tree dist 12 | ``` 13 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ISUCON13 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/scripts/openapi/generate-api-client.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import { CodeGenerator } from '@himenon/openapi-typescript-code-generator'; 4 | import * as Templates from '@himenon/openapi-typescript-code-generator/templates'; 5 | 6 | const main = () => { 7 | const codeGenerator = new CodeGenerator( 8 | path.join(__dirname, '../../../docs/isupipe.yaml'), 9 | ); 10 | 11 | const apiClientGeneratorTemplate = { 12 | generator: Templates.ClassApiClient.generator, 13 | option: {}, 14 | }; 15 | 16 | const typeDefCode = codeGenerator.generateTypeDefinition(); 17 | const apiClientCode = codeGenerator.generateCode([ 18 | { 19 | generator: () => [ 20 | `import { Schemas, RequestBodies, Responses } from "./types";`, 21 | ], 22 | }, 23 | codeGenerator.getAdditionalTypeDefinitionCustomCodeGenerator(), 24 | apiClientGeneratorTemplate, 25 | ]); 26 | 27 | fs.writeFileSync(__dirname + '/../../src/api/types.ts', typeDefCode, { 28 | encoding: 'utf-8', 29 | }); 30 | fs.writeFileSync(__dirname + '/../../src/api/apiClient.ts', apiClientCode, { 31 | encoding: 'utf-8', 32 | }); 33 | 34 | console.log('Generate API Client'); 35 | }; 36 | 37 | main(); 38 | -------------------------------------------------------------------------------- /frontend/scripts/openapi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openapi", 3 | "private": true, 4 | "version": "0.0.0", 5 | "packageManager": "yarn@3.2.2" 6 | } 7 | -------------------------------------------------------------------------------- /frontend/scripts/openapi/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "types": ["node"], 6 | "declaration": false, 7 | "noEmit": false 8 | }, 9 | "include": ["./*"] 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/@types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace JSX { 2 | interface IntrinsicElements { 3 | 'em-emoji': any; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/api/icon.ts: -------------------------------------------------------------------------------- 1 | export function iconUrl(username: string | undefined): string | undefined { 2 | return username && `/api/user/${username ?? 0}/icon`; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/api/url.ts: -------------------------------------------------------------------------------- 1 | export function normalizeUrl( 2 | path: string, 3 | tenant?: string | undefined, 4 | ): string { 5 | const hostname = tenant ? `${tenant}.u.isucon.dev` : 'pipe.u.isucon.dev'; 6 | const port = window.location.port; 7 | return `https://${hostname}${port ? `:${port}` : ''}${path}`; 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/assets/img/BBB.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/BBB.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/hiru.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/hiru.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/hkd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/hkd.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/isucon11_final.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/isucon11_final.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/isucon12_final.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/isucon12_final.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/isucon12_final_live.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/isucon12_final_live.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/isucon13.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/isucon13.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/isucon9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/isucon9.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/timewarp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/timewarp.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/ube.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/ube.webp -------------------------------------------------------------------------------- /frontend/src/assets/img/yoru.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/assets/img/yoru.webp -------------------------------------------------------------------------------- /frontend/src/assets/index.tsx: -------------------------------------------------------------------------------- 1 | import BBB from './img/BBB.webp?url'; 2 | import hiru from './img/hiru.webp?url'; 3 | import hkd from './img/hkd.webp?url'; 4 | import isucon11_final from './img/isucon11_final.webp?url'; 5 | import isucon12_final from './img/isucon12_final.webp?url'; 6 | import isucon12_final_live from './img/isucon12_final_live.webp?url'; 7 | import isucon13 from './img/isucon13.webp?url'; 8 | import isucon9 from './img/isucon9.webp?url'; 9 | import timewarp from './img/timewarp.webp?url'; 10 | import ube from './img/ube.webp?url'; 11 | import yoru from './img/yoru.webp?url'; 12 | 13 | export const imageUrls = [ 14 | BBB, 15 | hiru, 16 | hkd, 17 | isucon11_final, 18 | isucon12_final, 19 | isucon12_final_live, 20 | isucon13, 21 | isucon9, 22 | timewarp, 23 | ube, 24 | yoru, 25 | ]; 26 | 27 | export function getThumbnailUrl(id: number): string { 28 | return imageUrls[(id - 1) % imageUrls.length]; 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/components/layout/ISUPipe_yoko_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/frontend/src/components/layout/ISUPipe_yoko_color.png -------------------------------------------------------------------------------- /frontend/src/components/video/video.tsx: -------------------------------------------------------------------------------- 1 | import Box from '@mui/joy/Box'; 2 | import React from 'react'; 3 | import videojs from 'video.js'; 4 | import 'video.js/dist/video-js.css'; 5 | 6 | export interface VideoProps { 7 | playlist?: string; 8 | } 9 | export function Video(props: VideoProps): React.ReactElement { 10 | const containerRef = React.useRef(null); 11 | const playerRef = React.useRef | null>(null); 12 | 13 | React.useEffect(() => { 14 | if (!containerRef.current) { 15 | return; 16 | } 17 | 18 | const videoElement = document.createElement('video-js'); 19 | containerRef.current.appendChild(videoElement); 20 | console.log('videoElement', videoElement, containerRef.current); 21 | 22 | const player = videojs(videoElement, { 23 | autoplay: true, 24 | controls: true, 25 | fluid: true, 26 | sources: [ 27 | { 28 | src: props.playlist, 29 | type: 'application/x-mpegURL', 30 | }, 31 | ], 32 | }); 33 | playerRef.current = player; 34 | 35 | return () => { 36 | player.dispose(); 37 | playerRef.current = null; 38 | videoElement.remove(); 39 | }; 40 | }, [props.playlist]); 41 | 42 | return ; 43 | } 44 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | // import emojiData from '@emoji-mart/data'; 2 | import emojiData from '@emoji-mart/data/sets/14/twitter.json'; 3 | import { init as emojiInit } from 'emoji-mart'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom/client'; 6 | import { BrowserRouter } from 'react-router-dom'; 7 | import { App } from '~/App'; 8 | import '~/index.css'; 9 | 10 | emojiInit({ data: emojiData }); 11 | 12 | ReactDOM.createRoot(document.getElementById('root')!).render( 13 | 14 | 15 | 16 | 17 | , 18 | ); 19 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "~/*": ["src/*"] 6 | }, 7 | "typeRoots": ["src/@types"], 8 | "target": "ESNext", 9 | "useDefineForClassFields": true, 10 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 11 | "allowJs": false, 12 | "skipLibCheck": true, 13 | "esModuleInterop": false, 14 | "allowSyntheticDefaultImports": true, 15 | "strict": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "module": "ESNext", 18 | "moduleResolution": "Node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "include": ["src"], 25 | "references": [ 26 | { 27 | "path": "./tsconfig.node.json" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "~/*": ["src/*"] 6 | }, 7 | "composite": true, 8 | "module": "esnext", 9 | "moduleResolution": "node" 10 | }, 11 | "include": ["vite.config.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { defineConfig } from 'vite'; 3 | import Pages from 'vite-plugin-pages'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), Pages({})], 8 | server: { 9 | port: 4000, 10 | https: { 11 | key: '../provisioning/ansible/roles/nginx/files/etc/nginx/tls/_.u.isucon.dev.key', 12 | cert: '../provisioning/ansible/roles/nginx/files/etc/nginx/tls/_.u.isucon.dev.crt', 13 | }, 14 | proxy: { 15 | '/api': { 16 | target: 'http://localhost:8080', 17 | changeOrigin: true, 18 | }, 19 | }, 20 | }, 21 | resolve: { 22 | alias: [ 23 | { 24 | find: '~/', 25 | replacement: '/src/', 26 | }, 27 | ], 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /frontend/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | resolve: { 5 | alias: [ 6 | { 7 | find: '~/', 8 | replacement: '/src/', 9 | }, 10 | ], 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /provisioning/ansible/README.md: -------------------------------------------------------------------------------- 1 | # Ansible 2 | 3 | ## 利用方法 4 | 5 | ``` 6 | # ベンチマーカ、webappのアーカイブをアップデート  7 | $ ./make_latest_files.sh 8 | 9 | # ローカルの場合 10 | $ ansible-playbook -i inventory/localhost application.yml 11 | $ ansible-playbook -i inventory/localhost benchmark.yml 12 | 13 | # sacloud試し環境へのリモート実行 14 | $ ansible-playbook -i inventory/sacloud application.yml 15 | $ ansible-playbook -i inventory/sacloud benchmark.yml 16 | 17 | ``` 18 | 19 | すでに対象サーバに /home/isucon/webapp/sql がある場合、tarをアップロードするだけで展開はしません 20 | 21 | ## 証明書について 22 | 23 | *.t.isucon.dev の証明書はISUCON12予選のレポジトリにあるものを使っています 24 | https://github.com/isucon/isucon12-qualify 25 | 26 | 27 | ## make_lastest_filesの中身 28 | 29 | ``` 30 | $ cd bench 31 | $ make linux_amd64 32 | $ mkdir -p ../provisioning/ansible/roles/bench/files 33 | $ mv bin/bench_linux_amd64 ../provisioning/ansible/roles/bench/files 34 | $ cd .. 35 | $ tar -zcvf webapp.tar.gz webapp 36 | $ mv webapp.tar.gz provisioning/ansible/roles/webapp/files 37 | $ cd provisioning/ansible 38 | ``` -------------------------------------------------------------------------------- /provisioning/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | hash_behaviour = merge 3 | retry_files_enabled = False 4 | roles_path = ./roles 5 | callbacks_enabled = profile_tasks 6 | -------------------------------------------------------------------------------- /provisioning/ansible/application-base.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # ランタイムのインストールまでを行う 3 | # packerのビルドをわける場合につかう 4 | - hosts: application 5 | roles: 6 | - base 7 | - apt 8 | - isuadmin-user 9 | - isucon-user 10 | - xbuild 11 | - xbuildwebapp 12 | -------------------------------------------------------------------------------- /provisioning/ansible/application-deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # webappのデプロイ 3 | # 古いwebappディレクトリを消して作り直す 4 | - hosts: application 5 | pre_tasks: 6 | - name: remove webapp 7 | become: true 8 | shell: | 9 | rm -rf /home/isucon/webapp/ 10 | roles: 11 | - webapp 12 | -------------------------------------------------------------------------------- /provisioning/ansible/application.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: application 3 | roles: 4 | - base 5 | - apt 6 | - isuadmin-user 7 | - isucon-user 8 | - globalip 9 | - xbuild 10 | - xbuildwebapp 11 | - mysql 12 | - powerdns 13 | - nginx 14 | - envcheck 15 | - webapp 16 | -------------------------------------------------------------------------------- /provisioning/ansible/benchmark.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: benchmarker 3 | roles: 4 | - base 5 | - apt 6 | - isucon-user 7 | - xbuild 8 | - bench 9 | -------------------------------------------------------------------------------- /provisioning/ansible/inventory/hosts.yaml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | application: 4 | benchmarker: 5 | application: 6 | hosts: 7 | env1_app: 8 | ansible_host: 13.114.118.33 9 | ansible_user: root 10 | env2_app: 11 | ansible_host: 3.114.157.52 12 | ansible_user: root 13 | env3_app: 14 | ansible_host: 43.206.30.142 15 | ansible_user: root 16 | env4_app: 17 | ansible_host: 54.95.60.213 18 | ansible_user: root 19 | benchmarker: 20 | hosts: 21 | env1_bench: 22 | ansible_host: 35.78.12.143 23 | ansible_user: root 24 | env2_bench: 25 | ansible_host: 18.180.66.249 26 | ansible_user: root 27 | env3_bench: 28 | ansible_host: 54.92.119.204 29 | ansible_user: root 30 | env4_bench: 31 | ansible_host: 43.206.15.17 32 | ansible_user: root 33 | 34 | -------------------------------------------------------------------------------- /provisioning/ansible/inventory/localhost: -------------------------------------------------------------------------------- 1 | [benchmarker] 2 | localhost ansible_connection=local 3 | 4 | [application] 5 | localhost ansible_connection=local 6 | -------------------------------------------------------------------------------- /provisioning/ansible/make_latest_files.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | cd $(dirname $0) 5 | 6 | cd ../../bench 7 | make linux_amd64 8 | mkdir -p ../provisioning/ansible/roles/bench/files 9 | mv bin/bench_linux_amd64 ../provisioning/ansible/roles/bench/files 10 | cd ../provisioning/ansible 11 | 12 | cd ../../frontend 13 | make 14 | cp -r ./dist/ ../webapp/public/ 15 | cd ../provisioning/ansible 16 | 17 | cd ../../envcheck 18 | make 19 | cd ../provisioning/ansible 20 | 21 | cd ../../ 22 | tar -zcvf webapp.tar.gz webapp 23 | mv webapp.tar.gz provisioning/ansible/roles/webapp/files 24 | 25 | cd ./provisioning/ansible 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/apt/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Use jaist mirror 3 | become: true 4 | replace: 5 | path: /etc/apt/sources.list 6 | regexp: "http://archive.ubuntu.com/ubuntu" 7 | replace: "http://ftp.jaist.ac.jp/pub/Linux/ubuntu/" 8 | backup: yes 9 | 10 | - name: Update apt cache 11 | become: true 12 | apt: 13 | update_cache: yes 14 | cache_valid_time: 0 15 | 16 | - name: Install required packages 17 | become: true 18 | apt: 19 | name: 20 | - acl 21 | - build-essential 22 | - libxml2-dev 23 | - pkg-config 24 | - libsqlite3-dev 25 | - libbz2-dev 26 | - libcurl4-openssl-dev 27 | - libpng-dev 28 | - libjpeg-dev 29 | - libonig-dev 30 | - libreadline-dev 31 | - libtidy-dev 32 | - libxslt-dev 33 | - libzip-dev 34 | - autoconf 35 | - bison 36 | - dpkg-dev 37 | - libgdbm-dev 38 | - libssl-dev 39 | - libreadline-dev 40 | - libffi-dev 41 | - zlib1g-dev 42 | - libyaml-dev 43 | - libmysqlclient-dev 44 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/base/files/pubkey.conf: -------------------------------------------------------------------------------- 1 | PubkeyAcceptedAlgorithms=+ssh-rsa 2 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: "roles/common/tasks/timezone: Set timezone to UTC" 2 | become: true 3 | timezone: 4 | name: UTC 5 | 6 | - name: Copy sshd_config.d/pubkey.conf 7 | become: true 8 | copy: 9 | src: pubkey.conf 10 | dest: /etc/ssh/sshd_config.d/pubkey.conf 11 | mode: 0644 12 | 13 | - name: Set sysctl local_port_range 14 | become: true 15 | sysctl: 16 | name: net.ipv4.ip_local_port_range 17 | value: "10000 65535" 18 | state: present 19 | sysctl_set: true 20 | reload: true 21 | 22 | - name: Configure pam limits 23 | become: true 24 | pam_limits: 25 | domain: "*" 26 | limit_type: "{{item.limit_type}}" 27 | limit_item: "{{item.limit_item}}" 28 | value: "{{item.value}}" 29 | with_items: 30 | - { limit_type: "-", limit_item: "nofile", value: 655360 } 31 | - { limit_type: "-", limit_item: "nproc", value: 655360 } 32 | - { limit_type: "soft", limit_item: "memlock", value: unlimited } 33 | - { limit_type: "hard", limit_item: "memlock", value: unlimited } 34 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/bench/files/.gitignore: -------------------------------------------------------------------------------- 1 | bench_* 2 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/bench/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Send benchmarker binary 3 | become: yes 4 | copy: 5 | src: bench_linux_amd64 6 | dest: /home/isucon/ 7 | mode: 0755 8 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/envcheck/files/.gitignore: -------------------------------------------------------------------------------- 1 | envcheck.tar.gz 2 | envcheck 3 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/envcheck/files/envcheck.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Check Environments for ISUCON13 Contestants 3 | After=network.target 4 | 5 | [Service] 6 | Type=oneshot 7 | RemainAfterExit=yes 8 | ExecStartPre=/opt/isucon-env-checker/warmup.sh 9 | ExecStart=/opt/isucon-env-checker/run-isucon-env-checker.sh 10 | 11 | [Install] 12 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /provisioning/ansible/roles/envcheck/files/run-isucon-env-checker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | retried=0 5 | while [[ $retried -le 15 ]]; do 6 | sleep $(( RANDOM % 15 )) 7 | /opt/isucon-env-checker/envcheck boot && exit 0 8 | retried=$(( retried + 1 )) 9 | done 10 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/envcheck/files/warmup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | find /var/lib/mysql -type f | xargs cat > /dev/null 4 | find /home/isucon/webapp/sql -type f | xargs cat > /dev/null -------------------------------------------------------------------------------- /provisioning/ansible/roles/envcheck/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: mkdir for envcheck 3 | become: true 4 | ansible.builtin.file: 5 | path: /opt/isucon-env-checker 6 | state: directory 7 | 8 | - name: Send envcheck binary 9 | become: true 10 | ansible.builtin.copy: 11 | src: envcheck 12 | dest: /opt/isucon-env-checker/envcheck 13 | mode: 0755 14 | owner: root 15 | group: root 16 | 17 | - name: Put run script 18 | become: true 19 | ansible.builtin.copy: 20 | src: run-isucon-env-checker.sh 21 | dest: /opt/isucon-env-checker/run-isucon-env-checker.sh 22 | mode: 0755 23 | owner: root 24 | group: root 25 | 26 | - name: Put warmup script 27 | become: true 28 | ansible.builtin.copy: 29 | src: warmup.sh 30 | dest: /opt/isucon-env-checker/warmup.sh 31 | mode: 0755 32 | owner: root 33 | group: root 34 | 35 | - name: Put systemd service 36 | become: true 37 | ansible.builtin.copy: 38 | src: envcheck.service 39 | dest: /etc/systemd/system/ 40 | 41 | - name: Start envcheck 42 | become: true 43 | ansible.builtin.systemd: 44 | name: envcheck 45 | enabled: no 46 | daemon_reload: true 47 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/globalip/files/aws-env-isucon-subdomain-address.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=run aws-env-isucon-subdomain-address once 3 | Before=mysql.service 4 | 5 | [Service] 6 | Type=oneshot 7 | RemainAfterExit=yes 8 | ExecStart=/opt/aws-env-isucon-subdomain-address.sh 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/globalip/files/aws-env-isucon-subdomain-address.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eux 3 | 4 | if (test -f /opt/aws-env-isucon-subdomain-address.sh.lock); then 5 | exit 6 | fi 7 | 8 | touch /opt/aws-env-isucon-subdomain-address.sh.lock 9 | 10 | if !(grep Amazon /sys/devices/virtual/dmi/id/bios_vendor); then 11 | exit 12 | fi 13 | 14 | instance_ip=$(curl -s https://checkip.amazonaws.com) 15 | if [ -n $instance_ip ]; then 16 | sed -Ei 's/^ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS=.+$/ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS="'${instance_ip}'"/' /home/isucon/env.sh 17 | fi 18 | chmod 0755 /home/isucon/env.sh 19 | chown isucon:isucon /home/isucon/env.sh -------------------------------------------------------------------------------- /provisioning/ansible/roles/globalip/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: generator ISUCON_SUBDOMAIN_ADDRESS 3 | become: true 4 | copy: 5 | src: aws-env-isucon-subdomain-address.sh 6 | dest: /opt/aws-env-isucon-subdomain-address.sh 7 | owner: root 8 | group: root 9 | mode: 0755 10 | 11 | - name: generator ISUCON_SUBDOMAIN_ADDRESS service 12 | become: true 13 | copy: 14 | src: aws-env-isucon-subdomain-address.service 15 | dest: /etc/systemd/system/aws-env-isucon-subdomain-address.service 16 | owner: root 17 | group: root 18 | mode: 0644 19 | 20 | - name: Enable test service 21 | become: true 22 | service: 23 | name: aws-env-isucon-subdomain-address 24 | enabled: true 25 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/isuadmin-user/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create isuadmin group 3 | become: true 4 | group: 5 | name: isuadmin 6 | gid: 1110 7 | state: present 8 | system: no 9 | 10 | - name: Create isuadmin user 11 | become: true 12 | user: 13 | name: isuadmin 14 | uid: 1110 15 | group: isuadmin 16 | password: isuadmin 17 | home: /home/isuadmin 18 | shell: /bin/bash 19 | state: present 20 | system: no 21 | 22 | - name: Chmod isuadmin home directory 23 | become: true 24 | file: 25 | path: /home/isuadmin 26 | mode: 0755 27 | 28 | - name: Create .ssh directory for isuadmin 29 | become: true 30 | file: 31 | path: /home/isuadmin/.ssh 32 | state: directory 33 | mode: 0700 34 | owner: isuadmin 35 | group: isuadmin 36 | 37 | - name: Put authorized_keys 38 | become: true 39 | copy: 40 | dest: /home/isuadmin/.ssh/authorized_keys 41 | src: authorized_keys 42 | mode: 0600 43 | owner: isuadmin 44 | group: isuadmin 45 | 46 | - name: Add isuadmin to sudoers 47 | become: true 48 | copy: 49 | content: "isuadmin ALL=(ALL) NOPASSWD:ALL\n" 50 | dest: /etc/sudoers.d/99-isuadmin-user 51 | owner: root 52 | group: root 53 | mode: 0440 54 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/isucon-user/templates/env.sh: -------------------------------------------------------------------------------- 1 | ISUCON13_MYSQL_DIALCONFIG_NET="tcp" 2 | ISUCON13_MYSQL_DIALCONFIG_ADDRESS="127.0.0.1" 3 | ISUCON13_MYSQL_DIALCONFIG_PORT="3306" 4 | ISUCON13_MYSQL_DIALCONFIG_USER="isucon" 5 | ISUCON13_MYSQL_DIALCONFIG_DATABASE="isupipe" 6 | ISUCON13_MYSQL_DIALCONFIG_PARSETIME="true" 7 | ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS="{{ ansible_default_ipv4.address }}" 8 | ISUCON13_POWERDNS_DISABLED="false" 9 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/mysql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install MySQL 2 | become: true 3 | apt: 4 | name: mysql-server 5 | state: present 6 | 7 | - name: Start mysql service 8 | service: 9 | name: mysql 10 | enabled: true 11 | state: started 12 | 13 | - name: Create isucon user on MySQL 14 | become: true 15 | shell: > 16 | mysql -uroot -e " 17 | CREATE USER IF NOT EXISTS 'isucon'@'localhost' IDENTIFIED BY 'isucon'; 18 | GRANT ALL PRIVILEGES ON *.* TO 'isucon'@'localhost' WITH GRANT OPTION; 19 | CREATE USER IF NOT EXISTS 'isudns'@'localhost' IDENTIFIED BY 'isudns'; 20 | GRANT ALL PRIVILEGES ON *.* TO 'isudns'@'localhost' WITH GRANT OPTION; 21 | FLUSH PRIVILEGES; 22 | " 23 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/nginx/files/etc/nginx/sites-available/isupipe-php.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | 4 | client_max_body_size 10m; 5 | root /home/isucon/webapp/php/public/; 6 | 7 | location / { 8 | try_files $uri /index.php$is_args$args; 9 | } 10 | 11 | location = /index.php { 12 | include fastcgi_params; 13 | fastcgi_index index.php; 14 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 15 | fastcgi_pass 127.0.0.1:9000; 16 | } 17 | } -------------------------------------------------------------------------------- /provisioning/ansible/roles/nginx/files/etc/nginx/sites-enabled/isupipe.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | server_name _; 4 | index index.html index.htm index.nginx-debian.html; 5 | root /var/www/html; 6 | location / { 7 | try_files $uri $uri/ =404; 8 | } 9 | } 10 | 11 | server { 12 | listen 443 ssl default_server; 13 | server_name _; 14 | index index.html index.htm index.nginx-debian.html; 15 | root /var/www/html; 16 | 17 | # bot避けのためのvhostで、この証明書は有効期限がきれています 18 | ssl_certificate /etc/nginx/tls/_.t.isucon.dev.crt; 19 | ssl_certificate_key /etc/nginx/tls/_.t.isucon.dev.key; 20 | ssl_protocols TLSv1.3; 21 | ssl_prefer_server_ciphers off; 22 | 23 | location / { 24 | try_files $uri $uri/ =404; 25 | } 26 | } 27 | 28 | server { 29 | listen 443 ssl; 30 | server_name u.isucon.dev; 31 | server_name *.u.isucon.dev; 32 | 33 | ssl_certificate /etc/nginx/tls/_.u.isucon.dev.crt; 34 | ssl_certificate_key /etc/nginx/tls/_.u.isucon.dev.key; 35 | 36 | ssl_protocols TLSv1.3; 37 | ssl_prefer_server_ciphers off; 38 | 39 | client_max_body_size 10m; 40 | root /home/isucon/webapp/public/; 41 | location / { 42 | try_files $uri /index.html; 43 | } 44 | location /api { 45 | proxy_set_header Host $host; 46 | proxy_pass http://localhost:8080; 47 | } 48 | } -------------------------------------------------------------------------------- /provisioning/ansible/roles/nginx/files/etc/nginx/tls/_.u.isucon.dev.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIBjjgo8IfXK9zrU1FQW13c48XxCoX6Oyt8GwqaAbdy3eoAoGCCqGSM49 3 | AwEHoUQDQgAEtSYziO6gGlJFyvaBn1r07xlEQe0YaxkDktpKUfP7JapXWZT8ciUz 4 | DnawBi6o8senkB5dHvov6GljoXkrjOBS1g== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/auth-48: -------------------------------------------------------------------------------- 1 | Package: auth* 2 | Pin: origin repo.powerdns.com 3 | Pin-Priority: 600 -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/pdns.conf: -------------------------------------------------------------------------------- 1 | api=yes 2 | api-key=isudns 3 | webserver=yes 4 | include-dir=/etc/powerdns/pdns.d 5 | launch=gmysql 6 | gmysql-port=3306 7 | gmysql-user=isudns 8 | gmysql-dbname=isudns 9 | gmysql-password=isudns 10 | local-port=53 11 | security-poll-suffix= 12 | setgid=pdns 13 | setuid=pdns 14 | cache-ttl=0 15 | negquery-cache-ttl=0 16 | query-cache-ttl=0 17 | zone-cache-refresh-interval=0 18 | zone-metadata-cache-ttl=0 19 | 20 | log-dns-queries=yes 21 | loglevel=7 22 | log-dns-details=yes 23 | 24 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/pdns.d/docker.conf: -------------------------------------------------------------------------------- 1 | gmysql-host=mysql -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/pdns.d/gmysql-host.conf: -------------------------------------------------------------------------------- 1 | gmysql-host=127.0.0.1 -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/pdns.list: -------------------------------------------------------------------------------- 1 | deb [signed-by=/etc/apt/keyrings/auth-48-pub.asc arch=amd64] http://repo.powerdns.com/ubuntu jammy-auth-48 main 2 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/powerdns/files/resolved.conf: -------------------------------------------------------------------------------- 1 | [Resolve] 2 | DNSStubListener=no 3 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.gz 2 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-go.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-go 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/go 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/webapp/go/isupipe 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | RestartSec=5 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-node.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-node 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/node 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/.x npm run start 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | RestartSec=5 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-perl.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-perl 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/perl 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/.x ./local/bin/plackup -s Starlet -p 8080 -Ilib app.psgi 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-php.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-php 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/php 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/.x php-fpm --fpm-config /home/isucon/local/php/etc/isupipe.php-fpm.conf 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | RestartSec=5 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-python.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-python 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/python 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/.x pipenv run python app.py 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | RestartSec=5 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-ruby.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-ruby 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/ruby 9 | Environment=RUBY_YJIT_ENABLE=1 10 | EnvironmentFile=/home/isucon/env.sh 11 | 12 | User=isucon 13 | Group=isucon 14 | ExecStart=/home/isucon/.x bundle exec puma --bind tcp://0.0.0.0:8080 --workers 8 --threads 0:8 --environment production 15 | ExecStop=/bin/kill -s QUIT $MAINPID 16 | 17 | Restart=on-failure 18 | RestartSec=5 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe-rust.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=isupipe-rust 3 | After=syslog.target 4 | After=mysql.service 5 | Requires=mysql.service 6 | 7 | [Service] 8 | WorkingDirectory=/home/isucon/webapp/rust 9 | EnvironmentFile=/home/isucon/env.sh 10 | 11 | User=isucon 12 | Group=isucon 13 | ExecStart=/home/isucon/webapp/rust/target/release/isupipe 14 | ExecStop=/bin/kill -s QUIT $MAINPID 15 | 16 | Restart=on-failure 17 | RestartSec=1 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/files/isupipe.php-fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | daemonize = no 3 | error_log = log/isupipe.php-fpm.log 4 | syslog.facility = daemon 5 | syslog.ident = php-fpm 6 | log_level = notice 7 | 8 | [www] 9 | listen = 127.0.0.1:9000 10 | pm = static 11 | pm.max_children = 8 12 | clear_env = no 13 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/go.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for go 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/go 7 | state: directory 8 | 9 | - name: Build isupipe-go 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/local/golang/bin/go build -o /home/isucon/webapp/go/isupipe -ldflags "-s -w" 14 | args: 15 | chdir: /home/isucon/webapp/go 16 | 17 | - name: Put systemd service 18 | become: true 19 | ansible.builtin.copy: 20 | src: isupipe-go.service 21 | dest: /etc/systemd/system/ 22 | 23 | - name: Start webapp 24 | become: true 25 | service: 26 | name: isupipe-go 27 | enabled: true 28 | state: restarted 29 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/node.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for node 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/node 7 | state: directory 8 | 9 | - name: Build isupipe-node 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/.x npm install 14 | args: 15 | chdir: /home/isucon/webapp/node 16 | 17 | - name: Put systemd service 18 | become: true 19 | ansible.builtin.copy: 20 | src: isupipe-node.service 21 | dest: /etc/systemd/system/ 22 | 23 | - name: Start webapp 24 | become: true 25 | service: 26 | name: isupipe-node 27 | enabled: false 28 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/perl.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for perl 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/perl 7 | state: directory 8 | 9 | - name: Build isupipe-perl 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/.x cpm install --show-build-log-on-failure 14 | args: 15 | chdir: /home/isucon/webapp/perl 16 | 17 | - name: Put systemd service 18 | become: true 19 | ansible.builtin.copy: 20 | src: isupipe-perl.service 21 | dest: /etc/systemd/system/ 22 | 23 | - name: Start webapp 24 | become: true 25 | service: 26 | name: isupipe-perl 27 | enabled: false 28 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/php.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for php 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/php 7 | state: directory 8 | 9 | - name: Put php-fpn.conf 10 | become: true 11 | ansible.builtin.copy: 12 | src: isupipe.php-fpm.conf 13 | dest: /home/isucon/local/php/etc/isupipe.php-fpm.conf 14 | owner: isucon 15 | group: isucon 16 | 17 | - name: Build isupipe-php 18 | become: true 19 | become_user: isucon 20 | shell: | 21 | /home/isucon/.x ./composer.phar install 22 | args: 23 | chdir: /home/isucon/webapp/php 24 | 25 | - name: Put systemd service 26 | become: true 27 | ansible.builtin.copy: 28 | src: isupipe-php.service 29 | dest: /etc/systemd/system/ 30 | 31 | - name: Start webapp 32 | become: true 33 | service: 34 | name: isupipe-php 35 | enabled: false 36 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/python.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for python 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/python 7 | state: directory 8 | 9 | - name: install pipenv 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/.x pip install pipenv 14 | 15 | - name: Build isupipe-python 16 | become: true 17 | become_user: isucon 18 | shell: | 19 | /home/isucon/.x pipenv install 20 | args: 21 | chdir: /home/isucon/webapp/python 22 | 23 | - name: Put systemd service 24 | become: true 25 | ansible.builtin.copy: 26 | src: isupipe-python.service 27 | dest: /etc/systemd/system/ 28 | 29 | - name: Start webapp 30 | become: true 31 | service: 32 | name: isupipe-python 33 | enabled: false 34 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/ruby.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for ruby 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/ruby 7 | state: directory 8 | 9 | - name: Build isupipe-ruby 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/.x bundle install 14 | args: 15 | chdir: /home/isucon/webapp/ruby 16 | 17 | - name: Put systemd service 18 | become: true 19 | ansible.builtin.copy: 20 | src: isupipe-ruby.service 21 | dest: /etc/systemd/system/ 22 | 23 | - name: Start webapp 24 | become: true 25 | service: 26 | name: isupipe-ruby 27 | enabled: false 28 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/webapp/tasks/rust.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mkdir webapp for rust 3 | become: true 4 | become_user: isucon 5 | ansible.builtin.file: 6 | path: /home/isucon/webapp/rust 7 | state: directory 8 | 9 | - name: Build isupipe-rust 10 | become: true 11 | become_user: isucon 12 | shell: | 13 | /home/isucon/.x cargo build --release --locked 14 | args: 15 | chdir: /home/isucon/webapp/rust 16 | 17 | - name: Put systemd service 18 | become: true 19 | ansible.builtin.copy: 20 | src: isupipe-rust.service 21 | dest: /etc/systemd/system/ 22 | 23 | - name: Start webapp 24 | become: true 25 | service: 26 | name: isupipe-rust 27 | enabled: false 28 | state: stopped -------------------------------------------------------------------------------- /provisioning/ansible/roles/xbuild/files/.local.env: -------------------------------------------------------------------------------- 1 | export PATH=/home/isucon/local/golang/bin:$PATH 2 | export PATH=/home/isucon/local/node/bin:$PATH 3 | export PATH=/home/isucon/.cargo/bin:$PATH 4 | export PATH=/home/isucon/local/php/bin:/home/isucon/local/php/sbin:$PATH 5 | export PATH=/home/isucon/local/ruby/bin:$PATH 6 | export PATH=/home/isucon/local/perl/bin:/home/isucon/webapp/perl/local/bin:$PATH 7 | export PATH=/home/isucon/local/python/bin:$PATH 8 | export PERL5LIB=/home/isucon/webapp/perl/local/lib/perl5 9 | -------------------------------------------------------------------------------- /provisioning/ansible/roles/xbuild/files/.x: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . /home/isucon/.local.env 3 | exec "$@" -------------------------------------------------------------------------------- /provisioning/ansible/roles/xbuild/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install git 3 | become: true 4 | apt: 5 | name: git 6 | state: present 7 | 8 | - name: Install xbuild 9 | become: true 10 | git: 11 | repo: https://github.com/tagomoris/xbuild.git 12 | dest: /opt/xbuild 13 | version: master 14 | depth: 1 15 | 16 | - name: Create /home/isucon/local directory 17 | become: true 18 | file: 19 | path: /home/isucon/local 20 | state: directory 21 | mode: 0755 22 | owner: isucon 23 | group: isucon 24 | 25 | - name: Install golang 26 | become: true 27 | become_user: isucon 28 | shell: 29 | cmd: /opt/xbuild/go-install 1.21.2 /home/isucon/local/golang 30 | 31 | - name: Copy /home/isucon/.local.env 32 | become: true 33 | become_user: isucon 34 | copy: 35 | src: .local.env 36 | dest: /home/isucon/.local.env 37 | mode: 0644 38 | 39 | - name: Copy /home/isucon/.x 40 | become: true 41 | become_user: isucon 42 | copy: 43 | src: .x 44 | dest: /home/isucon/.x 45 | mode: 0755 46 | 47 | - name: modify /home/isucon/.profile 48 | become: true 49 | become_user: isucon 50 | lineinfile: 51 | path: /home/isucon/.profile 52 | regexp: '^source \$HOME/\.local\.env$' 53 | line: "source $HOME/.local.env" 54 | insertafter: EOF 55 | -------------------------------------------------------------------------------- /provisioning/ansible/sandbox.ini: -------------------------------------------------------------------------------- 1 | [application] 2 | 54.95.17.109 3 | ​ 4 | [application:vars] 5 | ansible_user=ubuntu 6 | ansible_ssh_private_key_file="../packer/ec2_honsen.pem" 7 | -------------------------------------------------------------------------------- /provisioning/packer/Makefile: -------------------------------------------------------------------------------- 1 | export BRANCH := $(shell git branch --contains | fgrep '*' | cut -d ' ' -f 2) 2 | .DEFAULT_GOAL := all 3 | 4 | .PHONY: all 5 | all: 6 | make echo-commit_hash 7 | make app-build 8 | make envcheck-build 9 | make init 10 | make build 11 | 12 | .PHONY: echo-commit_hash 13 | echo-commit_hash: 14 | git rev-parse HEAD 15 | 16 | .PHONY: app-build 17 | app-build: 18 | cd ../../provisioning/ansible/ && ./make_latest_files.sh 19 | 20 | .PHONY: envcheck-build 21 | envcheck-build: 22 | make -C ../../envcheck build 23 | 24 | .PHONY: init 25 | init: 26 | packer init -upgrade isucon13.pkr.hcl 27 | 28 | .PHONY: build 29 | build: 30 | packer build -var "commit_hash=$$(git rev-parse HEAD)" isucon13.pkr.hcl 31 | -------------------------------------------------------------------------------- /scripts/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | faker = "*" 8 | ipython = "*" 9 | mecab-python3 = "*" 10 | markovify = "*" 11 | requests = "*" 12 | 13 | [dev-packages] 14 | 15 | [requires] 16 | python_version = "3.10" 17 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # scripts 2 | 3 | ## requirements 4 | 5 | - bcrypt-tool 6 | - `go install github.com/shoenig/bcrypt-tool@latest` 7 | 8 | -------------------------------------------------------------------------------- /scripts/generate_livestream_reservation_slot.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta, timezone 2 | 3 | # 予約枠生成 4 | 5 | NUM_SLOTS = 5 6 | SQL_FORMAT="\t({slot}, {start_at}, {end_at})" 7 | 8 | # delta = timedelta(hours=1) 9 | base_time = datetime(2023, 11, 25, 1, tzinfo=timezone.utc) 10 | total_hours = (24*365)-1 11 | 12 | with open('/tmp/reservation_slot.sql', 'w') as f: 13 | f.write('INSERT INTO reservation_slots (slot, start_at, end_at)\n') 14 | f.write('VALUES\n') 15 | for idx, delta_hour in enumerate(range(total_hours)): 16 | start_delta = timedelta(hours=delta_hour) 17 | start_at = base_time + start_delta 18 | end_delta = timedelta(hours=delta_hour+1) 19 | end_at = base_time + end_delta 20 | 21 | sql = SQL_FORMAT.format(slot=NUM_SLOTS, start_at=int(start_at.timestamp()), end_at=int(end_at.timestamp())) 22 | print(f'start_at={start_at.isoformat()}', f'end_at={end_at.isoformat()}') 23 | if idx == total_hours-1: 24 | f.write(sql + ';\n') 25 | else: 26 | f.write(sql + ',\n') 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /validated/after/221.json: -------------------------------------------------------------------------------- 1 | 2 | 502 Bad Gateway 3 | 4 |

502 Bad Gateway

5 |
nginx/1.18.0 (Ubuntu)
6 | 7 | 8 | -------------------------------------------------------------------------------- /validated/after/235.json: -------------------------------------------------------------------------------- 1 | 2 | 502 Bad Gateway 3 | 4 |

502 Bad Gateway

5 |
nginx/1.18.0 (Ubuntu)
6 | 7 | 8 | -------------------------------------------------------------------------------- /validated/get.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | while IFS=' ' read -r team_id ip_address; do 5 | resolved_ip=$(dig +short pipe.u.isucon.dev "@$ip_address") 6 | curl -s --resolve "pipe.u.isucon.dev:443:${resolved_ip}" "https://pipe.u.isucon.dev/api/livestream/search?limit=50" > "${team_id}.json" 7 | done 8 | -------------------------------------------------------------------------------- /validated/teams.txt: -------------------------------------------------------------------------------- 1 | 10 54.178.156.176 2 | 37 54.95.27.130 3 | 14 52.198.53.219 4 | 237 52.198.50.116 5 | 18 18.176.29.78 6 | 157 52.197.3.79 7 | 6 35.78.160.73 8 | 823 52.69.240.148 9 | 217 57.180.110.119 10 | 24 35.76.184.95 11 | 687 18.176.230.93 12 | 166 54.238.245.39 13 | 266 35.78.4.198 14 | 238 18.176.233.191 15 | 102 35.72.213.27 16 | 235 13.113.27.245 17 | 31 35.76.236.243 18 | 205 18.178.4.134 19 | 276 54.178.97.21 20 | 187 52.69.112.84 21 | 176 52.195.50.137 22 | 728 13.113.177.192 23 | 386 18.181.151.10 24 | 683 175.41.229.98 25 | 12 13.114.245.3 26 | 49 52.193.199.92 27 | 75 13.230.78.40 28 | 56 35.76.179.85 29 | 34 54.65.30.66 30 | 55 35.76.197.153 31 | 702 54.95.184.103 32 | 399 52.197.175.198 33 | 221 13.113.240.32 34 | 222 52.198.91.20 35 | 35 35.79.81.235 36 | 30 18.182.209.61 37 | 520 54.248.242.20 38 | 4 176.34.46.12 39 | 684 54.64.16.49 40 | 65 52.196.251.200 41 | -------------------------------------------------------------------------------- /webapp/.gitignore: -------------------------------------------------------------------------------- 1 | /public/ 2 | -------------------------------------------------------------------------------- /webapp/go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21.0-bookworm 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y curl wget gcc g++ make sqlite3 locales locales-all && \ 8 | wget -q https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 9 | apt-get -y install ./mysql-apt-config_0.8.29-1_all.deb && \ 10 | apt-get -y update && \ 11 | apt-get -y install default-mysql-client pdns-server pdns-backend-mysql 12 | 13 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 14 | 15 | RUN locale-gen en_US.UTF-8 16 | RUN useradd --uid=1001 --create-home isucon 17 | USER isucon 18 | 19 | RUN mkdir -p /home/isucon/webapp/go 20 | WORKDIR /home/isucon/webapp/go 21 | COPY --chown=isucon:isucon ./ /home/isucon/webapp/go/ 22 | RUN go build -o isupipe . 23 | 24 | ENV GOPATH=/home/isucon/tmp/go 25 | ENV GOCACHE=/home/isucon/tmp/go/.cache 26 | 27 | ENV LANG en_US.UTF-8 28 | ENV LANGUAGE en_US:en 29 | ENV LC_ALL en_US.UTF-8 30 | 31 | ENV TZ utc 32 | 33 | EXPOSE 8080 34 | CMD ["/home/isucon/webapp/go/isupipe"] 35 | -------------------------------------------------------------------------------- /webapp/go/Makefile: -------------------------------------------------------------------------------- 1 | DARWIN_TARGET_ENV=GOOS=darwin GOARCH=arm64 2 | LINUX_TARGET_ENV=GOOS=linux GOARCH=amd64 3 | 4 | BUILD=go build 5 | 6 | DOCKER_BUILD=sudo docker build 7 | DOCKER_BUILD_OPTS=--no-cache 8 | 9 | DOCKER_RMI=sudo docker rmi -f 10 | 11 | DESTDIR=. 12 | TAG=isupipe:latest 13 | 14 | .PHONY: build 15 | build: 16 | CGO_ENABLED=0 $(LINUX_TARGET_ENV) $(BUILD) -o $(DESTDIR)/isupipe -ldflags "-s -w" 17 | 18 | .PHONY: darwin 19 | darwin: 20 | CGO_ENABLED=0 $(DARWIN_TARGET_ENV) $(BUILD) -o $(DESTDIR)/isupipe_darwin -ldflags "-s -w" 21 | 22 | .PHONY: docker_image 23 | docker_image: clean build 24 | $(DOCKER_BUILD) -t $(TAG) . $(DOCKER_BUILD_OPTS) 25 | 26 | .PHONY: clean 27 | clean: 28 | $(DOCKER_RMI) -f $(TAG) 29 | -------------------------------------------------------------------------------- /webapp/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/isucon/isucon13/webapp/go 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.7.1 7 | github.com/google/uuid v1.3.1 8 | github.com/gorilla/sessions v1.2.2 9 | github.com/jmoiron/sqlx v1.3.5 10 | github.com/labstack/echo-contrib v0.15.0 11 | github.com/labstack/echo/v4 v4.11.1 12 | github.com/labstack/gommon v0.4.0 13 | golang.org/x/crypto v0.11.0 14 | ) 15 | 16 | require ( 17 | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect 18 | github.com/gorilla/context v1.1.1 // indirect 19 | github.com/gorilla/securecookie v1.1.2 // indirect 20 | github.com/mattn/go-colorable v0.1.13 // indirect 21 | github.com/mattn/go-isatty v0.0.19 // indirect 22 | github.com/valyala/bytebufferpool v1.0.0 // indirect 23 | github.com/valyala/fasttemplate v1.2.2 // indirect 24 | golang.org/x/net v0.12.0 // indirect 25 | golang.org/x/sys v0.10.0 // indirect 26 | golang.org/x/text v0.11.0 // indirect 27 | golang.org/x/time v0.3.0 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /webapp/go/payment_handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo/v4" 7 | ) 8 | 9 | type PaymentResult struct { 10 | TotalTip int64 `json:"total_tip"` 11 | } 12 | 13 | func GetPaymentResult(c echo.Context) error { 14 | ctx := c.Request().Context() 15 | 16 | tx, err := dbConn.BeginTxx(ctx, nil) 17 | if err != nil { 18 | return echo.NewHTTPError(http.StatusInternalServerError, "failed to begin transaction: "+err.Error()) 19 | } 20 | defer tx.Rollback() 21 | 22 | var totalTip int64 23 | if err := tx.GetContext(ctx, &totalTip, "SELECT IFNULL(SUM(tip), 0) FROM livecomments"); err != nil { 24 | return echo.NewHTTPError(http.StatusInternalServerError, "failed to count total tip: "+err.Error()) 25 | } 26 | 27 | if err := tx.Commit(); err != nil { 28 | return echo.NewHTTPError(http.StatusInternalServerError, "failed to commit: "+err.Error()) 29 | } 30 | 31 | return c.JSON(http.StatusOK, &PaymentResult{ 32 | TotalTip: totalTip, 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /webapp/img/NoImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isucon/isucon13/8f6afdc3603f0c661368de4659a7240862f59623/webapp/img/NoImage.jpg -------------------------------------------------------------------------------- /webapp/node/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /webapp/node/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:@typescript-eslint/stylistic", 6 | "plugin:import/recommended", 7 | "plugin:import/typescript", 8 | "plugin:unicorn/recommended", 9 | "prettier" 10 | ], 11 | "settings": { 12 | "import/resolver": { 13 | "typescript": true 14 | } 15 | }, 16 | "rules": { 17 | "import/order": "error" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /webapp/node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /webapp/node/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /webapp/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20.9.0-bullseye-slim 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y curl wget gcc g++ make sqlite3 locales locales-all && \ 8 | wget -q https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 9 | apt-get -y install ./mysql-apt-config_0.8.29-1_all.deb && \ 10 | apt-get -y update && \ 11 | apt-get -y install default-mysql-client pdns-server pdns-backend-mysql 12 | 13 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 14 | 15 | RUN locale-gen en_US.UTF-8 16 | RUN useradd --uid=1001 --create-home isucon 17 | USER isucon 18 | 19 | RUN mkdir -p /home/isucon/webapp/node 20 | WORKDIR /home/isucon/webapp/node 21 | COPY --chown=isucon:isucon ./package.json ./package-lock.json /home/isucon/webapp/node/ 22 | RUN npm install 23 | 24 | COPY --chown=isucon:isucon ./ /home/isucon/webapp/node/ 25 | 26 | ENV LANG en_US.UTF-8 27 | ENV LANGUAGE en_US:en 28 | ENV LC_ALL en_US.UTF-8 29 | 30 | EXPOSE 8080 31 | CMD ["npm", "run", "start"] 32 | -------------------------------------------------------------------------------- /webapp/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "isupipe", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "tsx watch src/main.ts", 6 | "format": "prettier -w src", 7 | "lint": "eslint --fix src" 8 | }, 9 | "keywords": [], 10 | "overrides": { 11 | "hono-sessions": { 12 | "hono": "3.10.2" 13 | } 14 | }, 15 | "dependencies": { 16 | "@hono/node-server": "1.2.3", 17 | "bcrypt": "5.1.1", 18 | "hono": "3.10.2", 19 | "hono-sessions": "0.3.3", 20 | "mysql2": "3.6.5" 21 | }, 22 | "devDependencies": { 23 | "@tsconfig/recommended": "1.0.3", 24 | "@types/bcrypt": "5.0.2", 25 | "@types/node": "20.9.4", 26 | "@typescript-eslint/eslint-plugin": "6.12.0", 27 | "@typescript-eslint/parser": "6.12.0", 28 | "eslint": "8.54.0", 29 | "eslint-config-prettier": "9.0.0", 30 | "eslint-import-resolver-typescript": "3.6.1", 31 | "eslint-plugin-import": "2.29.0", 32 | "eslint-plugin-unicorn": "49.0.0", 33 | "prettier": "3.1.0", 34 | "tsx": "4.4.0", 35 | "typescript": "5.3.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /webapp/node/src/contants.ts: -------------------------------------------------------------------------------- 1 | // session 2 | export const defaultSessionIDKey = 'SESSIONID' 3 | export const defaultSessionExpiresKey = 'EXPIRES' 4 | export const defaultUserIDKey = 'USERID' 5 | export const defaultUserNameKey = 'USERNAME' 6 | -------------------------------------------------------------------------------- /webapp/node/src/handlers/payment-handler.ts: -------------------------------------------------------------------------------- 1 | import { Context } from 'hono' 2 | import { RowDataPacket } from 'mysql2/promise' 3 | import { HonoEnvironment } from '../types/application' 4 | import { throwErrorWith } from '../utils/throw-error-with' 5 | 6 | // GET /api/payment 7 | export const GetPaymentResult = async ( 8 | c: Context, 9 | ) => { 10 | const conn = await c.get('pool').getConnection() 11 | await conn.beginTransaction() 12 | 13 | try { 14 | const [[{ 'IFNULL(SUM(tip), 0)': totalTip }]] = await conn 15 | .query<({ 'IFNULL(SUM(tip), 0)': number } & RowDataPacket)[]>( 16 | 'SELECT IFNULL(SUM(tip), 0) FROM livecomments', 17 | ) 18 | .catch(throwErrorWith('failed to count total tip')) 19 | 20 | await conn.commit().catch(throwErrorWith('failed to commit')) 21 | 22 | return c.json({ totalTip: totalTip }) 23 | } catch (error) { 24 | await conn.rollback() 25 | return c.text(`Internal Server Error\n${error}`, 500) 26 | } finally { 27 | await conn.rollback() 28 | conn.release() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /webapp/node/src/middlewares/verify-user-session-middleare.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareHandler } from 'hono' 2 | import { HonoEnvironment } from '../types/application' 3 | import { defaultSessionExpiresKey, defaultUserIDKey } from '../contants' 4 | 5 | export const verifyUserSessionMiddleware: MiddlewareHandler< 6 | HonoEnvironment 7 | > = async (c, next) => { 8 | const session = c.get('session') 9 | 10 | const sessionExpires = session.get(defaultSessionExpiresKey) 11 | if (typeof sessionExpires !== 'number') { 12 | return c.text('failed to get EXPIRES value from session', 403) 13 | } 14 | if (typeof session.get(defaultUserIDKey) !== 'number') { 15 | return c.text('failed to get USERID value from session', 403) 16 | } 17 | if (Date.now() > sessionExpires) { 18 | return c.text('session has expired', 403) 19 | } 20 | await next() 21 | } 22 | -------------------------------------------------------------------------------- /webapp/node/src/types/application.ts: -------------------------------------------------------------------------------- 1 | import { Env } from 'hono' 2 | import { Session } from 'hono-sessions' 3 | import { Pool } from 'mysql2/promise' 4 | 5 | export interface Runtime { 6 | exec: (cmd: string[]) => Promise<{ stdout: string; stderr: string }> 7 | hashPassword: (password: string) => Promise 8 | comparePassword: (password: string, hash: string) => Promise 9 | fallbackUserIcon: () => Promise> 10 | } 11 | 12 | export interface ApplicationRuntime extends Runtime { 13 | powerDNSSubdomainAddress: string 14 | } 15 | 16 | export interface HonoEnvironment extends Env { 17 | Variables: { 18 | session: Session 19 | session_key_rotation: boolean 20 | pool: Pool 21 | runtime: ApplicationRuntime 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webapp/node/src/utils/integer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Goのstrconv.Atoiを模した関数 3 | * Integerを変換した値を返す 4 | * Integerに変換できない値が来たときはfalseを返す 5 | */ 6 | export const atoi = (string_: string): number | false => { 7 | const number_ = Number(string_) 8 | const isInt = Number.isInteger(number_) 9 | if (isInt) return number_ 10 | return false 11 | } 12 | -------------------------------------------------------------------------------- /webapp/node/src/utils/throw-error-with.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * エラーの情報にメッセージを付与して、再度エラーを投げる関数 3 | * @param message エラーに付与する文字列 4 | * 5 | * @example 6 | * await fetch('https://example.com').catch(throwErrorWith('GET https://example.com failed')) 7 | * // => GET https://example.com failed 8 | * // TypeError: Failed to fetch 9 | */ 10 | export const throwErrorWith = 11 | (message: string) => 12 | (error: unknown): never => { 13 | throw `${message}\n${error}` 14 | } 15 | -------------------------------------------------------------------------------- /webapp/node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "target": "ESNext" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /webapp/pdns/init_zone.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | cd $(dirname $0) 5 | 6 | if test -f /home/isucon/env.sh; then 7 | . /home/isucon/env.sh 8 | fi 9 | 10 | ISUCON_SUBDOMAIN_ADDRESS=${ISUCON13_POWERDNS_SUBDOMAIN_ADDRESS:-127.0.0.1} 11 | 12 | temp_dir=$(mktemp -d) 13 | trap 'rm -rf $temp_dir' EXIT 14 | sed 's//'$ISUCON_SUBDOMAIN_ADDRESS'/g' u.isucon.dev.zone > ${temp_dir}/u.isucon.dev.zone 15 | pdnsutil load-zone u.isucon.dev ${temp_dir}/u.isucon.dev.zone 16 | 17 | -------------------------------------------------------------------------------- /webapp/pdns/named.conf: -------------------------------------------------------------------------------- 1 | # Example for a manually configured master zone: 2 | # 3 | #zone "u.isucon.dev" { 4 | # file "/home/isucon/webapp/pdns/u.isucon.dev.zone"; 5 | # type master; 6 | #}; 7 | 8 | -------------------------------------------------------------------------------- /webapp/perl/.gitignore: -------------------------------------------------------------------------------- 1 | /local 2 | -------------------------------------------------------------------------------- /webapp/perl/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM perl:5.38.0-bookworm 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y curl wget gcc g++ make sqlite3 locales locales-all && \ 8 | wget -q https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 9 | apt-get -y install ./mysql-apt-config_0.8.29-1_all.deb && \ 10 | apt-get -y update && \ 11 | apt-get -y install default-mysql-client pdns-server pdns-backend-mysql 12 | 13 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 14 | 15 | RUN locale-gen en_US.UTF-8 16 | RUN useradd --uid=1001 --create-home isucon 17 | USER isucon 18 | 19 | RUN mkdir -p /home/isucon/webapp/perl 20 | WORKDIR /home/isucon/webapp/perl 21 | 22 | COPY cpanfile ./ 23 | RUN cpm install --show-build-log-on-failure 24 | 25 | COPY --chown=isucon:isucon ./ /home/isucon/webapp/perl/ 26 | ENV PERL5LIB=/home/isucon/webapp/perl/local/lib/perl5 27 | ENV PATH=/home/isucon/webapp/perl/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 28 | 29 | ENV LANG en_US.UTF-8 30 | ENV LANGUAGE en_US:en 31 | ENV LC_ALL en_US.UTF-8 32 | 33 | ENV TZ utc 34 | 35 | EXPOSE 8080 36 | CMD ["./local/bin/plackup", "-s", "Starlet", "-p", "8080", "-Ilib", "app.psgi"] 37 | -------------------------------------------------------------------------------- /webapp/perl/app.psgi: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | 3 | use File::Basename; 4 | use Plack::Builder; 5 | use Isupipe::App; 6 | 7 | my $root_dir = File::Basename::dirname(__FILE__); 8 | 9 | my $app = Isupipe::App->psgi($root_dir); 10 | 11 | builder { 12 | enable 'ReverseProxy'; 13 | enable 'Session::Cookie', 14 | session_key => 'isupipe_perl', 15 | domain => 'u.isucon.dev', 16 | path => '/', 17 | expires => 3600, 18 | secret => $ENV{ISUCON13_SESSION_SECRETKEY} || 'defaultsecret'; 19 | $app; 20 | } 21 | -------------------------------------------------------------------------------- /webapp/perl/cpanfile: -------------------------------------------------------------------------------- 1 | requires 'perl', '5.038'; 2 | 3 | requires 'Kossy', '0.63'; 4 | requires 'Plack::Middleware::Session', '0.33'; 5 | requires 'HTTP::Message', '6.45'; 6 | requires 'Cpanel::JSON::XS', '4.37'; 7 | requires 'Starlet', '0.31'; 8 | 9 | # DBD::mysql@5.01 requires requires MySQL 8.x client libraries. 10 | # https://github.com/perl5-dbi/DBD-mysql/issues/378#issuecomment-1786759895 11 | requires 'DBD::mysql', '== 4.051'; 12 | 13 | requires 'DBIx::Sunny', '0.9993'; 14 | requires 'Log::Minimal', '0.19'; 15 | requires 'Type::Tiny', '2.004000'; 16 | requires 'Crypt::Eksblowfish::Bcrypt', '0.009'; 17 | requires 'Crypt::OpenSSL::Random', '0.15'; 18 | requires 'Data::Lock', '1.03'; 19 | requires 'JSON::Types', '0.05'; 20 | requires 'Digest::SHA', '6.04'; 21 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Assert.pm: -------------------------------------------------------------------------------- 1 | package Isupipe::Assert; 2 | use v5.38; 3 | 4 | use Exporter 'import'; 5 | 6 | our @EXPORT = qw( 7 | ASSERT 8 | assert_field 9 | ); 10 | 11 | use Carp qw(croak); 12 | 13 | # 本番環境ではassertしない 14 | # 下記のassert_field以外にも、Isupipe::Util#check_paramsでも利用 15 | use constant ASSERT => ($ENV{PLACK_ENV}||'') ne 'production'; 16 | 17 | # 開発環境では、型チェックをしてあげる 18 | sub assert_field($type, $value, $field_name) { 19 | if (ASSERT && defined $value) { 20 | unless ($type->check($value)) { 21 | croak "Invalid field `$field_name`: " . $type->get_message($value); 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/LivestreamRankingEntry.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::LivestreamRankingEntry { 5 | field $livestream_id :param = undef; 6 | field $score :param = undef; 7 | 8 | use Isupipe::Assert; 9 | use Types::Standard -types; 10 | 11 | ADJUST { 12 | assert_field(Int, $livestream_id, 'livestream_id'); 13 | assert_field(Int, $score, 'score'); 14 | } 15 | 16 | method livestream_id($new=undef) { 17 | if (defined $new) { assert_field(Int, $new, 'livestream_id'); $livestream_id = $new }; 18 | $livestream_id 19 | } 20 | 21 | method score($new=undef) { 22 | if (defined $new) { assert_field(Int, $new, 'score'); $score = $new }; 23 | $score 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/LivestreamTag.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::LivestreamTag { 5 | field $id :param = undef; 6 | field $livestream_id :param = undef; 7 | field $tag_id :param = undef; 8 | 9 | use Isupipe::Assert; 10 | use Types::Standard -types; 11 | 12 | ADJUST { 13 | assert_field(Int, $id, 'id'); 14 | assert_field(Int, $livestream_id, 'livestream_id'); 15 | assert_field(Int, $tag_id, 'tag_id'); 16 | } 17 | 18 | method as_hashref() { 19 | return { 20 | id => $id, 21 | livestream_id => $livestream_id, 22 | tag_id => $tag_id, 23 | } 24 | } 25 | 26 | method TO_JSON() { 27 | ... 28 | } 29 | 30 | method id($new=undef) { 31 | if (defined $new) { assert_field(Int, $new, 'id'); $id = $new }; 32 | $id 33 | } 34 | 35 | method livestream_id($new=undef) { 36 | if (defined $new) { assert_field(Int, $new, 'livestream_id'); $livestream_id = $new }; 37 | $livestream_id 38 | } 39 | 40 | method tag_id($new=undef) { 41 | if (defined $new) { assert_field(Int, $new, 'tag_id'); $tag_id = $new }; 42 | $tag_id 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/LivestreamViewer.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::LivestreamViewer { 5 | field $user_id :param = undef; 6 | field $livestream_id :param = undef; 7 | field $created_at :param = undef; 8 | 9 | use Isupipe::Assert; 10 | use Types::Standard -types; 11 | 12 | ADJUST { 13 | assert_field(Int, $user_id, 'user_id'); 14 | assert_field(Int, $livestream_id, 'livestream_id'); 15 | assert_field(Int, $created_at, 'created_at'); 16 | } 17 | 18 | method as_hashref() { 19 | return { 20 | user_id => $user_id, 21 | livestream_id => $livestream_id, 22 | created_at => $created_at, 23 | } 24 | } 25 | 26 | method TO_JSON() { 27 | ... 28 | } 29 | 30 | method user_id($new=undef) { 31 | if (defined $new) { assert_field(Int, $new, 'user_id'); $user_id = $new }; 32 | $user_id 33 | } 34 | 35 | method livestream_id($new=undef) { 36 | if (defined $new) { assert_field(Int, $new, 'livestream_id'); $livestream_id = $new }; 37 | $livestream_id 38 | } 39 | 40 | method created_at($new=undef) { 41 | if (defined $new) { assert_field(Int, $new, 'created_at'); $created_at = $new }; 42 | $created_at 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/Tag.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::Tag { 5 | field $id :param = undef; 6 | field $name :param = undef; 7 | 8 | use Isupipe::Assert; 9 | use Types::Standard -types; 10 | 11 | ADJUST { 12 | assert_field(Int, $id, 'id'); 13 | assert_field(Str, $name, 'name'); 14 | } 15 | 16 | method as_hashref() { 17 | return { 18 | id => $id, 19 | name => $name, 20 | } 21 | } 22 | 23 | method TO_JSON() { 24 | return { 25 | id => $id, 26 | name => $name, 27 | }; 28 | } 29 | 30 | method id($new=undef) { 31 | if (defined $new) { assert_field(Int, $new, 'id'); $id = $new }; 32 | $id 33 | } 34 | 35 | method name($new=undef) { 36 | if (defined $new) { assert_field(Str, $new, 'name'); $name = $new }; 37 | $name 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/Theme.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::Theme { 5 | field $id :param = undef; 6 | field $user_id :param = undef; 7 | field $dark_mode :param = undef; 8 | 9 | use Isupipe::Assert; 10 | use Types::Standard -types; 11 | use JSON::Types; 12 | 13 | ADJUST { 14 | assert_field(Int, $id, 'id'); 15 | assert_field(Int, $user_id, 'user_id'); 16 | assert_field(Bool, $dark_mode, 'dark_mode'); 17 | } 18 | 19 | method as_hashref() { 20 | return { 21 | id => $id, 22 | user_id => $user_id, 23 | dark_mode => $dark_mode, 24 | } 25 | } 26 | 27 | method TO_JSON() { 28 | return { 29 | id => $id, 30 | dark_mode => bool $dark_mode, 31 | } 32 | } 33 | 34 | method id($new=undef) { 35 | if (defined $new) { assert_field(Int, $new, 'id'); $id = $new }; 36 | $id 37 | } 38 | 39 | method user_id($new=undef) { 40 | if (defined $new) { assert_field(Int, $new, 'user_id'); $user_id = $new }; 41 | $user_id 42 | } 43 | 44 | method dark_mode($new=undef) { 45 | if (defined $new) { assert_field(Int, $new, 'dark_mode'); $dark_mode = $new }; 46 | $dark_mode 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Entity/UserRankingEntry.pm: -------------------------------------------------------------------------------- 1 | use v5.38; 2 | use experimental qw(class); 3 | 4 | class Isupipe::Entity::UserRankingEntry { 5 | field $user_name :param = undef; 6 | field $score :param = undef; 7 | 8 | use Isupipe::Assert; 9 | use Types::Standard -types; 10 | 11 | ADJUST { 12 | assert_field(Str, $user_name, 'user_name'); 13 | assert_field(Int, $score, 'score'); 14 | } 15 | 16 | method user_name($new=undef) { 17 | if (defined $new) { assert_field(Str, $new, 'user_name'); $user_name = $new }; 18 | $user_name 19 | } 20 | 21 | method score($new=undef) { 22 | if (defined $new) { assert_field(Int, $new, 'score'); $score = $new }; 23 | $score 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Handler/PaymentHandler.pm: -------------------------------------------------------------------------------- 1 | package Isupipe::Handler::PaymentHandler; 2 | use v5.38; 3 | use utf8; 4 | 5 | sub get_payment_result($app, $c) { 6 | 7 | my $total_tip = $app->dbh->select_one( 8 | 'SELECT IFNULL(SUM(tip), 0) FROM livecomments' 9 | ); 10 | 11 | return $c->render_json({ 12 | total_tip => $total_tip, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Handler/TopHandler.pm: -------------------------------------------------------------------------------- 1 | package Isupipe::Handler::TopHandler; 2 | use v5.38; 3 | use utf8; 4 | 5 | use HTTP::Status qw(:constants); 6 | use Types::Standard -types; 7 | 8 | use Isupipe::Log; 9 | use Isupipe::Entity::Tag; 10 | use Isupipe::Entity::User; 11 | use Isupipe::Entity::Theme; 12 | use Isupipe::Util qw( 13 | verify_user_session 14 | ); 15 | 16 | sub get_tag_handler($app, $c) { 17 | my $tags = $app->dbh->select_all_as( 18 | 'Isupipe::Entity::Tag', 19 | 'SELECT * FROM tags' 20 | ); 21 | 22 | return $c->render_json({ tags => $tags }); 23 | } 24 | 25 | # 配信者のテーマ取得API 26 | sub get_streamer_theme_handler($app, $c) { 27 | verify_user_session($app, $c); 28 | 29 | my $username = $c->args->{username}; 30 | 31 | my $txn = $app->dbh->txn_scope; 32 | 33 | my $user = $app->dbh->select_row_as( 34 | 'Isupipe::Entity::User', 35 | 'SELECT id FROM users WHERE name = ?', 36 | $username 37 | ); 38 | unless ($user) { 39 | $c->halt(HTTP_NOT_FOUND, 'user not found: '. $username); 40 | } 41 | 42 | my $theme = $app->dbh->select_row_as( 43 | 'Isupipe::Entity::Theme', 44 | 'SELECT * FROM themes WHERE user_id = ?', 45 | $user->id 46 | ); 47 | 48 | $txn->commit; 49 | 50 | return $c->render_json($theme); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Icon.pm: -------------------------------------------------------------------------------- 1 | package Isupipe::Icon; 2 | use v5.38; 3 | use utf8; 4 | 5 | use Exporter 'import'; 6 | 7 | our @EXPORT_OK = qw( 8 | read_fallback_user_icon_image 9 | ); 10 | 11 | use constant FALLBACK_IMAGE_PATH => "../img/NoImage.jpg"; 12 | 13 | sub read_fallback_user_icon_image { 14 | open my $fh, '<:raw', FALLBACK_IMAGE_PATH or die "Cannot open FALLBACK_IMAGE: $!"; 15 | my $image = do { local $/; <$fh> }; 16 | return $image; 17 | } 18 | -------------------------------------------------------------------------------- /webapp/perl/lib/Isupipe/Log.pm: -------------------------------------------------------------------------------- 1 | package Isupipe::Log; 2 | use v5.38; 3 | use utf8; 4 | 5 | use Encode (); 6 | use Log::Minimal; 7 | 8 | use Exporter 'import'; 9 | our @EXPORT = @Log::Minimal::EXPORT; 10 | 11 | # warn する時のログフォーマット 12 | # Log::Minimalと違い、encode してから warnする 13 | $Log::Minimal::PRINT = sub { 14 | my ( $time, $type, $message, $trace, $raw_message) = @_; 15 | warn Encode::encode_utf8("$time [$type] $message at $trace\n"); 16 | }; 17 | 18 | # die する時のログフォーマット 19 | # Log::Minimalと違い、encode してから dieする 20 | $Log::Minimal::DIE = sub { 21 | my ( $time, $type, $message, $trace, $raw_message) = @_; 22 | die Encode::encode_utf8("$time [$type] $message at $trace\n"); 23 | }; 24 | 25 | # 色付きの出力設定 26 | # 1: 色あり 27 | # 0: 色なし 28 | $Log::Minimal::COLOR = 1; 29 | 30 | # ログを出力するレベル 31 | # DEBUG: debug 以上のログを出力(LM_DEBUG=1 で出力) 32 | # INFO: info 以上のログを出力 33 | # WARN: warn 以上のログを出力 34 | # ERROR: error 以上のログを出力 35 | # CRITICAL: critical 以上のログを出力 36 | # MUTE: ログを出力しない 37 | $Log::Minimal::LOG_LEVEL = 'DEBUG'; 38 | 39 | # DEBUG のログを出力するか 40 | # 1: 出力する 41 | # 0: 出力しない 42 | $ENV{LM_DEBUG} = ($ENV{PLACK_ENV}||'') ne 'production'; 43 | 44 | -------------------------------------------------------------------------------- /webapp/php/.dockerignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /webapp/php/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /webapp/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2.11-fpm-bullseye 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y curl wget gcc g++ make sqlite3 locales locales-all git zip && \ 8 | wget -q https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 9 | apt-get -y install ./mysql-apt-config_0.8.29-1_all.deb && \ 10 | apt-get -y update && \ 11 | apt-get -y install default-mysql-client pdns-server pdns-backend-mysql 12 | 13 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 14 | 15 | RUN docker-php-ext-install pdo_mysql 16 | 17 | RUN locale-gen en_US.UTF-8 18 | RUN useradd --uid=1001 --create-home isucon 19 | USER isucon 20 | 21 | WORKDIR /home/isucon/webapp/php 22 | COPY --chown=isucon:isucon ./ /home/isucon/webapp/php/ 23 | RUN ./composer.phar install 24 | 25 | ENV LANG en_US.UTF-8 26 | ENV LANGUAGE en_US:en 27 | ENV LC_ALL en_US.UTF-8 28 | 29 | ENV TZ utc 30 | -------------------------------------------------------------------------------- /webapp/php/app/Application/Handlers/HttpErrorHandler.php: -------------------------------------------------------------------------------- 1 | exception; 16 | $statusCode = 500; 17 | $message = 'server error'; 18 | 19 | if ($exception instanceof HttpException) { 20 | $statusCode = $exception->getCode(); 21 | $message = $exception->getMessage(); 22 | } elseif ($this->displayErrorDetails) { 23 | $message = $exception->getMessage(); 24 | } 25 | 26 | $encodedPayload = json_encode(['error' => $message], JSON_PRETTY_PRINT); 27 | 28 | $response = $this->responseFactory->createResponse($statusCode); 29 | $response->getBody()->write($encodedPayload); 30 | 31 | return $response->withHeader('Content-Type', 'application/json'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /webapp/php/app/Application/ResponseEmitter/ResponseEmitter.php: -------------------------------------------------------------------------------- 1 | withHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') 19 | ->withAddedHeader('Cache-Control', 'post-check=0, pre-check=0'); 20 | 21 | if (ob_get_contents()) { 22 | ob_clean(); 23 | } 24 | 25 | parent::emit($response); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webapp/php/app/Application/Settings/Settings.php: -------------------------------------------------------------------------------- 1 | $settings 13 | */ 14 | public function __construct(private array $settings) 15 | { 16 | } 17 | 18 | /** 19 | * @throws ErrorException 20 | */ 21 | public function get(string $key = ''): mixed 22 | { 23 | if (empty($key)) { 24 | return $this->settings; 25 | } 26 | 27 | return $this->settings[$key] ?? throw new ErrorException('undefined setting key'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /webapp/php/app/Application/Settings/SettingsInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | app 16 | public 17 | src 18 | 19 | -------------------------------------------------------------------------------- /webapp/php/phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 4 3 | paths: 4 | - app 5 | - public 6 | - src 7 | ignoreErrors: 8 | - 9 | message: '#^If condition is always#' 10 | path: public/index.php 11 | -------------------------------------------------------------------------------- /webapp/php/src/InitializeHandler.php: -------------------------------------------------------------------------------- 1 | execCommand([__DIR__ . '/../../sql/init.sh']); 22 | } catch (RuntimeException $e) { 23 | $this->logger->warning('init.sh failed with err=' . $e->getMessage()); 24 | throw new HttpInternalServerErrorException( 25 | request: $request, 26 | message: 'failed to initialize: ' . $e->getMessage(), 27 | previous: $e, 28 | ); 29 | } 30 | 31 | return $this->jsonResponse($response, new InitializeResponse( 32 | language: 'php', 33 | )); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webapp/php/src/InitializeResponse.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function jsonSerialize(): array 18 | { 19 | return [ 20 | 'language' => $this->language, 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webapp/php/src/Livecomment/Livecomment.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | public function jsonSerialize(): array 27 | { 28 | return [ 29 | 'id' => $this->id, 30 | 'user' => $this->user, 31 | 'livestream' => $this->livestream, 32 | 'comment' => $this->comment, 33 | 'tip' => $this->tip, 34 | 'created_at' => $this->createdAt, 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /webapp/php/src/Livecomment/LivecommentReport.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function jsonSerialize(): array 24 | { 25 | return [ 26 | 'id' => $this->id, 27 | 'reporter' => $this->reporter, 28 | 'livecomment' => $this->livecomment, 29 | 'created_at' => $this->createdAt, 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /webapp/php/src/Livecomment/LivecommentReportModel.php: -------------------------------------------------------------------------------- 1 | $row 23 | * @throws UnexpectedValueException 24 | */ 25 | public static function fromRow(array $row): LivecommentReportModel 26 | { 27 | try { 28 | return new LivecommentReportModel( 29 | id: $row['id'] ?? null, 30 | userId: $row['user_id'] ?? null, 31 | livestreamId: $row['livestream_id'] ?? null, 32 | livecommentId: $row['livecomment_id'] ?? null, 33 | createdAt: $row['created_at'] ?? null, 34 | ); 35 | } catch (TypeError $e) { 36 | throw new UnexpectedValueException( 37 | message: $e->getMessage(), 38 | previous: $e, 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webapp/php/src/Livecomment/ModerateRequest.php: -------------------------------------------------------------------------------- 1 | getMessage(), 28 | previous: $e, 29 | ); 30 | } 31 | 32 | if (!isset($data->ng_word)) { 33 | throw new UnexpectedValueException('required fields are missing'); 34 | } 35 | 36 | try { 37 | return new ModerateRequest( 38 | ngWord: $data->ng_word, 39 | ); 40 | } catch (TypeError $e) { 41 | throw new UnexpectedValueException( 42 | message: $e->getMessage(), 43 | previous: $e, 44 | ); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /webapp/php/src/Livestream/LivestreamTagModel.php: -------------------------------------------------------------------------------- 1 | $row 21 | * @throws UnexpectedValueException 22 | */ 23 | public static function fromRow(array $row): LivestreamTagModel 24 | { 25 | try { 26 | return new LivestreamTagModel( 27 | id: $row['id'] ?? null, 28 | livestreamId: $row['livestream_id'] ?? null, 29 | tagId: $row['tag_id'] ?? null, 30 | ); 31 | } catch (TypeError $e) { 32 | throw new UnexpectedValueException( 33 | message: $e->getMessage(), 34 | previous: $e, 35 | ); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /webapp/php/src/Livestream/LivestreamViewerModel.php: -------------------------------------------------------------------------------- 1 | $row 22 | * @throws UnexpectedValueException 23 | */ 24 | public static function fromRow(array $row): ReservationSlotModel 25 | { 26 | try { 27 | return new ReservationSlotModel( 28 | id: $row['id'] ?? null, 29 | slot: $row['slot'] ?? null, 30 | startAt: $row['start_at'] ?? null, 31 | endAt: $row['end_at'] ?? null, 32 | ); 33 | } catch (TypeError $e) { 34 | throw new UnexpectedValueException( 35 | message: $e->getMessage(), 36 | previous: $e, 37 | ); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /webapp/php/src/Payment/Handler.php: -------------------------------------------------------------------------------- 1 | db->beginTransaction(); 24 | 25 | try { 26 | $stmt = $this->db->prepare('SELECT IFNULL(SUM(tip), 0) FROM livecomments'); 27 | $stmt->execute(); 28 | $totalTip = (int) $stmt->fetchColumn(); 29 | } catch (PDOException $e) { 30 | throw new HttpInternalServerErrorException( 31 | request: $request, 32 | message: 'failed to count total tip: ' . $e->getMessage(), 33 | previous: $e, 34 | ); 35 | } 36 | 37 | $this->db->commit(); 38 | 39 | return $this->jsonResponse($response, new PaymentResult( 40 | totalTip: $totalTip 41 | )); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /webapp/php/src/Payment/PaymentResult.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function jsonSerialize(): array 20 | { 21 | return [ 22 | 'total_tip' => $this->totalTip, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webapp/php/src/Reaction/Reaction.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | public function jsonSerialize(): array 26 | { 27 | return [ 28 | 'id' => $this->id, 29 | 'emoji_name' => $this->emojiName, 30 | 'user' => $this->user, 31 | 'livestream' => $this->livestream, 32 | 'created_at' => $this->createdAt, 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webapp/php/src/Reaction/ReactionModel.php: -------------------------------------------------------------------------------- 1 | $row 23 | * @throws UnexpectedValueException 24 | */ 25 | public static function fromRow(array $row): ReactionModel 26 | { 27 | try { 28 | return new ReactionModel( 29 | id: $row['id'] ?? null, 30 | emojiName: $row['emoji_name'] ?? null, 31 | userId: $row['user_id'] ?? null, 32 | livestreamId: $row['livestream_id'] ?? null, 33 | createdAt: $row['created_at'] ?? null, 34 | ); 35 | } catch (TypeError $e) { 36 | throw new UnexpectedValueException( 37 | message: $e->getMessage(), 38 | previous: $e, 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webapp/php/src/Stats/LivestreamRankingEntry.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class LivestreamRankingEntry 11 | { 12 | public function __construct( 13 | public int $livestreamId, 14 | public int $score, 15 | ) { 16 | } 17 | 18 | public function compare(LivestreamRankingEntry $other): int 19 | { 20 | if ($this->score === $other->score) { 21 | return $this->livestreamId <=> $other->livestreamId; 22 | } 23 | 24 | return $this->score <=> $other->score; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /webapp/php/src/Stats/LivestreamStatistics.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function jsonSerialize(): array 24 | { 25 | return [ 26 | 'rank' => $this->rank, 27 | 'viewers_count' => $this->viewersCount, 28 | 'total_reactions' => $this->totalReactions, 29 | 'total_reports' => $this->totalReports, 30 | 'max_tip' => $this->maxTip, 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /webapp/php/src/Stats/UserRankingEntry.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class UserRankingEntry 11 | { 12 | public function __construct( 13 | public string $username, 14 | public int $score, 15 | ) { 16 | } 17 | 18 | public function compare(UserRankingEntry $other): int 19 | { 20 | if ($this->score === $other->score) { 21 | return $this->username <=> $other->username; 22 | } 23 | 24 | return $this->score <=> $other->score; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /webapp/php/src/Stats/UserStatistics.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function jsonSerialize(): array 25 | { 26 | return [ 27 | 'rank' => $this->rank, 28 | 'viewers_count' => $this->viewersCount, 29 | 'total_reactions' => $this->totalReactions, 30 | 'total_livecomments' => $this->totalLivecomments, 31 | 'total_tip' => $this->totalTip, 32 | 'favorite_emoji' => $this->favoriteEmoji, 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webapp/php/src/Top/Tag.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function jsonSerialize(): array 21 | { 22 | return [ 23 | 'id' => $this->id, 24 | 'name' => $this->name, 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webapp/php/src/Top/TagModel.php: -------------------------------------------------------------------------------- 1 | $row 20 | * @throws UnexpectedValueException 21 | */ 22 | public static function fromRow(array $row): TagModel 23 | { 24 | try { 25 | return new TagModel( 26 | id: $row['id'] ?? null, 27 | name: $row['name'] ?? null, 28 | ); 29 | } catch (TypeError $e) { 30 | throw new UnexpectedValueException( 31 | message: $e->getMessage(), 32 | previous: $e, 33 | ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /webapp/php/src/Top/TagsResponse.php: -------------------------------------------------------------------------------- 1 | $tags 13 | */ 14 | public function __construct( 15 | public array $tags, 16 | ) { 17 | foreach ($tags as $tag) { 18 | assert($tag instanceof Tag); 19 | } 20 | } 21 | 22 | /** 23 | * @return array 24 | */ 25 | public function jsonSerialize(): array 26 | { 27 | return [ 28 | 'tags' => $this->tags, 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /webapp/php/src/User/PostIconResponse.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function jsonSerialize(): array 20 | { 21 | return [ 22 | 'id' => $this->id, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webapp/php/src/User/PostUserRequestTheme.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public function jsonSerialize(): array 21 | { 22 | return [ 23 | 'id' => $this->id, 24 | 'dark_mode' => $this->darkMode, 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webapp/php/src/User/ThemeModel.php: -------------------------------------------------------------------------------- 1 | $row 21 | * @throws UnexpectedValueException 22 | */ 23 | public static function fromRow(array $row): ThemeModel 24 | { 25 | try { 26 | return new ThemeModel( 27 | id: $row['id'] ?? null, 28 | userId: $row['user_id'] ?? null, 29 | darkMode: is_null($row['dark_mode']) ? null : (bool) $row['dark_mode'], 30 | ); 31 | } catch (TypeError $e) { 32 | throw new UnexpectedValueException( 33 | message: $e->getMessage(), 34 | previous: $e, 35 | ); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /webapp/php/src/User/User.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | public function jsonSerialize(): array 25 | { 26 | $data = [ 27 | 'id' => $this->id, 28 | 'name' => $this->name, 29 | ]; 30 | 31 | if (!is_null($this->displayName)) { 32 | $data['display_name'] = $this->displayName; 33 | } 34 | 35 | if (!is_null($this->description)) { 36 | $data['description'] = $this->description; 37 | } 38 | 39 | if (!is_null($this->theme)) { 40 | $data['theme'] = $this->theme; 41 | } 42 | 43 | if (!is_null($this->iconHash)) { 44 | $data['icon_hash'] = $this->iconHash; 45 | } 46 | 47 | return $data; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /webapp/php/src/User/UserModel.php: -------------------------------------------------------------------------------- 1 | $row 23 | * @throws UnexpectedValueException 24 | */ 25 | public static function fromRow(array $row): UserModel 26 | { 27 | try { 28 | return new UserModel( 29 | id: $row['id'] ?? null, 30 | name: $row['name'] ?? null, 31 | displayName: $row['display_name'] ?? null, 32 | description: $row['description'] ?? null, 33 | hashedPassword: $row['password'] ?? null, 34 | ); 35 | } catch (TypeError $e) { 36 | throw new UnexpectedValueException( 37 | message: $e->getMessage(), 38 | previous: $e, 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /webapp/php/var/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /webapp/python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-bookworm 2 | 3 | WORKDIR /tmp 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | RUN apt-get update && \ 6 | apt-get -y upgrade && \ 7 | apt-get install -y curl wget gcc g++ make sqlite3 locales locales-all && \ 8 | wget -q https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 9 | apt-get -y install ./mysql-apt-config_0.8.29-1_all.deb && \ 10 | apt-get -y update && \ 11 | apt-get -y install default-mysql-client pdns-server pdns-backend-mysql && \ 12 | pip install pipenv 13 | RUN locale-gen en_US.UTF-8 14 | RUN useradd --uid=1001 --create-home isucon 15 | USER isucon 16 | 17 | RUN mkdir -p /home/isucon/webapp/python 18 | WORKDIR /home/isucon/webapp/python 19 | COPY --chown=isucon:isucon Pipfile /home/isucon/webapp/python/ 20 | COPY --chown=isucon:isucon Pipfile.lock /home/isucon/webapp/python/ 21 | RUN pipenv install 22 | COPY --chown=isucon:isucon models.py /home/isucon/webapp/python/ 23 | COPY --chown=isucon:isucon app.py /home/isucon/webapp/python/ 24 | 25 | # ENV GOPATH=/home/isucon/tmp/go 26 | # ENV GOCACHE=/home/isucon/tmp/go/.cache 27 | 28 | ENV LANG en_US.UTF-8 29 | ENV LANGUAGE en_US:en 30 | ENV LC_ALL en_US.UTF-8 31 | 32 | EXPOSE 8080 33 | CMD ["pipenv", "run", "python", "app.py"] 34 | -------------------------------------------------------------------------------- /webapp/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | flask = "*" 8 | pymysql = "*" 9 | bcrypt = "*" 10 | sqlalchemy = "*" 11 | mysql-connector-python = "*" 12 | 13 | [dev-packages] 14 | flake8 = "*" 15 | isort = "*" 16 | black = "*" 17 | mypy = "*" 18 | 19 | [requires] 20 | python_version = "3.12" 21 | python_full_version = "3.12.0" 22 | -------------------------------------------------------------------------------- /webapp/python/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | plugins = sqlmypy 3 | disallow_untyped_calls = True 4 | disallow_untyped_defs = True 5 | ignore_missing_imports = True 6 | no_implicit_optional = True 7 | follow_imports = skip 8 | warn_return_any = True 9 | -------------------------------------------------------------------------------- /webapp/ruby/.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | -------------------------------------------------------------------------------- /webapp/ruby/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.2.2-bookworm 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update && \ 5 | apt-get install -y lsb-release locales locales-all && \ 6 | curl -sSfLo /tmp/mysql-apt-config.deb https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 7 | apt-get install -y /tmp/mysql-apt-config.deb && \ 8 | apt-get update && \ 9 | apt-get install -y default-mysql-client pdns-server pdns-backend-mysql 10 | 11 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 12 | 13 | RUN locale-gen en_US.UTF-8 14 | RUN useradd --uid=1001 --create-home isucon 15 | USER isucon 16 | 17 | RUN mkdir -p /home/isucon/webapp/ruby 18 | WORKDIR /home/isucon/webapp/ruby 19 | 20 | COPY --chown=isucon:isucon ./Gemfile ./Gemfile.lock ./ 21 | ENV BUNDLE_DEPLOYMENT=1 BUNDLE_JOBS=8 22 | RUN bundle install 23 | COPY ./app.rb ./config.ru ./ 24 | 25 | ENV LANG en_US.UTF-8 26 | ENV LANGUAGE en_US:en 27 | ENV LC_ALL en_US.UTF-8 28 | 29 | ENV TZ utc 30 | 31 | ENV RUBY_YJIT_ENABLE=1 32 | 33 | EXPOSE 8080 34 | CMD ["bundle", "exec", "puma", "--bind", "tcp://0.0.0.0:8080", "--workers", "8", "--threads", "0:8", "--environment", "production"] 35 | -------------------------------------------------------------------------------- /webapp/ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem 'bcrypt' 6 | gem 'mysql2' 7 | gem 'mysql2-cs-bind' 8 | gem 'puma' 9 | gem 'sinatra' 10 | gem 'sinatra-contrib' 11 | -------------------------------------------------------------------------------- /webapp/ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | bcrypt (3.1.19) 5 | multi_json (1.15.0) 6 | mustermann (3.0.0) 7 | ruby2_keywords (~> 0.0.1) 8 | mysql2 (0.5.5) 9 | mysql2-cs-bind (0.1.1) 10 | mysql2 11 | nio4r (2.5.9) 12 | puma (6.4.0) 13 | nio4r (~> 2.0) 14 | rack (2.2.8) 15 | rack-protection (3.1.0) 16 | rack (~> 2.2, >= 2.2.4) 17 | ruby2_keywords (0.0.5) 18 | sinatra (3.1.0) 19 | mustermann (~> 3.0) 20 | rack (~> 2.2, >= 2.2.4) 21 | rack-protection (= 3.1.0) 22 | tilt (~> 2.0) 23 | sinatra-contrib (3.1.0) 24 | multi_json 25 | mustermann (~> 3.0) 26 | rack-protection (= 3.1.0) 27 | sinatra (= 3.1.0) 28 | tilt (~> 2.0) 29 | tilt (2.3.0) 30 | 31 | PLATFORMS 32 | ruby 33 | 34 | DEPENDENCIES 35 | bcrypt 36 | mysql2 37 | mysql2-cs-bind 38 | puma 39 | sinatra 40 | sinatra-contrib 41 | 42 | BUNDLED WITH 43 | 2.4.10 44 | -------------------------------------------------------------------------------- /webapp/ruby/config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'app' 4 | 5 | run Isupipe::App 6 | -------------------------------------------------------------------------------- /webapp/rust/.gitignore: -------------------------------------------------------------------------------- 1 | ./target/ -------------------------------------------------------------------------------- /webapp/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "isupipe" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | async-session = "3" 9 | axum = { version = "0.6", features = ["headers", "tracing"] } 10 | axum-extra = { version = "0.8", features = ["cookie-signed", "cookie-key-expansion"] } 11 | base64 = "0.21" 12 | bcrypt = "0.15" 13 | chrono = { version = "0.4", features = ["serde"] } 14 | hyper = "0.14" 15 | listenfd = "1" 16 | num-traits = "0.2" 17 | serde = { version = "1", features = ["derive"] } 18 | sha2 = "0.10" 19 | sqlx = { version = "0.7", default-features = false, features = ["macros", "runtime-tokio", "mysql", "rust_decimal"] } 20 | thiserror = "1" 21 | time = "0.3" 22 | tokio = { version = "1", features = ["macros", "rt-multi-thread", "process", "fs"] } 23 | tokio-util = { version = "0.7", features = ["io"] } 24 | tower-http = { version = "0.4", features = ["trace"] } 25 | tracing = "0.1" 26 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 27 | uuid = { version = "1", features = ["v4"] } 28 | -------------------------------------------------------------------------------- /webapp/rust/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.73.0-bookworm 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update && \ 5 | apt-get install -y lsb-release locales locales-all && \ 6 | curl -sSfLo /tmp/mysql-apt-config.deb https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb && \ 7 | apt-get install -y /tmp/mysql-apt-config.deb && \ 8 | apt-get update && \ 9 | apt-get install -y default-mysql-client pdns-server pdns-backend-mysql 10 | 11 | RUN rm -f /etc/powerdns/pdns.d/bind.conf 12 | 13 | RUN locale-gen en_US.UTF-8 14 | RUN useradd --uid=1001 --create-home isucon 15 | USER isucon 16 | 17 | RUN mkdir -p /home/isucon/webapp/rust 18 | WORKDIR /home/isucon/webapp/rust 19 | COPY --chown=isucon:isucon ./Cargo.toml ./Cargo.lock ./ 20 | RUN mkdir src && echo 'fn main() {}' > ./src/main.rs && cargo build --release --locked && rm src/main.rs target/release/deps/isupipe-* 21 | COPY --chown=isucon:isucon ./src/ ./src/ 22 | RUN cargo build --release --locked --frozen 23 | 24 | ENV LANG en_US.UTF-8 25 | ENV LANGUAGE en_US:en 26 | ENV LC_ALL en_US.UTF-8 27 | 28 | ENV TZ utc 29 | 30 | EXPOSE 8080 31 | CMD ["/home/isucon/webapp/rust/target/release/isupipe"] 32 | -------------------------------------------------------------------------------- /webapp/sql/init.sql: -------------------------------------------------------------------------------- 1 | TRUNCATE TABLE themes; 2 | TRUNCATE TABLE icons; 3 | TRUNCATE TABLE reservation_slots; 4 | TRUNCATE TABLE livestream_viewers_history; 5 | TRUNCATE TABLE livecomment_reports; 6 | TRUNCATE TABLE ng_words; 7 | TRUNCATE TABLE reactions; 8 | TRUNCATE TABLE tags; 9 | TRUNCATE TABLE livestream_tags; 10 | TRUNCATE TABLE livecomments; 11 | TRUNCATE TABLE livestreams; 12 | TRUNCATE TABLE users; 13 | 14 | ALTER TABLE `themes` auto_increment = 1; 15 | ALTER TABLE `icons` auto_increment = 1; 16 | ALTER TABLE `reservation_slots` auto_increment = 1; 17 | ALTER TABLE `livestream_tags` auto_increment = 1; 18 | ALTER TABLE `livestream_viewers_history` auto_increment = 1; 19 | ALTER TABLE `livecomment_reports` auto_increment = 1; 20 | ALTER TABLE `ng_words` auto_increment = 1; 21 | ALTER TABLE `reactions` auto_increment = 1; 22 | ALTER TABLE `tags` auto_increment = 1; 23 | ALTER TABLE `livecomments` auto_increment = 1; 24 | ALTER TABLE `livestreams` auto_increment = 1; 25 | ALTER TABLE `users` auto_increment = 1; -------------------------------------------------------------------------------- /webapp/sql/initdb.d/00_create_database.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS `isupipe`; 2 | 3 | DROP USER IF EXISTS `isucon`@`%`; 4 | CREATE USER isucon IDENTIFIED BY 'isucon'; 5 | GRANT ALL PRIVILEGES ON isupipe.* TO 'isucon'@'%'; 6 | 7 | CREATE DATABASE IF NOT EXISTS `isudns`; 8 | 9 | DROP USER IF EXISTS `isudns`@`%`; 10 | CREATE USER isudns IDENTIFIED BY 'isudns'; 11 | GRANT ALL PRIVILEGES ON isudns.* TO 'isudns'@'%'; 12 | --------------------------------------------------------------------------------