tetsuzawa / isucon9-qualify-suburi-20230114

0 stars 0 forks source link

ベンチマークスレッド #4

Open tetsuzawa opened 1 year ago

tetsuzawa commented 1 year ago

ベンチマーク実行方法

cd isucari
./bin/benchmarker -target-url=http://192.168.0.11:80 -payment-url=http://192.168.0.10:5555 -shipment-url=http://192.168.0.10:7000
tetsuzawa commented 1 year ago

初期スコア

2023/01/14 01:55:07 main.go:212: 2510 0
{"pass":true,"score":2510,"campaign":0,"language":"Go","messages":[]}
tetsuzawa commented 1 year ago

alp

COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
180 0 169 0 11 0 GET /users/transactions.json 0.020 7.291 518.057 2.878 5.632 0.000 26998.000 19559.389
1096 0 1095 0 1 0 GET /new_items/\d+.json 0.020 0.776 110.252 0.101 0.320 0.000 24032.000 23461.302
63 0 35 0 28 0 POST /buy 0.000 1.628 61.168 0.971 1.612 29.000 49.000 36.333
296 0 296 0 0 0 GET /users/\d+.json 0.012 0.692 29.892 0.101 0.296 95.000 24011.000 14354.696
136 0 136 0 0 0 GET /new_items.json 0.040 0.596 28.632 0.211 0.496 23076.000 23756.000 23419.066
49 0 31 0 18 0 POST /ship_done 0.804 0.812 26.602 0.543 0.812 29.000 83.000 39.755
46 0 31 0 15 0 POST /ship 0.738 0.812 23.370 0.508 0.808 0.000 61.000 51.152
28 0 28 0 0 0 POST /complete 0.004 0.808 20.144 0.719 0.808 34.000 34.000 34.000
62 0 54 0 8 0 POST /login 0.068 0.584 12.528 0.202 0.500 73.000 107.000 94.468
3063 0 3062 0 1 0 GET /items/\d+.json 0.006 0.076 5.706 0.002 0.004 0.000 4151.000 2188.015
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.332 5.064 0.087 0.212 52251.000 144808.000 81517.586
1 0 1 0 0 0 POST /initialize 5.036 5.036 5.036 5.036 5.036 31.000 31.000 31.000
54 0 54 0 0 0 GET /settings 0.000 0.180 2.212 0.041 0.164 2939.000 2956.000 2946.648
62 0 41 0 21 0 POST /sell 0.004 0.488 1.864 0.030 0.164 13.000 106.000 35.806
13 0 13 0 0 0 POST /bump 0.004 0.136 0.192 0.015 0.136 89.000 92.000 90.769
44 0 31 0 13 0 GET /transactions/\d+.png 0.000 0.004 0.040 0.001 0.004 33.000 634.000 446.614
10 0 3 0 7 0 POST /items/edit 0.000 0.020 0.036 0.004 0.020 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.004 0.004 0.004 0.004 0.004 90365.000 90365.000 90365.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 1502.000 1502.000 1502.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.000 0.000 0.000 0.000 0.000 508459.000 508459.000 508459.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /reports.json 0.000 0.000 0.000 0.000 0.000 124345.000 124345.000 124345.000
tetsuzawa commented 1 year ago

pprof

Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)
Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.001.pb.gz
Total: 15.47s
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       10ms (flat, cum) 0.065% of Total
         .          .    173:       }
         .          .    174:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .    175:   }
         .          .    176:
         .          .    177:   ssr := &APIShipmentStatusRes{}
         .       10ms    178:   err = json.NewDecoder(res.Body).Decode(&ssr)
         .          .    179:   if err != nil {
         .          .    180:       return nil, err
         .          .    181:   }
         .          .    182:
         .          .    183:   return ssr, nil
ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go
      30ms      6.68s (flat, cum) 43.18% of Total
         .          .    413:   userSimple.AccountName = user.AccountName
         .          .    414:   userSimple.NumSellItems = user.NumSellItems
         .          .    415:   return userSimple, err
         .          .    416:}
         .          .    417:
         .       60ms    418:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .      4.46s    419:   err = sqlx.Get(q, &category, "SELECT * FROM `categories` WHERE `id` = ?", categoryID)
      30ms       30ms    420:   if category.ParentID != 0 {
         .      2.13s    421:       parentCategory, err := getCategoryByID(q, category.ParentID)
         .          .    422:       if err != nil {
         .          .    423:           return category, err
         .          .    424:       }
         .          .    425:       category.ParentCategoryName = parentCategory.CategoryName
         .          .    426:   }
ROUTINE ======================== main.getConfigByName in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.52% of Total
         .          .    427:   return category, err
         .          .    428:}
         .          .    429:
         .          .    430:func getConfigByName(name string) (string, error) {
         .          .    431:   config := Config{}
         .       80ms    432:   err := dbx.Get(&config, "SELECT * FROM `configs` WHERE `name` = ?", name)
         .          .    433:   if err == sql.ErrNoRows {
         .          .    434:       return "", nil
         .          .    435:   }
         .          .    436:   if err != nil {
         .          .    437:       log.Print(err)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.39% of Total
         .          .   2319:       Error string `json:"error"`
         .          .   2320:   }{Error: msg})
         .          .   2321:}
         .          .   2322:
         .          .   2323:func getImageURL(imageName string) string {
         .       60ms   2324:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2325:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
         0      900ms (flat, cum)  5.82% of Total
         .          .   1023:   json.NewEncoder(w).Encode(rts)
         .          .   1024:
         .          .   1025:}
         .          .   1026:
         .          .   1027:func getItem(w http.ResponseWriter, r *http.Request) {
         .       10ms   1028:   itemIDStr := pat.Param(r, "item_id")
         .          .   1029:   itemID, err := strconv.ParseInt(itemIDStr, 10, 64)
         .          .   1030:   if err != nil || itemID <= 0 {
         .          .   1031:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1032:       return
         .          .   1033:   }
         .          .   1034:
         .      330ms   1035:   user, errCode, errMsg := getUser(r)
         .          .   1036:   if errMsg != "" {
         .          .   1037:       outputErrorMsg(w, errCode, errMsg)
         .          .   1038:       return
         .          .   1039:   }
         .          .   1040:
         .          .   1041:   item := Item{}
         .      110ms   1042:   err = dbx.Get(&item, "SELECT * FROM `items` WHERE `id` = ?", itemID)
         .          .   1043:   if err == sql.ErrNoRows {
         .          .   1044:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1045:       return
         .          .   1046:   }
         .          .   1047:   if err != nil {
         .          .   1048:       log.Print(err)
         .          .   1049:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1050:       return
         .          .   1051:   }
         .          .   1052:
         .      210ms   1053:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1054:   if err != nil {
         .          .   1055:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1056:       return
         .          .   1057:   }
         .          .   1058:
         .      170ms   1059:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1060:   if err != nil {
         .          .   1061:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1062:       return
         .          .   1063:   }
         .          .   1064:
         .          .   1065:   itemDetail := ItemDetail{
         .          .   1066:       ID:       item.ID,
         .          .   1067:       SellerID: item.SellerID,
         .          .   1068:       Seller:   &seller,
         .          .   1069:       // BuyerID
         .          .   1070:       // Buyer
         .          .   1071:       Status:      item.Status,
         .          .   1072:       Name:        item.Name,
         .          .   1073:       Price:       item.Price,
         .          .   1074:       Description: item.Description,
         .          .   1075:       ImageURL:    getImageURL(item.ImageName),
         .          .   1076:       CategoryID:  item.CategoryID,
         .          .   1077:       // TransactionEvidenceID
         .          .   1078:       // TransactionEvidenceStatus
         .          .   1079:       // ShippingStatus
         .          .   1080:       Category:  &category,
         .          .   1081:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1082:   }
         .          .   1083:
         .          .   1084:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .       10ms   1085:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1086:       if err != nil {
         .          .   1087:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1088:           return
         .          .   1089:       }
         .          .   1090:       itemDetail.BuyerID = item.BuyerID
         .          .   1091:       itemDetail.Buyer = &buyer
         .          .   1092:
         .          .   1093:       transactionEvidence := TransactionEvidence{}
         .          .   1094:       err = dbx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ?", item.ID)
         .          .   1095:       if err != nil && err != sql.ErrNoRows {
         .          .   1096:           // It's able to ignore ErrNoRows
         .          .   1097:           log.Print(err)
         .          .   1098:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1099:           return
         .          .   1100:       }
         .          .   1101:
         .          .   1102:       if transactionEvidence.ID > 0 {
         .          .   1103:           shipping := Shipping{}
         .       10ms   1104:           err = dbx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ?", transactionEvidence.ID)
         .          .   1105:           if err == sql.ErrNoRows {
         .          .   1106:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1107:               return
         .          .   1108:           }
         .          .   1109:           if err != nil {
         .          .   1110:               log.Print(err)
         .          .   1111:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1112:               return
         .          .   1113:           }
         .          .   1114:
         .          .   1115:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1116:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1117:           itemDetail.ShippingStatus = shipping.Status
         .          .   1118:       }
         .          .   1119:   }
         .          .   1120:
         .       10ms   1121:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       40ms   1122:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1123:}
         .          .   1124:
         .          .   1125:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1126:   rie := reqItemEdit{}
         .          .   1127:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
         0      5.29s (flat, cum) 34.20% of Total
         .          .    605:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .    606:   json.NewEncoder(w).Encode(rni)
         .          .    607:}
         .          .    608:
         .          .    609:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .       10ms    610:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    611:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
         .          .    612:   if err != nil || rootCategoryID <= 0 {
         .          .    613:       outputErrorMsg(w, http.StatusBadRequest, "incorrect category id")
         .          .    614:       return
         .          .    615:   }
         .          .    616:
         .       50ms    617:   rootCategory, err := getCategoryByID(dbx, rootCategoryID)
         .          .    618:   if err != nil || rootCategory.ParentID != 0 {
         .          .    619:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    620:       return
         .          .    621:   }
         .          .    622:
         .          .    623:   var categoryIDs []int
         .       40ms    624:   err = dbx.Select(&categoryIDs, "SELECT id FROM `categories` WHERE parent_id=?", rootCategory.ID)
         .          .    625:   if err != nil {
         .          .    626:       log.Print(err)
         .          .    627:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    628:       return
         .          .    629:   }
         .          .    630:
         .          .    631:   query := r.URL.Query()
         .          .    632:   itemIDStr := query.Get("item_id")
         .          .    633:   var itemID int64
         .          .    634:   if itemIDStr != "" {
         .          .    635:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    636:       if err != nil || itemID <= 0 {
         .          .    637:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    638:           return
         .          .    639:       }
         .          .    640:   }
         .          .    641:
         .          .    642:   createdAtStr := query.Get("created_at")
         .          .    643:   var createdAt int64
         .          .    644:   if createdAtStr != "" {
         .          .    645:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    646:       if err != nil || createdAt <= 0 {
         .          .    647:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    648:           return
         .          .    649:       }
         .          .    650:   }
         .          .    651:
         .          .    652:   var inQuery string
         .          .    653:   var inArgs []interface{}
         .          .    654:   if itemID > 0 && createdAt > 0 {
         .          .    655:       // paging
         .       10ms    656:       inQuery, inArgs, err = sqlx.In(
         .          .    657:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND category_id IN (?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    658:           ItemStatusOnSale,
         .          .    659:           ItemStatusSoldOut,
         .          .    660:           categoryIDs,
         .          .    661:           time.Unix(createdAt, 0),
         .          .    662:           time.Unix(createdAt, 0),
         .          .    663:           itemID,
         .          .    664:           ItemsPerPage+1,
         .          .    665:       )
         .          .    666:       if err != nil {
         .          .    667:           log.Print(err)
         .          .    668:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    669:           return
         .          .    670:       }
         .          .    671:   } else {
         .          .    672:       // 1st page
         .          .    673:       inQuery, inArgs, err = sqlx.In(
         .          .    674:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    675:           ItemStatusOnSale,
         .          .    676:           ItemStatusSoldOut,
         .          .    677:           categoryIDs,
         .          .    678:           ItemsPerPage+1,
         .          .    679:       )
         .          .    680:       if err != nil {
         .          .    681:           log.Print(err)
         .          .    682:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    683:           return
         .          .    684:       }
         .          .    685:   }
         .          .    686:
         .          .    687:   items := []Item{}
         .      260ms    688:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    689:
         .          .    690:   if err != nil {
         .          .    691:       log.Print(err)
         .          .    692:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    693:       return
         .          .    694:   }
         .          .    695:
         .          .    696:   itemSimples := []ItemSimple{}
         .       10ms    697:   for _, item := range items {
         .      1.76s    698:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    699:       if err != nil {
         .          .    700:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    701:           return
         .          .    702:       }
         .      2.97s    703:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    704:       if err != nil {
         .          .    705:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    706:           return
         .          .    707:       }
         .       10ms    708:       itemSimples = append(itemSimples, ItemSimple{
         .          .    709:           ID:         item.ID,
         .          .    710:           SellerID:   item.SellerID,
         .          .    711:           Seller:     &seller,
         .          .    712:           Status:     item.Status,
         .          .    713:           Name:       item.Name,
         .          .    714:           Price:      item.Price,
         .       50ms    715:           ImageURL:   getImageURL(item.ImageName),
         .          .    716:           CategoryID: item.CategoryID,
         .          .    717:           Category:   &category,
         .          .    718:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    719:       })
         .          .    720:   }
         .          .    721:
         .          .    722:   hasNext := false
         .          .    723:   if len(itemSimples) > ItemsPerPage {
         .          .    724:       hasNext = true
         .          .    725:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    726:   }
         .          .    727:
         .          .    728:   rni := resNewItems{
         .          .    729:       RootCategoryID:   rootCategory.ID,
         .          .    730:       RootCategoryName: rootCategory.CategoryName,
         .          .    731:       Items:            itemSimples,
         .          .    732:       HasNext:          hasNext,
         .          .    733:   }
         .          .    734:
         .          .    735:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      120ms    736:   json.NewEncoder(w).Encode(rni)
         .          .    737:
         .          .    738:}
         .          .    739:
         .          .    740:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    741:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      810ms (flat, cum)  5.24% of Total
         .          .    534:   }
         .          .    535:
         .          .    536:   items := []Item{}
         .          .    537:   if itemID > 0 && createdAt > 0 {
         .          .    538:       // paging
         .       30ms    539:       err := dbx.Select(&items,
         .          .    540:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    541:           ItemStatusOnSale,
         .          .    542:           ItemStatusSoldOut,
         .          .    543:           time.Unix(createdAt, 0),
         .          .    544:           time.Unix(createdAt, 0),
         .          .    545:           itemID,
         .          .    546:           ItemsPerPage+1,
         .          .    547:       )
         .          .    548:       if err != nil {
         .          .    549:           log.Print(err)
         .          .    550:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    551:           return
         .          .    552:       }
         .          .    553:   } else {
         .          .    554:       // 1st page
         .          .    555:       err := dbx.Select(&items,
         .          .    556:           "SELECT * FROM `items` WHERE `status` IN (?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    557:           ItemStatusOnSale,
         .          .    558:           ItemStatusSoldOut,
         .          .    559:           ItemsPerPage+1,
         .          .    560:       )
         .          .    561:       if err != nil {
         .          .    562:           log.Print(err)
         .          .    563:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    564:           return
         .          .    565:       }
         .          .    566:   }
         .          .    567:
         .          .    568:   itemSimples := []ItemSimple{}
         .          .    569:   for _, item := range items {
         .      260ms    570:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    571:       if err != nil {
         .          .    572:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    573:           return
         .          .    574:       }
         .      500ms    575:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    576:       if err != nil {
         .          .    577:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    578:           return
         .          .    579:       }
         .          .    580:       itemSimples = append(itemSimples, ItemSimple{
         .          .    581:           ID:         item.ID,
         .          .    582:           SellerID:   item.SellerID,
         .          .    583:           Seller:     &seller,
         .          .    584:           Status:     item.Status,
         .          .    585:           Name:       item.Name,
         .          .    586:           Price:      item.Price,
         .          .    587:           ImageURL:   getImageURL(item.ImageName),
         .          .    588:           CategoryID: item.CategoryID,
         .          .    589:           Category:   &category,
         .          .    590:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    591:       })
         .          .    592:   }
         .          .    593:
         .          .    594:   hasNext := false
         .          .    595:   if len(itemSimples) > ItemsPerPage {
         .          .    596:       hasNext = true
         .          .    597:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    598:   }
         .          .    599:
         .          .    600:   rni := resNewItems{
         .          .    601:       Items:   itemSimples,
         .          .    602:       HasNext: hasNext,
         .          .    603:   }
         .          .    604:
         .          .    605:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms    606:   json.NewEncoder(w).Encode(rni)
         .          .    607:}
         .          .    608:
         .          .    609:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    610:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    611:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      250ms (flat, cum)  1.62% of Total
         .          .    366:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    367:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    368:}
         .          .    369:
         .          .    370:func getSession(r *http.Request) *sessions.Session {
         .      250ms    371:   session, _ := store.Get(r, sessionName)
         .          .    372:
         .          .    373:   return session
         .          .    374:}
         .          .    375:
         .          .    376:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.065% of Total
         .          .   2148:}
         .          .   2149:
         .          .   2150:func getSettings(w http.ResponseWriter, r *http.Request) {
         .          .   2151:   csrfToken := getCSRFToken(r)
         .          .   2152:
         .       10ms   2153:   user, _, errMsg := getUser(r)
         .          .   2154:
         .          .   2155:   ress := resSetting{}
         .          .   2156:   ress.CSRFToken = csrfToken
         .          .   2157:   if errMsg == "" {
         .          .   2158:       ress.User = &user
ROUTINE ======================== main.getShipmentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.52% of Total
         .          .    447:   }
         .          .    448:   return val
         .          .    449:}
         .          .    450:
         .          .    451:func getShipmentServiceURL() string {
         .       80ms    452:   val, _ := getConfigByName("shipment_service_url")
         .          .    453:   if val == "" {
         .          .    454:       return DefaultShipmentServiceURL
         .          .    455:   }
         .          .    456:   return val
         .          .    457:}
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      530ms (flat, cum)  3.43% of Total
         .          .    845:   json.NewEncoder(w).Encode(rui)
         .          .    846:}
         .          .    847:
         .          .    848:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    849:
         .       10ms    850:   user, errCode, errMsg := getUser(r)
         .          .    851:   if errMsg != "" {
         .          .    852:       outputErrorMsg(w, errCode, errMsg)
         .          .    853:       return
         .          .    854:   }
         .          .    855:
         .          .    856:   query := r.URL.Query()
         .          .    857:   itemIDStr := query.Get("item_id")
         .          .    858:   var err error
         .          .    859:   var itemID int64
         .          .    860:   if itemIDStr != "" {
         .          .    861:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    862:       if err != nil || itemID <= 0 {
         .          .    863:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    864:           return
         .          .    865:       }
         .          .    866:   }
         .          .    867:
         .          .    868:   createdAtStr := query.Get("created_at")
         .          .    869:   var createdAt int64
         .          .    870:   if createdAtStr != "" {
         .          .    871:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    872:       if err != nil || createdAt <= 0 {
         .          .    873:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    874:           return
         .          .    875:       }
         .          .    876:   }
         .          .    877:
         .          .    878:   tx := dbx.MustBegin()
         .          .    879:   items := []Item{}
         .          .    880:   if itemID > 0 && createdAt > 0 {
         .          .    881:       // paging
         .          .    882:       err := tx.Select(&items,
         .          .    883:           "SELECT * FROM `items` WHERE (`seller_id` = ? OR `buyer_id` = ?) AND `status` IN (?,?,?,?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    884:           user.ID,
         .          .    885:           user.ID,
         .          .    886:           ItemStatusOnSale,
         .          .    887:           ItemStatusTrading,
         .          .    888:           ItemStatusSoldOut,
         .          .    889:           ItemStatusCancel,
         .          .    890:           ItemStatusStop,
         .          .    891:           time.Unix(createdAt, 0),
         .          .    892:           time.Unix(createdAt, 0),
         .          .    893:           itemID,
         .          .    894:           TransactionsPerPage+1,
         .          .    895:       )
         .          .    896:       if err != nil {
         .          .    897:           log.Print(err)
         .          .    898:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    899:           tx.Rollback()
         .          .    900:           return
         .          .    901:       }
         .          .    902:   } else {
         .          .    903:       // 1st page
         .       10ms    904:       err := tx.Select(&items,
         .          .    905:           "SELECT * FROM `items` WHERE (`seller_id` = ? OR `buyer_id` = ?) AND `status` IN (?,?,?,?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    906:           user.ID,
         .          .    907:           user.ID,
         .          .    908:           ItemStatusOnSale,
         .          .    909:           ItemStatusTrading,
         .          .    910:           ItemStatusSoldOut,
         .          .    911:           ItemStatusCancel,
         .          .    912:           ItemStatusStop,
         .          .    913:           TransactionsPerPage+1,
         .          .    914:       )
         .          .    915:       if err != nil {
         .          .    916:           log.Print(err)
         .          .    917:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    918:           tx.Rollback()
         .          .    919:           return
         .          .    920:       }
         .          .    921:   }
         .          .    922:
         .          .    923:   itemDetails := []ItemDetail{}
         .          .    924:   for _, item := range items {
         .       80ms    925:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .    926:       if err != nil {
         .          .    927:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    928:           tx.Rollback()
         .          .    929:           return
         .          .    930:       }
         .      210ms    931:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .    932:       if err != nil {
         .          .    933:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    934:           tx.Rollback()
         .          .    935:           return
         .          .    936:       }
         .          .    937:
         .          .    938:       itemDetail := ItemDetail{
         .          .    939:           ID:       item.ID,
         .          .    940:           SellerID: item.SellerID,
         .          .    941:           Seller:   &seller,
         .          .    942:           // BuyerID
         .          .    943:           // Buyer
         .          .    944:           Status:      item.Status,
         .          .    945:           Name:        item.Name,
         .          .    946:           Price:       item.Price,
         .          .    947:           Description: item.Description,
         .          .    948:           ImageURL:    getImageURL(item.ImageName),
         .          .    949:           CategoryID:  item.CategoryID,
         .          .    950:           // TransactionEvidenceID
         .          .    951:           // TransactionEvidenceStatus
         .          .    952:           // ShippingStatus
         .          .    953:           Category:  &category,
         .          .    954:           CreatedAt: item.CreatedAt.Unix(),
         .          .    955:       }
         .          .    956:
         .          .    957:       if item.BuyerID != 0 {
         .       30ms    958:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .    959:           if err != nil {
         .          .    960:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .    961:               tx.Rollback()
         .          .    962:               return
         .          .    963:           }
         .          .    964:           itemDetail.BuyerID = item.BuyerID
         .          .    965:           itemDetail.Buyer = &buyer
         .          .    966:       }
         .          .    967:
         .          .    968:       transactionEvidence := TransactionEvidence{}
         .       80ms    969:       err = tx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ?", item.ID)
         .          .    970:       if err != nil && err != sql.ErrNoRows {
         .          .    971:           // It's able to ignore ErrNoRows
         .          .    972:           log.Print(err)
         .          .    973:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    974:           tx.Rollback()
         .          .    975:           return
         .          .    976:       }
         .          .    977:
         .          .    978:       if transactionEvidence.ID > 0 {
         .          .    979:           shipping := Shipping{}
         .       40ms    980:           err = tx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ?", transactionEvidence.ID)
         .          .    981:           if err == sql.ErrNoRows {
         .          .    982:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .    983:               tx.Rollback()
         .          .    984:               return
         .          .    985:           }
         .          .    986:           if err != nil {
         .          .    987:               log.Print(err)
         .          .    988:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    989:               tx.Rollback()
         .          .    990:               return
         .          .    991:           }
         .       50ms    992:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .    993:               ReserveID: shipping.ReserveID,
         .          .    994:           })
         .          .    995:           if err != nil {
         .          .    996:               log.Print(err)
         .          .    997:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .    998:               tx.Rollback()
         .          .    999:               return
         .          .   1000:           }
         .          .   1001:
         .          .   1002:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1003:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1004:           itemDetail.ShippingStatus = ssr.Status
         .          .   1005:       }
         .          .   1006:
         .          .   1007:       itemDetails = append(itemDetails, itemDetail)
         .          .   1008:   }
         .          .   1009:   tx.Commit()
         .          .   1010:
         .          .   1011:   hasNext := false
         .          .   1012:   if len(itemDetails) > TransactionsPerPage {
         .          .   1013:       hasNext = true
         .          .   1014:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1015:   }
         .          .   1016:
         .          .   1017:   rts := resTransactions{
         .          .   1018:       Items:   itemDetails,
         .          .   1019:       HasNext: hasNext,
         .          .   1020:   }
         .          .   1021:
         .          .   1022:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1023:   json.NewEncoder(w).Encode(rts)
         .          .   1024:
         .          .   1025:}
         .          .   1026:
         .          .   1027:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1028:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
         0      360ms (flat, cum)  2.33% of Total
         .          .    383:
         .          .    384:   return csrfToken.(string)
         .          .    385:}
         .          .    386:
         .          .    387:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      250ms    388:   session := getSession(r)
         .          .    389:   userID, ok := session.Values["user_id"]
         .          .    390:   if !ok {
         .          .    391:       return user, http.StatusNotFound, "no session"
         .          .    392:   }
         .          .    393:
         .      110ms    394:   err := dbx.Get(&user, "SELECT * FROM `users` WHERE `id` = ?", userID)
         .          .    395:   if err == sql.ErrNoRows {
         .          .    396:       return user, http.StatusNotFound, "user not found"
         .          .    397:   }
         .          .    398:   if err != nil {
         .          .    399:       log.Print(err)
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
         0      730ms (flat, cum)  4.72% of Total
         .          .    736:   json.NewEncoder(w).Encode(rni)
         .          .    737:
         .          .    738:}
         .          .    739:
         .          .    740:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .       10ms    741:   userIDStr := pat.Param(r, "user_id")
         .          .    742:   userID, err := strconv.ParseInt(userIDStr, 10, 64)
         .          .    743:   if err != nil || userID <= 0 {
         .          .    744:       outputErrorMsg(w, http.StatusBadRequest, "incorrect user id")
         .          .    745:       return
         .          .    746:   }
         .          .    747:
         .          .    748:   userSimple, err := getUserSimpleByID(dbx, userID)
         .          .    749:   if err != nil {
         .          .    750:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .    751:       return
         .          .    752:   }
         .          .    753:
         .          .    754:   query := r.URL.Query()
         .          .    755:   itemIDStr := query.Get("item_id")
         .          .    756:   var itemID int64
         .          .    757:   if itemIDStr != "" {
         .          .    758:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    759:       if err != nil || itemID <= 0 {
         .          .    760:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    761:           return
         .          .    762:       }
         .          .    763:   }
         .          .    764:
         .          .    765:   createdAtStr := query.Get("created_at")
         .          .    766:   var createdAt int64
         .          .    767:   if createdAtStr != "" {
         .          .    768:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    769:       if err != nil || createdAt <= 0 {
         .          .    770:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    771:           return
         .          .    772:       }
         .          .    773:   }
         .          .    774:
         .          .    775:   items := []Item{}
         .          .    776:   if itemID > 0 && createdAt > 0 {
         .          .    777:       // paging
         .       50ms    778:       err := dbx.Select(&items,
         .          .    779:           "SELECT * FROM `items` WHERE `seller_id` = ? AND `status` IN (?,?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    780:           userSimple.ID,
         .          .    781:           ItemStatusOnSale,
         .          .    782:           ItemStatusTrading,
         .          .    783:           ItemStatusSoldOut,
         .          .    784:           time.Unix(createdAt, 0),
         .          .    785:           time.Unix(createdAt, 0),
         .          .    786:           itemID,
         .          .    787:           ItemsPerPage+1,
         .          .    788:       )
         .          .    789:       if err != nil {
         .          .    790:           log.Print(err)
         .          .    791:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    792:           return
         .          .    793:       }
         .          .    794:   } else {
         .          .    795:       // 1st page
         .       40ms    796:       err := dbx.Select(&items,
         .          .    797:           "SELECT * FROM `items` WHERE `seller_id` = ? AND `status` IN (?,?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    798:           userSimple.ID,
         .          .    799:           ItemStatusOnSale,
         .          .    800:           ItemStatusTrading,
         .          .    801:           ItemStatusSoldOut,
         .          .    802:           ItemsPerPage+1,
         .          .    803:       )
         .          .    804:       if err != nil {
         .          .    805:           log.Print(err)
         .          .    806:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    807:           return
         .          .    808:       }
         .          .    809:   }
         .          .    810:
         .          .    811:   itemSimples := []ItemSimple{}
         .          .    812:   for _, item := range items {
         .      600ms    813:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    814:       if err != nil {
         .          .    815:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    816:           return
         .          .    817:       }
         .          .    818:       itemSimples = append(itemSimples, ItemSimple{
         .          .    819:           ID:         item.ID,
         .          .    820:           SellerID:   item.SellerID,
         .          .    821:           Seller:     &userSimple,
         .          .    822:           Status:     item.Status,
         .          .    823:           Name:       item.Name,
         .          .    824:           Price:      item.Price,
         .       10ms    825:           ImageURL:   getImageURL(item.ImageName),
         .          .    826:           CategoryID: item.CategoryID,
         .          .    827:           Category:   &category,
         .          .    828:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    829:       })
         .          .    830:   }
         .          .    831:
         .          .    832:   hasNext := false
         .          .    833:   if len(itemSimples) > ItemsPerPage {
         .          .    834:       hasNext = true
         .          .    835:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    836:   }
         .          .    837:
         .          .    838:   rui := resUserItems{
         .          .    839:       User:    &userSimple,
         .          .    840:       Items:   itemSimples,
         .          .    841:       HasNext: hasNext,
         .          .    842:   }
         .          .    843:
         .          .    844:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms    845:   json.NewEncoder(w).Encode(rui)
         .          .    846:}
         .          .    847:
         .          .    848:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    849:
         .          .    850:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      2.31s (flat, cum) 14.93% of Total
         .          .    402:
         .          .    403:   return user, http.StatusOK, ""
         .          .    404:}
         .          .    405:
         .          .    406:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       20ms    407:   user := User{}
         .      2.29s    408:   err = sqlx.Get(q, &user, "SELECT * FROM `users` WHERE `id` = ?", userID)
         .          .    409:   if err != nil {
         .          .    410:       return userSimple, err
         .          .    411:   }
         .          .    412:   userSimple.ID = user.ID
         .          .    413:   userSimple.AccountName = user.AccountName
ROUTINE ======================== main.main in /home/isucon/isucari/webapp/go/main.go
         0      100ms (flat, cum)  0.65% of Total
         .          .    362:   mux.HandleFunc(pat.Get("/transactions/:transaction_id"), getIndex)
         .          .    363:   mux.HandleFunc(pat.Get("/users/:user_id"), getIndex)
         .          .    364:   mux.HandleFunc(pat.Get("/users/setting"), getIndex)
         .          .    365:   // Assets
         .          .    366:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .      100ms    367:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    368:}
         .          .    369:
         .          .    370:func getSession(r *http.Request) *sessions.Session {
         .          .    371:   session, _ := store.Get(r, sessionName)
         .          .    372:
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .   1387:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1388:       tx.Rollback()
         .          .   1389:       return
         .          .   1390:   }
         .          .   1391:
         .       20ms   1392:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1393:       ToAddress:   buyer.Address,
         .          .   1394:       ToName:      buyer.AccountName,
         .          .   1395:       FromAddress: seller.Address,
         .          .   1396:       FromName:    seller.AccountName,
         .          .   1397:   })
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .   1820:       tx.Rollback()
         .          .   1821:       return
         .          .   1822:   }
         .          .   1823:
         .          .   1824:   shipping := Shipping{}
         .       10ms   1825:   err = tx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1826:   if err != nil {
         .          .   1827:       log.Print(err)
         .          .   1828:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1829:       tx.Rollback()
         .          .   1830:       return
         .          .   1831:   }
         .          .   1832:
         .       10ms   1833:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1834:       ReserveID: shipping.ReserveID,
         .          .   1835:   })
         .          .   1836:   if err != nil {
         .          .   1837:       log.Print(err)
         .          .   1838:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0      3.92s (flat, cum) 25.34% of Total
         .          .   2202:
         .          .   2203:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2204:       return
         .          .   2205:   }
         .          .   2206:
         .      3.92s   2207:   err = bcrypt.CompareHashAndPassword(u.HashedPassword, []byte(password))
         .          .   2208:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2209:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2210:       return
         .          .   2211:   }
         .          .   2212:   if err != nil {
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0       30ms (flat, cum)  0.19% of Total
         .          .   1891:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   1892:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   1893:}
         .          .   1894:
         .          .   1895:func postSell(w http.ResponseWriter, r *http.Request) {
         .       10ms   1896:   csrfToken := r.FormValue("csrf_token")
         .          .   1897:   name := r.FormValue("name")
         .          .   1898:   description := r.FormValue("description")
         .          .   1899:   priceStr := r.FormValue("price")
         .          .   1900:   categoryIDStr := r.FormValue("category_id")
         .          .   1901:
         .          .   1902:   f, header, err := r.FormFile("image")
         .          .   1903:   if err != nil {
         .          .   1904:       log.Print(err)
         .          .   1905:       outputErrorMsg(w, http.StatusBadRequest, "image error")
         .          .   1906:       return
         .          .   1907:   }
         .          .   1908:   defer f.Close()
         .          .   1909:
         .          .   1910:   if csrfToken != getCSRFToken(r) {
         .          .   1911:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1912:       return
         .          .   1913:   }
         .          .   1914:
         .          .   1915:   categoryID, err := strconv.Atoi(categoryIDStr)
         .          .   1916:   if err != nil || categoryID < 0 {
         .          .   1917:       outputErrorMsg(w, http.StatusBadRequest, "category id error")
         .          .   1918:       return
         .          .   1919:   }
         .          .   1920:
         .          .   1921:   price, err := strconv.Atoi(priceStr)
         .          .   1922:   if err != nil {
         .          .   1923:       outputErrorMsg(w, http.StatusBadRequest, "price error")
         .          .   1924:       return
         .          .   1925:   }
         .          .   1926:
         .          .   1927:   if name == "" || description == "" || price == 0 || categoryID == 0 {
         .          .   1928:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   1929:
         .          .   1930:       return
         .          .   1931:   }
         .          .   1932:
         .          .   1933:   if price < ItemMinPrice || price > ItemMaxPrice {
         .          .   1934:       outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg)
         .          .   1935:
         .          .   1936:       return
         .          .   1937:   }
         .          .   1938:
         .       10ms   1939:   category, err := getCategoryByID(dbx, categoryID)
         .          .   1940:   if err != nil || category.ParentID == 0 {
         .          .   1941:       log.Print(categoryID, category)
         .          .   1942:       outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID")
         .          .   1943:       return
         .          .   1944:   }
         .          .   1945:
         .          .   1946:   user, errCode, errMsg := getUser(r)
         .          .   1947:   if errMsg != "" {
         .          .   1948:       outputErrorMsg(w, errCode, errMsg)
         .          .   1949:       return
         .          .   1950:   }
         .          .   1951:
         .       10ms   1952:   img, err := ioutil.ReadAll(f)
         .          .   1953:   if err != nil {
         .          .   1954:       log.Print(err)
         .          .   1955:       outputErrorMsg(w, http.StatusInternalServerError, "image error")
         .          .   1956:       return
         .          .   1957:   }
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .   1478:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1479:
         .          .   1480:       return
         .          .   1481:   }
         .          .   1482:
         .       10ms   1483:   seller, errCode, errMsg := getUser(r)
         .          .   1484:   if errMsg != "" {
         .          .   1485:       outputErrorMsg(w, errCode, errMsg)
         .          .   1486:       return
         .          .   1487:   }
         .          .   1488:
         .          .   1489:   transactionEvidence := TransactionEvidence{}
         .          .   1490:   err = dbx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ?", itemID)
         .          .   1491:   if err == sql.ErrNoRows {
         .          .   1492:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1493:       return
         .          .   1494:   }
         .          .   1495:   if err != nil {
         .          .   1496:       log.Print(err)
         .          .   1497:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1498:
         .          .   1499:       return
         .          .   1500:   }
         .          .   1501:
         .          .   1502:   if transactionEvidence.SellerID != seller.ID {
         .          .   1503:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1504:       return
         .          .   1505:   }
         .          .   1506:
         .          .   1507:   tx := dbx.MustBegin()
         .          .   1508:
         .          .   1509:   item := Item{}
         .          .   1510:   err = tx.Get(&item, "SELECT * FROM `items` WHERE `id` = ? FOR UPDATE", itemID)
         .          .   1511:   if err == sql.ErrNoRows {
         .          .   1512:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1513:       tx.Rollback()
         .          .   1514:       return
         .          .   1515:   }
         .          .   1516:   if err != nil {
         .          .   1517:       log.Print(err)
         .          .   1518:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1519:       tx.Rollback()
         .          .   1520:       return
         .          .   1521:   }
         .          .   1522:
         .          .   1523:   if item.Status != ItemStatusTrading {
         .          .   1524:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1525:       tx.Rollback()
         .          .   1526:       return
         .          .   1527:   }
         .          .   1528:
         .          .   1529:   err = tx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `id` = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1530:   if err == sql.ErrNoRows {
         .          .   1531:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1532:       tx.Rollback()
         .          .   1533:       return
         .          .   1534:   }
         .          .   1535:   if err != nil {
         .          .   1536:       log.Print(err)
         .          .   1537:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1538:       tx.Rollback()
         .          .   1539:       return
         .          .   1540:   }
         .          .   1541:
         .          .   1542:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1543:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1544:       tx.Rollback()
         .          .   1545:       return
         .          .   1546:   }
         .          .   1547:
         .          .   1548:   shipping := Shipping{}
         .          .   1549:   err = tx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1550:   if err == sql.ErrNoRows {
         .          .   1551:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1552:       tx.Rollback()
         .          .   1553:       return
         .          .   1554:   }
         .          .   1555:   if err != nil {
         .          .   1556:       log.Print(err)
         .          .   1557:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1558:       tx.Rollback()
         .          .   1559:       return
         .          .   1560:   }
         .          .   1561:
         .       10ms   1562:   img, err := APIShipmentRequest(getShipmentServiceURL(), &APIShipmentRequestReq{
         .          .   1563:       ReserveID: shipping.ReserveID,
         .          .   1564:   })
         .          .   1565:   if err != nil {
         .          .   1566:       log.Print(err)
         .
tetsuzawa commented 1 year ago

category cache

{"pass":true,"score":3210,"campaign":0,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
186 0 177 0 9 0 GET /users/transactions.json 0.020 8.116 539.968 2.903 5.636 0.000 30529.000 19960.237
1398 0 1397 0 1 0 GET /new_items/\d+.json 0.006 0.632 75.362 0.054 0.180 0.000 24043.000 23497.079
68 0 40 0 28 0 POST /buy 1.604 1.644 69.256 1.018 1.616 29.000 49.000 36.162
58 0 37 0 21 0 POST /ship_done 0.804 0.808 32.208 0.555 0.808 29.000 83.000 39.672
54 0 39 0 15 0 POST /ship 0.379 0.816 29.459 0.546 0.812 0.000 61.000 52.611
34 0 32 0 2 0 POST /complete 0.004 0.820 24.570 0.723 0.816 0.000 34.000 32.000
304 0 304 0 0 0 GET /users/\d+.json 0.012 0.288 21.784 0.072 0.212 100.000 23919.000 14780.257
139 0 139 0 0 0 GET /new_items.json 0.028 0.424 20.204 0.145 0.372 23035.000 23752.000 23432.165
62 0 54 0 8 0 POST /login 0.060 0.380 11.024 0.178 0.368 73.000 105.000 94.387
1 0 1 0 0 0 POST /initialize 4.892 4.892 4.892 4.892 4.892 31.000 31.000 31.000
3697 0 3697 0 0 0 GET /items/\d+.json 0.000 0.188 4.580 0.001 0.004 1843.000 4026.000 2178.380
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.216 4.424 0.076 0.152 52361.000 151233.000 79065.397
54 0 54 0 0 0 GET /settings 0.000 0.240 2.576 0.048 0.192 2940.000 2954.000 2946.556
65 0 44 0 21 0 POST /sell 0.000 0.252 1.764 0.027 0.180 13.000 106.000 34.754
13 0 13 0 0 0 POST /bump 0.004 0.228 0.288 0.022 0.228 90.000 91.000 90.769
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.088 0.088 0.088 0.088 0.088 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.064 0.064 0.064 0.064 0.064 90365.000 90365.000 90365.000
51 0 37 0 14 0 GET /transactions/\d+.png 0.000 0.008 0.044 0.001 0.004 33.000 629.000 458.725
10 0 3 0 7 0 POST /items/edit 0.004 0.008 0.020 0.002 0.008 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.004 0.004 0.004 0.004 0.004 508459.000 508459.000 508459.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 1502.000 1502.000 1502.000
1 0 1 0 0 0 GET /reports.json 0.000 0.000 0.000 0.000 0.000 139095.000 139095.000 139095.000
$ go tool pprof -list main. -cum -seconds 60 http://localhost:8000/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)
Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.001.pb.gz
Total: 8.81s
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       20ms (flat, cum)  0.23% of Total
         .          .    149:}
         .          .    150:
         .          .    151:func APIShipmentStatus(shipmentURL string, param *APIShipmentStatusReq) (*APIShipmentStatusRes, error) {
         .          .    152:   b, _ := json.Marshal(param)
         .          .    153:
         .       10ms    154:   req, err := http.NewRequest(http.MethodGet, shipmentURL+"/status", bytes.NewBuffer(b))
         .          .    155:   if err != nil {
         .          .    156:       return nil, err
         .          .    157:   }
         .          .    158:
         .          .    159:   req.Header.Set("User-Agent", userAgent)
         .          .    160:   req.Header.Set("Content-Type", "application/json")
         .          .    161:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .    162:
         .       10ms    163:   res, err := http.DefaultClient.Do(req)
         .          .    164:   if err != nil {
         .          .    165:       return nil, err
         .          .    166:   }
         .          .    167:   defer res.Body.Close()
         .          .    168:
ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go
         0       40ms (flat, cum)  0.45% of Total
         .          .    372:
         .          .    373:   return session
         .          .    374:}
         .          .    375:
         .          .    376:func getCSRFToken(r *http.Request) string {
         .       40ms    377:   session := getSession(r)
         .          .    378:
         .          .    379:   csrfToken, ok := session.Values["csrf_token"]
         .          .    380:   if !ok {
         .          .    381:       return ""
         .          .    382:   }
ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum)  0.11% of Total
         .          .    425://         category.ParentCategoryName = parentCategory.CategoryName
         .          .    426://     }
         .          .    427://     return category, err
         .          .    428:// }
         .          .    429:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .       10ms    430:   return getCategoryByIDOnMemory(q, categoryID)
         .          .    431:}
         .          .    432:
         .          .    433:var categories = map[int]Category{
         .          .    434:   1:  {ID: 1, ParentID: 0, CategoryName: "ソファー", ParentCategoryName: "nan"},
         .          .    435:   2:  {ID: 2, ParentID: 1, CategoryName: "一人掛けソファー", ParentCategoryName: "ソファー"},
ROUTINE ======================== main.getCategoryByIDOnMemory in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum)  0.11% of Total
         .          .    475:   65: {ID: 65, ParentID: 60, CategoryName: "座布団", ParentCategoryName: "座椅子"},
         .          .    476:   66: {ID: 66, ParentID: 60, CategoryName: "空気椅子", ParentCategoryName: "座椅子"},
         .          .    477:}
         .          .    478:
         .          .    479:func getCategoryByIDOnMemory(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .       10ms    480:   return categories[categoryID], nil
         .          .    481:}
         .          .    482:
         .          .    483:func getConfigByName(name string) (string, error) {
         .          .    484:   config := Config{}
         .          .    485:   err := dbx.Get(&config, "SELECT * FROM `configs` WHERE `name` = ?", name)
ROUTINE ======================== main.getConfigByName in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.91% of Total
         .          .    480:   return categories[categoryID], nil
         .          .    481:}
         .          .    482:
         .          .    483:func getConfigByName(name string) (string, error) {
         .          .    484:   config := Config{}
         .       80ms    485:   err := dbx.Get(&config, "SELECT * FROM `configs` WHERE `name` = ?", name)
         .          .    486:   if err == sql.ErrNoRows {
         .          .    487:       return "", nil
         .          .    488:   }
         .          .    489:   if err != nil {
         .          .    490:       log.Print(err)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.91% of Total
         .          .   2372:       Error string `json:"error"`
         .          .   2373:   }{Error: msg})
         .          .   2374:}
         .          .   2375:
         .          .   2376:func getImageURL(imageName string) string {
         .       80ms   2377:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2378:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
      10ms      920ms (flat, cum) 10.44% of Total
         .          .   1083:   if err != nil || itemID <= 0 {
         .          .   1084:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1085:       return
         .          .   1086:   }
         .          .   1087:
         .      460ms   1088:   user, errCode, errMsg := getUser(r)
         .          .   1089:   if errMsg != "" {
         .          .   1090:       outputErrorMsg(w, errCode, errMsg)
         .          .   1091:       return
         .          .   1092:   }
         .          .   1093:
         .          .   1094:   item := Item{}
         .      210ms   1095:   err = dbx.Get(&item, "SELECT * FROM `items` WHERE `id` = ?", itemID)
         .          .   1096:   if err == sql.ErrNoRows {
         .          .   1097:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1098:       return
         .          .   1099:   }
         .          .   1100:   if err != nil {
         .          .   1101:       log.Print(err)
         .          .   1102:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1103:       return
         .          .   1104:   }
         .          .   1105:
         .          .   1106:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1107:   if err != nil {
         .          .   1108:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1109:       return
         .          .   1110:   }
         .          .   1111:
         .      170ms   1112:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1113:   if err != nil {
         .          .   1114:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1115:       return
         .          .   1116:   }
         .          .   1117:
         .          .   1118:   itemDetail := ItemDetail{
         .          .   1119:       ID:       item.ID,
         .          .   1120:       SellerID: item.SellerID,
         .          .   1121:       Seller:   &seller,
         .          .   1122:       // BuyerID
         .          .   1123:       // Buyer
         .          .   1124:       Status:      item.Status,
         .          .   1125:       Name:        item.Name,
         .          .   1126:       Price:       item.Price,
         .          .   1127:       Description: item.Description,
         .          .   1128:       ImageURL:    getImageURL(item.ImageName),
         .          .   1129:       CategoryID:  item.CategoryID,
         .          .   1130:       // TransactionEvidenceID
         .          .   1131:       // TransactionEvidenceStatus
         .          .   1132:       // ShippingStatus
         .          .   1133:       Category:  &category,
         .          .   1134:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1135:   }
         .          .   1136:
         .          .   1137:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .          .   1138:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1139:       if err != nil {
         .          .   1140:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1141:           return
         .          .   1142:       }
         .          .   1143:       itemDetail.BuyerID = item.BuyerID
         .          .   1144:       itemDetail.Buyer = &buyer
         .          .   1145:
         .          .   1146:       transactionEvidence := TransactionEvidence{}
         .          .   1147:       err = dbx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ?", item.ID)
         .          .   1148:       if err != nil && err != sql.ErrNoRows {
         .          .   1149:           // It's able to ignore ErrNoRows
         .          .   1150:           log.Print(err)
         .          .   1151:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1152:           return
         .          .   1153:       }
         .          .   1154:
         .          .   1155:       if transactionEvidence.ID > 0 {
         .          .   1156:           shipping := Shipping{}
         .          .   1157:           err = dbx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ?", transactionEvidence.ID)
         .          .   1158:           if err == sql.ErrNoRows {
         .          .   1159:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1160:               return
         .          .   1161:           }
         .          .   1162:           if err != nil {
         .          .   1163:               log.Print(err)
         .          .   1164:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1165:               return
         .          .   1166:           }
         .          .   1167:
         .          .   1168:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1169:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1170:           itemDetail.ShippingStatus = shipping.Status
         .          .   1171:       }
         .          .   1172:   }
         .          .   1173:
         .          .   1174:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
      10ms       80ms   1175:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1176:}
         .          .   1177:
         .          .   1178:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1179:   rie := reqItemEdit{}
         .          .   1180:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
         0      3.15s (flat, cum) 35.75% of Total
         .          .    658:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .    659:   json.NewEncoder(w).Encode(rni)
         .          .    660:}
         .          .    661:
         .          .    662:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .       20ms    663:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    664:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
         .          .    665:   if err != nil || rootCategoryID <= 0 {
         .          .    666:       outputErrorMsg(w, http.StatusBadRequest, "incorrect category id")
         .          .    667:       return
         .          .    668:   }
         .          .    669:
         .          .    670:   rootCategory, err := getCategoryByID(dbx, rootCategoryID)
         .          .    671:   if err != nil || rootCategory.ParentID != 0 {
         .          .    672:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    673:       return
         .          .    674:   }
         .          .    675:
         .          .    676:   var categoryIDs []int
         .       60ms    677:   err = dbx.Select(&categoryIDs, "SELECT id FROM `categories` WHERE parent_id=?", rootCategory.ID)
         .          .    678:   if err != nil {
         .          .    679:       log.Print(err)
         .          .    680:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    681:       return
         .          .    682:   }
         .          .    683:
         .          .    684:   query := r.URL.Query()
         .          .    685:   itemIDStr := query.Get("item_id")
         .          .    686:   var itemID int64
         .          .    687:   if itemIDStr != "" {
         .          .    688:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    689:       if err != nil || itemID <= 0 {
         .          .    690:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    691:           return
         .          .    692:       }
         .          .    693:   }
         .          .    694:
         .          .    695:   createdAtStr := query.Get("created_at")
         .          .    696:   var createdAt int64
         .          .    697:   if createdAtStr != "" {
         .          .    698:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    699:       if err != nil || createdAt <= 0 {
         .          .    700:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    701:           return
         .          .    702:       }
         .          .    703:   }
         .          .    704:
         .          .    705:   var inQuery string
         .          .    706:   var inArgs []interface{}
         .          .    707:   if itemID > 0 && createdAt > 0 {
         .          .    708:       // paging
         .          .    709:       inQuery, inArgs, err = sqlx.In(
         .          .    710:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND category_id IN (?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    711:           ItemStatusOnSale,
         .          .    712:           ItemStatusSoldOut,
         .          .    713:           categoryIDs,
         .          .    714:           time.Unix(createdAt, 0),
         .          .    715:           time.Unix(createdAt, 0),
         .          .    716:           itemID,
         .          .    717:           ItemsPerPage+1,
         .          .    718:       )
         .          .    719:       if err != nil {
         .          .    720:           log.Print(err)
         .          .    721:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    722:           return
         .          .    723:       }
         .          .    724:   } else {
         .          .    725:       // 1st page
         .          .    726:       inQuery, inArgs, err = sqlx.In(
         .          .    727:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    728:           ItemStatusOnSale,
         .          .    729:           ItemStatusSoldOut,
         .          .    730:           categoryIDs,
         .          .    731:           ItemsPerPage+1,
         .          .    732:       )
         .          .    733:       if err != nil {
         .          .    734:           log.Print(err)
         .          .    735:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    736:           return
         .          .    737:       }
         .          .    738:   }
         .          .    739:
         .          .    740:   items := []Item{}
         .      330ms    741:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    742:
         .          .    743:   if err != nil {
         .          .    744:       log.Print(err)
         .          .    745:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    746:       return
         .          .    747:   }
         .          .    748:
         .          .    749:   itemSimples := []ItemSimple{}
         .          .    750:   for _, item := range items {
         .      2.42s    751:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    752:       if err != nil {
         .          .    753:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    754:           return
         .          .    755:       }
         .       10ms    756:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    757:       if err != nil {
         .          .    758:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    759:           return
         .          .    760:       }
         .          .    761:       itemSimples = append(itemSimples, ItemSimple{
         .          .    762:           ID:         item.ID,
         .          .    763:           SellerID:   item.SellerID,
         .          .    764:           Seller:     &seller,
         .          .    765:           Status:     item.Status,
         .          .    766:           Name:       item.Name,
         .          .    767:           Price:      item.Price,
         .       60ms    768:           ImageURL:   getImageURL(item.ImageName),
         .          .    769:           CategoryID: item.CategoryID,
         .          .    770:           Category:   &category,
         .          .    771:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    772:       })
         .          .    773:   }
         .          .    774:
         .          .    775:   hasNext := false
         .          .    776:   if len(itemSimples) > ItemsPerPage {
         .          .    777:       hasNext = true
         .          .    778:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    779:   }
         .          .    780:
         .          .    781:   rni := resNewItems{
         .          .    782:       RootCategoryID:   rootCategory.ID,
         .          .    783:       RootCategoryName: rootCategory.CategoryName,
         .          .    784:       Items:            itemSimples,
         .          .    785:       HasNext:          hasNext,
         .          .    786:   }
         .          .    787:
         .       10ms    788:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      240ms    789:   json.NewEncoder(w).Encode(rni)
         .          .    790:
         .          .    791:}
         .          .    792:
         .          .    793:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    794:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      250ms (flat, cum)  2.84% of Total
         .          .    587:   }
         .          .    588:
         .          .    589:   items := []Item{}
         .          .    590:   if itemID > 0 && createdAt > 0 {
         .          .    591:       // paging
         .       30ms    592:       err := dbx.Select(&items,
         .          .    593:           "SELECT * FROM `items` WHERE `status` IN (?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    594:           ItemStatusOnSale,
         .          .    595:           ItemStatusSoldOut,
         .          .    596:           time.Unix(createdAt, 0),
         .          .    597:           time.Unix(createdAt, 0),
         .          .    598:           itemID,
         .          .    599:           ItemsPerPage+1,
         .          .    600:       )
         .          .    601:       if err != nil {
         .          .    602:           log.Print(err)
         .          .    603:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    604:           return
         .          .    605:       }
         .          .    606:   } else {
         .          .    607:       // 1st page
         .       10ms    608:       err := dbx.Select(&items,
         .          .    609:           "SELECT * FROM `items` WHERE `status` IN (?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    610:           ItemStatusOnSale,
         .          .    611:           ItemStatusSoldOut,
         .          .    612:           ItemsPerPage+1,
         .          .    613:       )
         .          .    614:       if err != nil {
         .          .    615:           log.Print(err)
         .          .    616:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    617:           return
         .          .    618:       }
         .          .    619:   }
         .          .    620:
         .          .    621:   itemSimples := []ItemSimple{}
         .          .    622:   for _, item := range items {
         .      180ms    623:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    624:       if err != nil {
         .          .    625:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    626:           return
         .          .    627:       }
         .          .    628:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    629:       if err != nil {
         .          .    630:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    631:           return
         .          .    632:       }
         .          .    633:       itemSimples = append(itemSimples, ItemSimple{
         .          .    634:           ID:         item.ID,
         .          .    635:           SellerID:   item.SellerID,
         .          .    636:           Seller:     &seller,
         .          .    637:           Status:     item.Status,
         .          .    638:           Name:       item.Name,
         .          .    639:           Price:      item.Price,
         .       10ms    640:           ImageURL:   getImageURL(item.ImageName),
         .          .    641:           CategoryID: item.CategoryID,
         .          .    642:           Category:   &category,
         .          .    643:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    644:       })
         .          .    645:   }
         .          .    646:
         .          .    647:   hasNext := false
         .          .    648:   if len(itemSimples) > ItemsPerPage {
         .          .    649:       hasNext = true
         .          .    650:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    651:   }
         .          .    652:
         .          .    653:   rni := resNewItems{
         .          .    654:       Items:   itemSimples,
         .          .    655:       HasNext: hasNext,
         .          .    656:   }
         .          .    657:
         .          .    658:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms    659:   json.NewEncoder(w).Encode(rni)
         .          .    660:}
         .          .    661:
         .          .    662:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    663:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    664:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getPaymentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.23% of Total
         .          .    492:   }
         .          .    493:   return config.Val, err
         .          .    494:}
         .          .    495:
         .          .    496:func getPaymentServiceURL() string {
         .       20ms    497:   val, _ := getConfigByName("payment_service_url")
         .          .    498:   if val == "" {
         .          .    499:       return DefaultPaymentServiceURL
         .          .    500:   }
         .          .    501:   return val
         .          .    502:}
ROUTINE ======================== main.getQRCode in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum)  0.11% of Total
         .          .   1300:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1301:       return
         .          .   1302:   }
         .          .   1303:
         .          .   1304:   shipping := Shipping{}
         .       10ms   1305:   err = dbx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ?", transactionEvidence.ID)
         .          .   1306:   if err == sql.ErrNoRows {
         .          .   1307:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1308:       return
         .          .   1309:   }
         .          .   1310:   if err != nil {
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      350ms (flat, cum)  3.97% of Total
         .          .    366:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    367:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    368:}
         .          .    369:
         .          .    370:func getSession(r *http.Request) *sessions.Session {
         .      350ms    371:   session, _ := store.Get(r, sessionName)
         .          .    372:
         .          .    373:   return session
         .          .    374:}
         .          .    375:
         .          .    376:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum)  0.11% of Total
         .          .   2199:       ItemUpdatedAt: targetItem.UpdatedAt.Unix(),
         .          .   2200:   })
         .          .   2201:}
         .          .   2202:
         .          .   2203:func getSettings(w http.ResponseWriter, r *http.Request) {
         .       10ms   2204:   csrfToken := getCSRFToken(r)
         .          .   2205:
         .          .   2206:   user, _, errMsg := getUser(r)
         .          .   2207:
         .          .   2208:   ress := resSetting{}
         .          .   2209:   ress.CSRFToken = csrfToken
ROUTINE ======================== main.getShipmentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.68% of Total
         .          .    500:   }
         .          .    501:   return val
         .          .    502:}
         .          .    503:
         .          .    504:func getShipmentServiceURL() string {
         .       60ms    505:   val, _ := getConfigByName("shipment_service_url")
         .          .    506:   if val == "" {
         .          .    507:       return DefaultShipmentServiceURL
         .          .    508:   }
         .          .    509:   return val
         .          .    510:}
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      380ms (flat, cum)  4.31% of Total
         .          .    898:   json.NewEncoder(w).Encode(rui)
         .          .    899:}
         .          .    900:
         .          .    901:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    902:
         .       40ms    903:   user, errCode, errMsg := getUser(r)
         .          .    904:   if errMsg != "" {
         .          .    905:       outputErrorMsg(w, errCode, errMsg)
         .          .    906:       return
         .          .    907:   }
         .          .    908:
         .          .    909:   query := r.URL.Query()
         .          .    910:   itemIDStr := query.Get("item_id")
         .          .    911:   var err error
         .          .    912:   var itemID int64
         .          .    913:   if itemIDStr != "" {
         .          .    914:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    915:       if err != nil || itemID <= 0 {
         .          .    916:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    917:           return
         .          .    918:       }
         .          .    919:   }
         .          .    920:
         .          .    921:   createdAtStr := query.Get("created_at")
         .          .    922:   var createdAt int64
         .          .    923:   if createdAtStr != "" {
         .          .    924:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    925:       if err != nil || createdAt <= 0 {
         .          .    926:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    927:           return
         .          .    928:       }
         .          .    929:   }
         .          .    930:
         .       10ms    931:   tx := dbx.MustBegin()
         .          .    932:   items := []Item{}
         .          .    933:   if itemID > 0 && createdAt > 0 {
         .          .    934:       // paging
         .       10ms    935:       err := tx.Select(&items,
         .          .    936:           "SELECT * FROM `items` WHERE (`seller_id` = ? OR `buyer_id` = ?) AND `status` IN (?,?,?,?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    937:           user.ID,
         .          .    938:           user.ID,
         .          .    939:           ItemStatusOnSale,
         .          .    940:           ItemStatusTrading,
         .          .    941:           ItemStatusSoldOut,
         .          .    942:           ItemStatusCancel,
         .          .    943:           ItemStatusStop,
         .          .    944:           time.Unix(createdAt, 0),
         .          .    945:           time.Unix(createdAt, 0),
         .          .    946:           itemID,
         .          .    947:           TransactionsPerPage+1,
         .          .    948:       )
         .          .    949:       if err != nil {
         .          .    950:           log.Print(err)
         .          .    951:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    952:           tx.Rollback()
         .          .    953:           return
         .          .    954:       }
         .          .    955:   } else {
         .          .    956:       // 1st page
         .       10ms    957:       err := tx.Select(&items,
         .          .    958:           "SELECT * FROM `items` WHERE (`seller_id` = ? OR `buyer_id` = ?) AND `status` IN (?,?,?,?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    959:           user.ID,
         .          .    960:           user.ID,
         .          .    961:           ItemStatusOnSale,
         .          .    962:           ItemStatusTrading,
         .          .    963:           ItemStatusSoldOut,
         .          .    964:           ItemStatusCancel,
         .          .    965:           ItemStatusStop,
         .          .    966:           TransactionsPerPage+1,
         .          .    967:       )
         .          .    968:       if err != nil {
         .          .    969:           log.Print(err)
         .          .    970:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    971:           tx.Rollback()
         .          .    972:           return
         .          .    973:       }
         .          .    974:   }
         .          .    975:
         .          .    976:   itemDetails := []ItemDetail{}
         .          .    977:   for _, item := range items {
         .       70ms    978:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .    979:       if err != nil {
         .          .    980:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    981:           tx.Rollback()
         .          .    982:           return
         .          .    983:       }
         .          .    984:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .    985:       if err != nil {
         .          .    986:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    987:           tx.Rollback()
         .          .    988:           return
         .          .    989:       }
         .          .    990:
         .          .    991:       itemDetail := ItemDetail{
         .          .    992:           ID:       item.ID,
         .          .    993:           SellerID: item.SellerID,
         .          .    994:           Seller:   &seller,
         .          .    995:           // BuyerID
         .          .    996:           // Buyer
         .          .    997:           Status:      item.Status,
         .          .    998:           Name:        item.Name,
         .          .    999:           Price:       item.Price,
         .          .   1000:           Description: item.Description,
         .          .   1001:           ImageURL:    getImageURL(item.ImageName),
         .          .   1002:           CategoryID:  item.CategoryID,
         .          .   1003:           // TransactionEvidenceID
         .          .   1004:           // TransactionEvidenceStatus
         .          .   1005:           // ShippingStatus
         .          .   1006:           Category:  &category,
         .          .   1007:           CreatedAt: item.CreatedAt.Unix(),
         .          .   1008:       }
         .          .   1009:
         .          .   1010:       if item.BuyerID != 0 {
         .       60ms   1011:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .   1012:           if err != nil {
         .          .   1013:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1014:               tx.Rollback()
         .          .   1015:               return
         .          .   1016:           }
         .          .   1017:           itemDetail.BuyerID = item.BuyerID
         .          .   1018:           itemDetail.Buyer = &buyer
         .          .   1019:       }
         .          .   1020:
         .          .   1021:       transactionEvidence := TransactionEvidence{}
         .       50ms   1022:       err = tx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ?", item.ID)
         .          .   1023:       if err != nil && err != sql.ErrNoRows {
         .          .   1024:           // It's able to ignore ErrNoRows
         .          .   1025:           log.Print(err)
         .          .   1026:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1027:           tx.Rollback()
         .          .   1028:           return
         .          .   1029:       }
         .          .   1030:
         .          .   1031:       if transactionEvidence.ID > 0 {
         .          .   1032:           shipping := Shipping{}
         .       30ms   1033:           err = tx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ?", transactionEvidence.ID)
         .          .   1034:           if err == sql.ErrNoRows {
         .          .   1035:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1036:               tx.Rollback()
         .          .   1037:               return
         .          .   1038:           }
         .          .   1039:           if err != nil {
         .          .   1040:               log.Print(err)
         .          .   1041:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1042:               tx.Rollback()
         .          .   1043:               return
         .          .   1044:           }
         .       70ms   1045:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1046:               ReserveID: shipping.ReserveID,
         .          .   1047:           })
         .          .   1048:           if err != nil {
         .          .   1049:               log.Print(err)
         .          .   1050:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1051:               tx.Rollback()
         .          .   1052:               return
         .          .   1053:           }
         .          .   1054:
         .          .   1055:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1056:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1057:           itemDetail.ShippingStatus = ssr.Status
         .          .   1058:       }
         .          .   1059:
         .          .   1060:       itemDetails = append(itemDetails, itemDetail)
         .          .   1061:   }
         .       10ms   1062:   tx.Commit()
         .          .   1063:
         .          .   1064:   hasNext := false
         .          .   1065:   if len(itemDetails) > TransactionsPerPage {
         .          .   1066:       hasNext = true
         .          .   1067:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1068:   }
         .          .   1069:
         .          .   1070:   rts := resTransactions{
         .          .   1071:       Items:   itemDetails,
         .          .   1072:       HasNext: hasNext,
         .          .   1073:   }
         .          .   1074:
         .          .   1075:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1076:   json.NewEncoder(w).Encode(rts)
         .          .   1077:
         .          .   1078:}
         .          .   1079:
         .          .   1080:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1081:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
         0      520ms (flat, cum)  5.90% of Total
         .          .    382:   }
         .          .    383:
         .          .    384:   return csrfToken.(string)
         .          .    385:}
         .          .    386:
         .       10ms    387:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      310ms    388:   session := getSession(r)
         .          .    389:   userID, ok := session.Values["user_id"]
         .          .    390:   if !ok {
         .          .    391:       return user, http.StatusNotFound, "no session"
         .          .    392:   }
         .          .    393:
         .      200ms    394:   err := dbx.Get(&user, "SELECT * FROM `users` WHERE `id` = ?", userID)
         .          .    395:   if err == sql.ErrNoRows {
         .          .    396:       return user, http.StatusNotFound, "user not found"
         .          .    397:   }
         .          .    398:   if err != nil {
         .          .    399:       log.Print(err)
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
         0      120ms (flat, cum)  1.36% of Total
         .          .    796:   if err != nil || userID <= 0 {
         .          .    797:       outputErrorMsg(w, http.StatusBadRequest, "incorrect user id")
         .          .    798:       return
         .          .    799:   }
         .          .    800:
         .       30ms    801:   userSimple, err := getUserSimpleByID(dbx, userID)
         .          .    802:   if err != nil {
         .          .    803:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .    804:       return
         .          .    805:   }
         .          .    806:
         .          .    807:   query := r.URL.Query()
         .          .    808:   itemIDStr := query.Get("item_id")
         .          .    809:   var itemID int64
         .          .    810:   if itemIDStr != "" {
         .          .    811:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    812:       if err != nil || itemID <= 0 {
         .          .    813:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    814:           return
         .          .    815:       }
         .          .    816:   }
         .          .    817:
         .          .    818:   createdAtStr := query.Get("created_at")
         .          .    819:   var createdAt int64
         .          .    820:   if createdAtStr != "" {
         .          .    821:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    822:       if err != nil || createdAt <= 0 {
         .          .    823:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    824:           return
         .          .    825:       }
         .          .    826:   }
         .          .    827:
         .          .    828:   items := []Item{}
         .          .    829:   if itemID > 0 && createdAt > 0 {
         .          .    830:       // paging
         .       20ms    831:       err := dbx.Select(&items,
         .          .    832:           "SELECT * FROM `items` WHERE `seller_id` = ? AND `status` IN (?,?,?) AND (`created_at` < ?  OR (`created_at` <= ? AND `id` < ?)) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    833:           userSimple.ID,
         .          .    834:           ItemStatusOnSale,
         .          .    835:           ItemStatusTrading,
         .          .    836:           ItemStatusSoldOut,
         .          .    837:           time.Unix(createdAt, 0),
         .          .    838:           time.Unix(createdAt, 0),
         .          .    839:           itemID,
         .          .    840:           ItemsPerPage+1,
         .          .    841:       )
         .          .    842:       if err != nil {
         .          .    843:           log.Print(err)
         .          .    844:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    845:           return
         .          .    846:       }
         .          .    847:   } else {
         .          .    848:       // 1st page
         .       50ms    849:       err := dbx.Select(&items,
         .          .    850:           "SELECT * FROM `items` WHERE `seller_id` = ? AND `status` IN (?,?,?) ORDER BY `created_at` DESC, `id` DESC LIMIT ?",
         .          .    851:           userSimple.ID,
         .          .    852:           ItemStatusOnSale,
         .          .    853:           ItemStatusTrading,
         .          .    854:           ItemStatusSoldOut,
         .          .    855:           ItemsPerPage+1,
         .          .    856:       )
         .          .    857:       if err != nil {
         .          .    858:           log.Print(err)
         .          .    859:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    860:           return
         .          .    861:       }
         .          .    862:   }
         .          .    863:
         .          .    864:   itemSimples := []ItemSimple{}
         .          .    865:   for _, item := range items {
         .          .    866:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    867:       if err != nil {
         .          .    868:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    869:           return
         .          .    870:       }
         .          .    871:       itemSimples = append(itemSimples, ItemSimple{
         .          .    872:           ID:         item.ID,
         .          .    873:           SellerID:   item.SellerID,
         .          .    874:           Seller:     &userSimple,
         .          .    875:           Status:     item.Status,
         .          .    876:           Name:       item.Name,
         .          .    877:           Price:      item.Price,
         .       10ms    878:           ImageURL:   getImageURL(item.ImageName),
         .          .    879:           CategoryID: item.CategoryID,
         .          .    880:           Category:   &category,
         .          .    881:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    882:       })
         .          .    883:   }
         .          .    884:
         .          .    885:   hasNext := false
         .          .    886:   if len(itemSimples) > ItemsPerPage {
         .          .    887:       hasNext = true
         .          .    888:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    889:   }
         .          .    890:
         .          .    891:   rui := resUserItems{
         .          .    892:       User:    &userSimple,
         .          .    893:       Items:   itemSimples,
         .          .    894:       HasNext: hasNext,
         .          .    895:   }
         .          .    896:
         .          .    897:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms    898:   json.NewEncoder(w).Encode(rui)
         .          .    899:}
         .          .    900:
         .          .    901:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    902:
         .          .    903:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      2.93s (flat, cum) 33.26% of Total
         .          .    402:
         .          .    403:   return user, http.StatusOK, ""
         .          .    404:}
         .          .    405:
         .          .    406:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       20ms    407:   user := User{}
         .      2.91s    408:   err = sqlx.Get(q, &user, "SELECT * FROM `users` WHERE `id` = ?", userID)
         .          .    409:   if err != nil {
         .          .    410:       return userSimple, err
         .          .    411:   }
         .          .    412:   userSimple.ID = user.ID
         .          .    413:   userSimple.AccountName = user.AccountName
ROUTINE ======================== main.main in /home/isucon/isucari/webapp/go/main.go
         0      100ms (flat, cum)  1.14% of Total
         .          .    362:   mux.HandleFunc(pat.Get("/transactions/:transaction_id"), getIndex)
         .          .    363:   mux.HandleFunc(pat.Get("/users/:user_id"), getIndex)
         .          .    364:   mux.HandleFunc(pat.Get("/users/setting"), getIndex)
         .          .    365:   // Assets
         .          .    366:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .      100ms    367:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    368:}
         .          .    369:
         .          .    370:func getSession(r *http.Request) *sessions.Session {
         .          .    371:   session, _ := store.Get(r, sessionName)
         .          .    372:
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0       40ms (flat, cum)  0.45% of Total
         .          .   1333:   if err != nil {
         .          .   1334:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1335:       return
         .          .   1336:   }
         .          .   1337:
         .       10ms   1338:   if rb.CSRFToken != getCSRFToken(r) {
         .          .   1339:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1340:
         .          .   1341:       return
         .          .   1342:   }
         .          .   1343:
         .       10ms   1344:   buyer, errCode, errMsg := getUser(r)
         .          .   1345:   if errMsg != "" {
         .          .   1346:       outputErrorMsg(w, errCode, errMsg)
         .          .   1347:       return
         .          .   1348:   }
         .          .   1349:
         .          .   1350:   tx := dbx.MustBegin()
         .          .   1351:
         .          .   1352:   targetItem := Item{}
         .          .   1353:   err = tx.Get(&targetItem, "SELECT * FROM `items` WHERE `id` = ? FOR UPDATE", rb.ItemID)
         .          .   1354:   if err == sql.ErrNoRows {
         .          .   1355:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1356:       tx.Rollback()
         .          .   1357:       return
         .          .   1358:   }
         .          .   1359:   if err != nil {
         .          .   1360:       log.Print(err)
         .          .   1361:
         .          .   1362:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1363:       tx.Rollback()
         .          .   1364:       return
         .          .   1365:   }
         .          .   1366:
         .          .   1367:   if targetItem.Status != ItemStatusOnSale {
         .          .   1368:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .          .   1369:       tx.Rollback()
         .          .   1370:       return
         .          .   1371:   }
         .          .   1372:
         .          .   1373:   if targetItem.SellerID == buyer.ID {
         .          .   1374:       outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません")
         .          .   1375:       tx.Rollback()
         .          .   1376:       return
         .          .   1377:   }
         .          .   1378:
         .          .   1379:   seller := User{}
         .          .   1380:   err = tx.Get(&seller, "SELECT * FROM `users` WHERE `id` = ? FOR UPDATE", targetItem.SellerID)
         .          .   1381:   if err == sql.ErrNoRows {
         .          .   1382:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1383:       tx.Rollback()
         .          .   1384:       return
         .          .   1385:   }
         .          .   1386:   if err != nil {
         .          .   1387:       log.Print(err)
         .          .   1388:
         .          .   1389:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1390:       tx.Rollback()
         .          .   1391:       return
         .          .   1392:   }
         .          .   1393:
         .          .   1394:   category, err := getCategoryByID(tx, targetItem.CategoryID)
         .          .   1395:   if err != nil {
         .          .   1396:       log.Print(err)
         .          .   1397:
         .          .   1398:       outputErrorMsg(w, http.StatusInternalServerError, "category id error")
         .          .   1399:       tx.Rollback()
         .          .   1400:       return
         .          .   1401:   }
         .          .   1402:
         .          .   1403:   result, err := tx.Exec("INSERT INTO `transaction_evidences` (`seller_id`, `buyer_id`, `status`, `item_id`, `item_name`, `item_price`, `item_description`,`item_category_id`,`item_root_category_id`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
         .          .   1404:       targetItem.SellerID,
         .          .   1405:       buyer.ID,
         .          .   1406:       TransactionEvidenceStatusWaitShipping,
         .          .   1407:       targetItem.ID,
         .          .   1408:       targetItem.Name,
         .          .   1409:       targetItem.Price,
         .          .   1410:       targetItem.Description,
         .          .   1411:       category.ID,
         .          .   1412:       category.ParentID,
         .          .   1413:   )
         .          .   1414:   if err != nil {
         .          .   1415:       log.Print(err)
         .          .   1416:
         .          .   1417:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1418:       tx.Rollback()
         .          .   1419:       return
         .          .   1420:   }
         .          .   1421:
         .          .   1422:   transactionEvidenceID, err := result.LastInsertId()
         .          .   1423:   if err != nil {
         .          .   1424:       log.Print(err)
         .          .   1425:
         .          .   1426:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1427:       tx.Rollback()
         .          .   1428:       return
         .          .   1429:   }
         .          .   1430:
         .          .   1431:   _, err = tx.Exec("UPDATE `items` SET `buyer_id` = ?, `status` = ?, `updated_at` = ? WHERE `id` = ?",
         .          .   1432:       buyer.ID,
         .          .   1433:       ItemStatusTrading,
         .          .   1434:       time.Now(),
         .          .   1435:       targetItem.ID,
         .          .   1436:   )
         .          .   1437:   if err != nil {
         .          .   1438:       log.Print(err)
         .          .   1439:
         .          .   1440:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1441:       tx.Rollback()
         .          .   1442:       return
         .          .   1443:   }
         .          .   1444:
         .          .   1445:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1446:       ToAddress:   buyer.Address,
         .          .   1447:       ToName:      buyer.AccountName,
         .          .   1448:       FromAddress: seller.Address,
         .          .   1449:       FromName:    seller.AccountName,
         .          .   1450:   })
         .          .   1451:   if err != nil {
         .          .   1452:       log.Print(err)
         .          .   1453:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1454:       tx.Rollback()
         .          .   1455:
         .          .   1456:       return
         .          .   1457:   }
         .          .   1458:
         .       20ms   1459:   pstr, err := APIPaymentToken(getPaymentServiceURL(), &APIPaymentServiceTokenReq{
         .          .   1460:       ShopID: PaymentServiceIsucariShopID,
         .          .   1461:       Token:  rb.Token,
         .          .   1462:       APIKey: PaymentServiceIsucariAPIKey,
         .          .   1463:       Price:  targetItem.Price,
         .          .   1464:   })
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       40ms (flat, cum)  0.45% of Total
         .          .   1834:       return
         .          .   1835:   }
         .          .   1836:
         .          .   1837:   tx := dbx.MustBegin()
         .          .   1838:   item := Item{}
         .       10ms   1839:   err = tx.Get(&item, "SELECT * FROM `items` WHERE `id` = ? FOR UPDATE", itemID)
         .          .   1840:   if err == sql.ErrNoRows {
         .          .   1841:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1842:       tx.Rollback()
         .          .   1843:       return
         .          .   1844:   }
         .          .   1845:   if err != nil {
         .          .   1846:       log.Print(err)
         .          .   1847:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1848:       tx.Rollback()
         .          .   1849:       return
         .          .   1850:   }
         .          .   1851:
         .          .   1852:   if item.Status != ItemStatusTrading {
         .          .   1853:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1854:       tx.Rollback()
         .          .   1855:       return
         .          .   1856:   }
         .          .   1857:
         .       10ms   1858:   err = tx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `item_id` = ? FOR UPDATE", itemID)
         .          .   1859:   if err == sql.ErrNoRows {
         .          .   1860:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1861:       tx.Rollback()
         .          .   1862:       return
         .          .   1863:   }
         .          .   1864:   if err != nil {
         .          .   1865:       log.Print(err)
         .          .   1866:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1867:       tx.Rollback()
         .          .   1868:       return
         .          .   1869:   }
         .          .   1870:
         .          .   1871:   if transactionEvidence.Status != TransactionEvidenceStatusWaitDone {
         .          .   1872:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1873:       tx.Rollback()
         .          .   1874:       return
         .          .   1875:   }
         .          .   1876:
         .          .   1877:   shipping := Shipping{}
         .       10ms   1878:   err = tx.Get(&shipping, "SELECT * FROM `shippings` WHERE `transaction_evidence_id` = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1879:   if err != nil {
         .          .   1880:       log.Print(err)
         .          .   1881:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1882:       tx.Rollback()
         .          .   1883:       return
         .          .   1884:   }
         .          .   1885:
         .       10ms   1886:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1887:       ReserveID: shipping.ReserveID,
         .          .   1888:   })
         .          .   1889:   if err != nil {
         .          .   1890:       log.Print(err)
         .          .   1891:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0      1.46s (flat, cum) 16.57% of Total
         .          .   2255:
         .          .   2256:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2257:       return
         .          .   2258:   }
         .          .   2259:
         .      1.46s   2260:   err = bcrypt.CompareHashAndPassword(u.HashedPassword, []byte(password))
         .          .   2261:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2262:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2263:       return
         .          .   2264:   }
         .          .   2265:   if err != nil {
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0       70ms (flat, cum)  0.79% of Total
         .          .   1944:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   1945:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   1946:}
         .          .   1947:
         .          .   1948:func postSell(w http.ResponseWriter, r *http.Request) {
         .       20ms   1949:   csrfToken := r.FormValue("csrf_token")
         .          .   1950:   name := r.FormValue("name")
         .          .   1951:   description := r.FormValue("description")
         .          .   1952:   priceStr := r.FormValue("price")
         .          .   1953:   categoryIDStr := r.FormValue("category_id")
         .          .   1954:
         .          .   1955:   f, header, err := r.FormFile("image")
         .          .   1956:   if err != nil {
         .          .   1957:       log.Print(err)
         .          .   1958:       outputErrorMsg(w, http.StatusBadRequest, "image error")
         .          .   1959:       return
         .          .   1960:   }
         .          .   1961:   defer f.Close()
         .          .   1962:
         .       10ms   1963:   if csrfToken != getCSRFToken(r) {
         .          .   1964:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1965:       return
         .          .   1966:   }
         .          .   1967:
         .          .   1968:   categoryID, err := strconv.Atoi(categoryIDStr)
         .          .   1969:   if err != nil || categoryID < 0 {
         .          .   1970:       outputErrorMsg(w, http.StatusBadRequest, "category id error")
         .          .   1971:       return
         .          .   1972:   }
         .          .   1973:
         .          .   1974:   price, err := strconv.Atoi(priceStr)
         .          .   1975:   if err != nil {
         .          .   1976:       outputErrorMsg(w, http.StatusBadRequest, "price error")
         .          .   1977:       return
         .          .   1978:   }
         .          .   1979:
         .          .   1980:   if name == "" || description == "" || price == 0 || categoryID == 0 {
         .          .   1981:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   1982:
         .          .   1983:       return
         .          .   1984:   }
         .          .   1985:
         .          .   1986:   if price < ItemMinPrice || price > ItemMaxPrice {
         .          .   1987:       outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg)
         .          .   1988:
         .          .   1989:       return
         .          .   1990:   }
         .          .   1991:
         .          .   1992:   category, err := getCategoryByID(dbx, categoryID)
         .          .   1993:   if err != nil || category.ParentID == 0 {
         .          .   1994:       log.Print(categoryID, category)
         .          .   1995:       outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID")
         .          .   1996:       return
         .          .   1997:   }
         .          .   1998:
         .       10ms   1999:   user, errCode, errMsg := getUser(r)
         .          .   2000:   if errMsg != "" {
         .          .   2001:       outputErrorMsg(w, errCode, errMsg)
         .          .   2002:       return
         .          .   2003:   }
         .          .   2004:
         .       20ms   2005:   img, err := ioutil.ReadAll(f)
         .          .   2006:   if err != nil {
         .          .   2007:       log.Print(err)
         .          .   2008:       outputErrorMsg(w, http.StatusInternalServerError, "image error")
         .          .   2009:       return
         .          .   2010:   }
         .          .   2011:
         .          .   2012:   ext := filepath.Ext(header.Filename)
         .          .   2013:
         .          .   2014:   if !(ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif") {
         .          .   2015:       outputErrorMsg(w, http.StatusBadRequest, "unsupported image format error")
         .          .   2016:       return
         .          .   2017:   }
         .          .   2018:
         .          .   2019:   if ext == ".jpeg" {
         .          .   2020:       ext = ".jpg"
         .          .   2021:   }
         .          .   2022:
         .          .   2023:   imgName := fmt.Sprintf("%s%s", secureRandomStr(16), ext)
         .       10ms   2024:   err = ioutil.WriteFile(fmt.Sprintf("../public/upload/%s", imgName), img, 0644)
         .          .   2025:   if err != nil {
         .          .   2026:       log.Print(err)
         .          .   2027:       outputErrorMsg(w, http.StatusInternalServerError, "Saving image failed")
         .          .   2028:       return
         .          .   2029:   }
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.23% of Total
         .          .   1516:}
         .          .   1517:
         .          .   1518:func postShip(w http.ResponseWriter, r *http.Request) {
         .          .   1519:   reqps := reqPostShip{}
         .          .   1520:
         .       10ms   1521:   err := json.NewDecoder(r.Body).Decode(&reqps)
         .          .   1522:   if err != nil {
         .          .   1523:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1524:       return
         .          .   1525:   }
         .          .   1526:
         .          .   1527:   csrfToken := reqps.CSRFToken
         .          .   1528:   itemID := reqps.ItemID
         .          .   1529:
         .       10ms   1530:   if csrfToken != getCSRFToken(r) {
         .          .   1531:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1532:
         .          .   1533:       return
         .          .   1534:   }
         .          .   1535:
ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.23% of Total
         .          .   1708:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1709:       tx.Rollback()
         .          .   1710:       return
         .          .   1711:   }
         .          .   1712:
         .       20ms   1713:   err = tx.Get(&transactionEvidence, "SELECT * FROM `transaction_evidences` WHERE `id` = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1714:   if err == sql.ErrNoRows {
         .          .   1715:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1716:       tx.Rollback()
         .          .   1717:       return
         .          .   1718:   }
mackee commented 1 year ago

pgx-replace

{"pass":true,"score":3020,"campaign":0,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
200 0 189 0 10 1 GET /users/transactions.json 0.016 7.340 545.303 2.727 4.948 0.000 30802.000 19924.170
1362 0 1362 0 0 0 GET /new_items/\d+.json 0.016 0.848 78.845 0.058 0.184 22647.000 24046.000 23493.125
72 0 39 0 30 3 POST /buy 0.004 1.724 69.609 0.967 1.700 0.000 49.000 35.194
56 0 33 0 23 0 POST /ship_done 0.138 0.824 29.593 0.528 0.820 0.000 83.000 38.661
53 0 39 0 14 0 POST /ship 0.004 0.836 29.332 0.553 0.828 29.000 61.000 53.604
356 0 356 0 0 0 GET /users/\d+.json 0.008 0.588 24.714 0.069 0.236 95.000 23949.000 14677.309
31 0 30 0 1 0 POST /complete 0.004 0.836 22.578 0.728 0.828 0.000 34.000 32.903
144 0 144 0 0 0 GET /new_items.json 0.044 0.844 18.726 0.130 0.288 23064.000 23752.000 23442.014
104 0 93 0 11 0 POST /login 0.060 0.376 16.675 0.160 0.328 73.000 108.000 95.087
210 0 210 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.244 13.116 0.062 0.200 51468.000 150038.000 78341.133
5 0 5 0 0 0 POST /initialize 1.608 1.944 8.480 1.696 1.944 31.000 31.000 31.000
3808 0 3808 0 0 0 GET /items/\d+.json 0.004 0.100 7.684 0.002 0.004 1843.000 4135.000 2179.764
93 0 93 0 0 0 GET /settings 0.012 0.288 6.830 0.073 0.220 2940.000 2957.000 2946.699
81 0 46 0 30 5 POST /sell 0.004 0.252 1.807 0.022 0.124 13.000 106.000 38.432
16 0 16 0 0 0 POST /bump 0.004 0.172 0.232 0.015 0.172 90.000 92.000 91.000
49 0 35 0 14 0 GET /transactions/\d+.png 0.004 0.012 0.048 0.001 0.004 33.000 630.000 453.408
4 0 4 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.008 0.024 0.032 0.008 0.024 90365.000 90365.000 90365.000
13 0 6 0 7 0 POST /items/edit 0.000 0.008 0.024 0.002 0.008 58.000 93.000 73.385
4 0 4 0 0 0 GET /static/css/main.19393e92.chunk.css 0.012 0.012 0.024 0.006 0.012 994.000 994.000 994.000
4 0 4 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.008 0.008 0.008 0.002 0.008 1502.000 1502.000 1502.000
4 0 4 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.000 0.004 0.004 0.001 0.004 508459.000 508459.000 508459.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 141722.000 141722.000 141722.000
mackee commented 1 year ago

pgx-replace + category cache

{"pass":true,"score":3510,"campaign":0,"language":"Go","messages":[]}
mackee commented 1 year ago

campaign=1

{"pass":false,"score":0,"campaign":1,"language":"Go","messages":["GET /items/17356.json: got response status code 500; expected 200","GET /items/43456.json: got response status code 500; expected 200","GET /items/49400.json: got response status code 500; expected 200","GET /items/49844.json: got response status code 500; expected 200","POST /buy: got response status code 500; expected 200 (item_id: 50168)","POST /buy: got response status code 500; expected 200 (item_id: 50170)","POST /login: got response status code 500; expected 200","POST /sell: got response status code 500; expected 200"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
632 0 26 0 520 86 POST /buy 0.749 7.872 2382.944 3.770 7.428 0.000 186.000 53.809
129 0 114 0 15 0 GET /users/transactions.json 0.004 6.504 387.774 3.006 6.132 0.000 24384.000 17639.713
692 0 692 0 0 0 GET /new_items/\d+.json 0.008 3.012 364.114 0.526 1.728 22675.000 24106.000 23515.103
1158 0 1131 0 8 19 POST /login 0.001 1.060 334.907 0.289 0.632 73.000 186.000 98.310
1131 0 1131 0 0 0 GET /settings 0.000 0.992 260.203 0.230 0.548 2935.000 2958.000 2946.016
2049 0 2045 0 0 4 GET /items/\d+.json 0.000 1.104 172.251 0.084 0.332 186.000 4038.000 2202.087
158 0 158 0 0 0 GET /new_items.json 0.036 2.356 115.128 0.729 1.920 23049.000 23938.000 23433.456
26 0 22 0 4 0 POST /ship 0.220 1.868 24.420 0.939 1.840 29.000 61.000 56.692
218 0 218 0 0 0 GET /users/\d+.json 0.000 0.564 22.372 0.103 0.328 96.000 23980.000 12632.991
25 0 19 0 6 0 POST /ship_done 0.012 2.460 19.915 0.797 1.516 29.000 83.000 37.760
19 0 19 0 0 0 POST /complete 0.004 1.436 15.235 0.802 1.436 34.000 34.000 34.000
46 0 39 0 6 1 POST /sell 0.004 0.956 10.143 0.220 0.680 13.000 186.000 25.543
12 0 12 0 0 0 POST /bump 0.004 0.616 3.936 0.328 0.616 90.000 92.000 90.917
23 0 19 0 4 0 GET /transactions/\d+.png 0.000 0.632 2.796 0.122 0.576 33.000 633.000 516.696
53 0 53 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.004 0.240 2.732 0.052 0.144 53578.000 133495.000 80888.038
1 0 1 0 0 0 POST /initialize 1.924 1.924 1.924 1.924 1.924 31.000 31.000 31.000
5 0 3 0 2 0 POST /items/edit 0.132 0.419 0.795 0.159 0.419 58.000 93.000 78.400
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.004 0.004 0.004 0.004 0.004 508459.000 508459.000 508459.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.000 0.000 0.000 0.000 0.000 90365.000 90365.000 90365.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 1502.000 1502.000 1502.000
mackee commented 1 year ago
pprof結果 ``` ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go 0 10ms (flat, cum) 0.017% of Total . . 154: req, err := http.NewRequest(http.MethodGet, shipmentURL+"/status", bytes.NewBuffer(b)) . . 155: if err != nil { . . 156: return nil, err . . 157: } . . 158: . 10ms 159: req.Header.Set("User-Agent", userAgent) . . 160: req.Header.Set("Content-Type", "application/json") . . 161: req.Header.Set("Authorization", IsucariAPIToken) . . 162: . . 163: res, err := http.DefaultClient.Do(req) . . 164: if err != nil { ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go 0 70ms (flat, cum) 0.12% of Total . . 357: . . 358: return session . . 359:} . . 360: . . 361:func getCSRFToken(r *http.Request) string { . 70ms 362: session := getSession(r) . . 363: . . 364: csrfToken, ok := session.Values["csrf_token"] . . 365: if !ok { . . 366: return "" . . 367: } ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go 0 10ms (flat, cum) 0.017% of Total . . 410:// category.ParentCategoryName = parentCategory.CategoryName . . 411:// } . . 412:// return category, err . . 413:// } . . 414:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) { . 10ms 415: return getCategoryByIDOnMemory(q, categoryID) . . 416:} . . 417: . . 418:var categories = map[int]Category{ . . 419: 1: {ID: 1, ParentID: 0, CategoryName: "ソファー", ParentCategoryName: "nan"}, . . 420: 2: {ID: 2, ParentID: 1, CategoryName: "一人掛けソファー", ParentCategoryName: "ソファー"}, ROUTINE ======================== main.getCategoryByIDOnMemory in /home/isucon/isucari/webapp/go/main.go 0 10ms (flat, cum) 0.017% of Total . . 460: 65: {ID: 65, ParentID: 60, CategoryName: "座布団", ParentCategoryName: "座椅子"}, . . 461: 66: {ID: 66, ParentID: 60, CategoryName: "空気椅子", ParentCategoryName: "座椅子"}, . . 462:} . . 463: . . 464:func getCategoryByIDOnMemory(q sqlx.Queryer, categoryID int) (category Category, err error) { . 10ms 465: return categories[categoryID], nil . . 466:} . . 467: . . 468:func getConfigByName(name string) (string, error) { . . 469: config := Config{} . . 470: err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name) ROUTINE ======================== main.getConfigByName in /home/isucon/isucari/webapp/go/main.go 0 310ms (flat, cum) 0.53% of Total . . 465: return categories[categoryID], nil . . 466:} . . 467: . . 468:func getConfigByName(name string) (string, error) { . . 469: config := Config{} . 310ms 470: err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name) . . 471: if err == sql.ErrNoRows { . . 472: return "", nil . . 473: } . . 474: if err != nil { . . 475: log.Print(err) ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go 0 1.42s (flat, cum) 2.41% of Total . . 1070: if err != nil || itemID <= 0 { . . 1071: outputErrorMsg(w, http.StatusBadRequest, "incorrect item id") . . 1072: return . . 1073: } . . 1074: . 1.38s 1075: user, errCode, errMsg := getUser(r) . . 1076: if errMsg != "" { . . 1077: outputErrorMsg(w, errCode, errMsg) . . 1078: return . . 1079: } . . 1080: . . 1081: item := Item{} . 10ms 1082: err = dbx.Get(&item, "SELECT * FROM items WHERE id = ?", itemID) . . 1083: if err == sql.ErrNoRows { . . 1084: outputErrorMsg(w, http.StatusNotFound, "item not found") . . 1085: return . . 1086: } . . 1087: if err != nil { . . 1088: log.Print(err) . . 1089: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1090: return . . 1091: } . . 1092: . . 1093: category, err := getCategoryByID(dbx, item.CategoryID) . . 1094: if err != nil { . . 1095: outputErrorMsg(w, http.StatusNotFound, "category not found") . . 1096: return . . 1097: } . . 1098: . 20ms 1099: seller, err := getUserSimpleByID(dbx, item.SellerID) . . 1100: if err != nil { . . 1101: outputErrorMsg(w, http.StatusNotFound, "seller not found") . . 1102: return . . 1103: } . . 1104: . . 1105: itemDetail := ItemDetail{ . . 1106: ID: item.ID, . . 1107: SellerID: item.SellerID, . . 1108: Seller: &seller, . . 1109: // BuyerID . . 1110: // Buyer . . 1111: Status: item.Status, . . 1112: Name: item.Name, . . 1113: Price: item.Price, . . 1114: Description: item.Description, . . 1115: ImageURL: getImageURL(item.ImageName), . . 1116: CategoryID: item.CategoryID, . . 1117: // TransactionEvidenceID . . 1118: // TransactionEvidenceStatus . . 1119: // ShippingStatus . . 1120: Category: &category, . . 1121: CreatedAt: item.CreatedAt.Unix(), . . 1122: } . . 1123: . . 1124: if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 { . . 1125: buyer, err := getUserSimpleByID(dbx, item.BuyerID) . . 1126: if err != nil { . . 1127: outputErrorMsg(w, http.StatusNotFound, "buyer not found") . . 1128: return . . 1129: } . . 1130: itemDetail.BuyerID = item.BuyerID . . 1131: itemDetail.Buyer = &buyer . . 1132: . . 1133: transactionEvidence := TransactionEvidence{} . . 1134: err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID) . . 1135: if err != nil && err != sql.ErrNoRows { . . 1136: // It's able to ignore ErrNoRows . . 1137: log.Print(err) . . 1138: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1139: return . . 1140: } . . 1141: . . 1142: if transactionEvidence.ID > 0 { . . 1143: shipping := Shipping{} . . 1144: err = dbx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID) . . 1145: if err == sql.ErrNoRows { . . 1146: outputErrorMsg(w, http.StatusNotFound, "shipping not found") . . 1147: return . . 1148: } . . 1149: if err != nil { . . 1150: log.Print(err) . . 1151: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1152: return . . 1153: } . . 1154: . . 1155: itemDetail.TransactionEvidenceID = transactionEvidence.ID . . 1156: itemDetail.TransactionEvidenceStatus = transactionEvidence.Status . . 1157: itemDetail.ShippingStatus = shipping.Status . . 1158: } . . 1159: } . . 1160: . . 1161: w.Header().Set("Content-Type", "application/json;charset=utf-8") . 10ms 1162: json.NewEncoder(w).Encode(itemDetail) . . 1163:} . . 1164: . . 1165:func postItemEdit(w http.ResponseWriter, r *http.Request) { . . 1166: rie := reqItemEdit{} . . 1167: err := json.NewDecoder(r.Body).Decode(&rie) ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go 0 970ms (flat, cum) 1.65% of Total . . 659: outputErrorMsg(w, http.StatusNotFound, "category not found") . . 660: return . . 661: } . . 662: . . 663: var categoryIDs []int . 580ms 664: err = dbx.Select(&categoryIDs, "SELECT id FROM categories WHERE parent_id=?", rootCategory.ID) . . 665: if err != nil { . . 666: log.Print(err) . . 667: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 668: return . . 669: } . . 670: . . 671: query := r.URL.Query() . . 672: itemIDStr := query.Get("item_id") . . 673: var itemID int64 . . 674: if itemIDStr != "" { . . 675: itemID, err = strconv.ParseInt(itemIDStr, 10, 64) . . 676: if err != nil || itemID <= 0 { . . 677: outputErrorMsg(w, http.StatusBadRequest, "item_id param error") . . 678: return . . 679: } . . 680: } . . 681: . . 682: createdAtStr := query.Get("created_at") . . 683: var createdAt int64 . . 684: if createdAtStr != "" { . . 685: createdAt, err = strconv.ParseInt(createdAtStr, 10, 64) . . 686: if err != nil || createdAt <= 0 { . . 687: outputErrorMsg(w, http.StatusBadRequest, "created_at param error") . . 688: return . . 689: } . . 690: } . . 691: . . 692: var inQuery string . . 693: var inArgs []interface{} . . 694: if itemID > 0 && createdAt > 0 { . . 695: // paging . . 696: inQuery, inArgs, err = sqlx.In( . . 697: "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) AND (created_at < ? OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?", . . 698: ItemStatusOnSale, . . 699: ItemStatusSoldOut, . . 700: categoryIDs, . . 701: time.Unix(createdAt, 0), . . 702: time.Unix(createdAt, 0), . . 703: itemID, . . 704: ItemsPerPage+1, . . 705: ) . . 706: if err != nil { . . 707: log.Print(err) . . 708: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 709: return . . 710: } . . 711: } else { . . 712: // 1st page . . 713: inQuery, inArgs, err = sqlx.In( . . 714: "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?", . . 715: ItemStatusOnSale, . . 716: ItemStatusSoldOut, . . 717: categoryIDs, . . 718: ItemsPerPage+1, . . 719: ) . . 720: if err != nil { . . 721: log.Print(err) . . 722: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 723: return . . 724: } . . 725: } . . 726: . . 727: items := []Item{} . 70ms 728: err = dbx.Select(&items, inQuery, inArgs...) . . 729: . . 730: if err != nil { . . 731: log.Print(err) . . 732: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 733: return . . 734: } . . 735: . . 736: itemSimples := []ItemSimple{} . . 737: for _, item := range items { . 280ms 738: seller, err := getUserSimpleByID(dbx, item.SellerID) . . 739: if err != nil { . . 740: outputErrorMsg(w, http.StatusNotFound, "seller not found") . . 741: return . . 742: } . 10ms 743: category, err := getCategoryByID(dbx, item.CategoryID) . . 744: if err != nil { . . 745: outputErrorMsg(w, http.StatusNotFound, "category not found") . . 746: return . . 747: } . 20ms 748: itemSimples = append(itemSimples, ItemSimple{ . . 749: ID: item.ID, . . 750: SellerID: item.SellerID, . . 751: Seller: &seller, . . 752: Status: item.Status, . . 753: Name: item.Name, . . 754: Price: item.Price, . . 755: ImageURL: getImageURL(item.ImageName), . . 756: CategoryID: item.CategoryID, . . 757: Category: &category, . . 758: CreatedAt: item.CreatedAt.Unix(), . . 759: }) . . 760: } . . 761: . . 762: hasNext := false . . 763: if len(itemSimples) > ItemsPerPage { . . 764: hasNext = true . . 765: itemSimples = itemSimples[0:ItemsPerPage] . . 766: } . . 767: . . 768: rni := resNewItems{ . . 769: RootCategoryID: rootCategory.ID, . . 770: RootCategoryName: rootCategory.CategoryName, . . 771: Items: itemSimples, . . 772: HasNext: hasNext, . . 773: } . . 774: . . 775: w.Header().Set("Content-Type", "application/json;charset=utf-8") . 10ms 776: json.NewEncoder(w).Encode(rni) . . 777: . . 778:} . . 779: . . 780:func getUserItems(w http.ResponseWriter, r *http.Request) { . . 781: userIDStr := pat.Param(r, "user_id") ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go 0 310ms (flat, cum) 0.53% of Total . . 574: } . . 575: . . 576: items := []Item{} . . 577: if itemID > 0 && createdAt > 0 { . . 578: // paging . 190ms 579: err := dbx.Select(&items, . . 580: "SELECT * FROM items WHERE status IN (?,?) AND (created_at < ? OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?", . . 581: ItemStatusOnSale, . . 582: ItemStatusSoldOut, . . 583: time.Unix(createdAt, 0), . . 584: time.Unix(createdAt, 0), . . 585: itemID, . . 586: ItemsPerPage+1, . . 587: ) . . 588: if err != nil { . . 589: log.Print(err) . . 590: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 591: return . . 592: } . . 593: } else { . . 594: // 1st page . . 595: err := dbx.Select(&items, . . 596: "SELECT * FROM items WHERE status IN (?,?) ORDER BY created_at DESC, id DESC LIMIT ?", . . 597: ItemStatusOnSale, . . 598: ItemStatusSoldOut, . . 599: ItemsPerPage+1, . . 600: ) . . 601: if err != nil { . . 602: log.Print(err) . . 603: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 604: return . . 605: } . . 606: } . . 607: . . 608: itemSimples := []ItemSimple{} . . 609: for _, item := range items { . 80ms 610: seller, err := getUserSimpleByID(dbx, item.SellerID) . . 611: if err != nil { . . 612: outputErrorMsg(w, http.StatusNotFound, "seller not found") . . 613: return . . 614: } . . 615: category, err := getCategoryByID(dbx, item.CategoryID) . . 616: if err != nil { . . 617: outputErrorMsg(w, http.StatusNotFound, "category not found") . . 618: return . . 619: } . . 620: itemSimples = append(itemSimples, ItemSimple{ . . 621: ID: item.ID, . . 622: SellerID: item.SellerID, . . 623: Seller: &seller, . . 624: Status: item.Status, . . 625: Name: item.Name, . . 626: Price: item.Price, . . 627: ImageURL: getImageURL(item.ImageName), . . 628: CategoryID: item.CategoryID, . . 629: Category: &category, . . 630: CreatedAt: item.CreatedAt.Unix(), . . 631: }) . . 632: } . . 633: . . 634: hasNext := false . . 635: if len(itemSimples) > ItemsPerPage { . . 636: hasNext = true . . 637: itemSimples = itemSimples[0:ItemsPerPage] . . 638: } . . 639: . . 640: rni := resNewItems{ . . 641: Items: itemSimples, . . 642: HasNext: hasNext, . . 643: } . . 644: . . 645: w.Header().Set("Content-Type", "application/json;charset=utf-8") . 40ms 646: json.NewEncoder(w).Encode(rni) . . 647:} . . 648: . . 649:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) { . . 650: rootCategoryIDStr := pat.Param(r, "root_category_id") . . 651: rootCategoryID, err := strconv.Atoi(rootCategoryIDStr) ROUTINE ======================== main.getPaymentServiceURL in /home/isucon/isucari/webapp/go/main.go 0 20ms (flat, cum) 0.034% of Total . . 477: } . . 478: return config.Val, err . . 479:} . . 480: . . 481:func getPaymentServiceURL() string { . 20ms 482: val, _ := getConfigByName("payment_service_url") . . 483: if val == "" { . . 484: return DefaultPaymentServiceURL . . 485: } . . 486: return val . . 487:} ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go 0 90ms (flat, cum) 0.15% of Total . . 351: mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public"))) . . 352: log.Fatal(http.ListenAndServe(":8000", mux)) . . 353:} . . 354: . . 355:func getSession(r *http.Request) *sessions.Session { . 90ms 356: session, _ := store.Get(r, sessionName) . . 357: . . 358: return session . . 359:} . . 360: . . 361:func getCSRFToken(r *http.Request) string { ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go 0 2.04s (flat, cum) 3.46% of Total . . 2173: ItemUpdatedAt: targetItem.UpdatedAt.Unix(), . . 2174: }) . . 2175:} . . 2176: . . 2177:func getSettings(w http.ResponseWriter, r *http.Request) { . 60ms 2178: csrfToken := getCSRFToken(r) . . 2179: . 1.93s 2180: user, _, errMsg := getUser(r) . . 2181: . . 2182: ress := resSetting{} . . 2183: ress.CSRFToken = csrfToken . . 2184: if errMsg == "" { . . 2185: ress.User = &user . . 2186: } . . 2187: . 10ms 2188: ress.PaymentServiceURL = getPaymentServiceURL() . . 2189: . . 2190: categories := []Category{} . . 2191: . 20ms 2192: err := dbx.Select(&categories, "SELECT * FROM categories") . . 2193: if err != nil { . . 2194: log.Print(err) . . 2195: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 2196: return . . 2197: } . . 2198: ress.Categories = categories . . 2199: . . 2200: w.Header().Set("Content-Type", "application/json;charset=utf-8") . 20ms 2201: json.NewEncoder(w).Encode(ress) . . 2202:} . . 2203: . . 2204:func postLogin(w http.ResponseWriter, r *http.Request) { . . 2205: rl := reqLogin{} . . 2206: err := json.NewDecoder(r.Body).Decode(&rl) ROUTINE ======================== main.getShipmentServiceURL in /home/isucon/isucari/webapp/go/main.go 0 290ms (flat, cum) 0.49% of Total . . 485: } . . 486: return val . . 487:} . . 488: . . 489:func getShipmentServiceURL() string { . 290ms 490: val, _ := getConfigByName("shipment_service_url") . . 491: if val == "" { . . 492: return DefaultShipmentServiceURL . . 493: } . . 494: return val . . 495:} ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go 0 290ms (flat, cum) 0.49% of Total . . 885: json.NewEncoder(w).Encode(rui) . . 886:} . . 887: . . 888:func getTransactions(w http.ResponseWriter, r *http.Request) { . . 889: . 30ms 890: user, errCode, errMsg := getUser(r) . . 891: if errMsg != "" { . . 892: outputErrorMsg(w, errCode, errMsg) . . 893: return . . 894: } . . 895: . . 896: query := r.URL.Query() . . 897: itemIDStr := query.Get("item_id") . . 898: var err error . . 899: var itemID int64 . . 900: if itemIDStr != "" { . . 901: itemID, err = strconv.ParseInt(itemIDStr, 10, 64) . . 902: if err != nil || itemID <= 0 { . . 903: outputErrorMsg(w, http.StatusBadRequest, "item_id param error") . . 904: return . . 905: } . . 906: } . . 907: . . 908: createdAtStr := query.Get("created_at") . . 909: var createdAt int64 . . 910: if createdAtStr != "" { . . 911: createdAt, err = strconv.ParseInt(createdAtStr, 10, 64) . . 912: if err != nil || createdAt <= 0 { . . 913: outputErrorMsg(w, http.StatusBadRequest, "created_at param error") . . 914: return . . 915: } . . 916: } . . 917: . 10ms 918: tx := dbx.MustBegin() . . 919: items := []Item{} . . 920: if itemID > 0 && createdAt > 0 { . . 921: // paging . . 922: err := tx.Select(&items, . . 923: "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) AND (created_at < ? OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?", . . 924: user.ID, . . 925: user.ID, . . 926: ItemStatusOnSale, . . 927: ItemStatusTrading, . . 928: ItemStatusSoldOut, . . 929: ItemStatusCancel, . . 930: ItemStatusStop, . . 931: time.Unix(createdAt, 0), . . 932: time.Unix(createdAt, 0), . . 933: itemID, . . 934: TransactionsPerPage+1, . . 935: ) . . 936: if err != nil { . . 937: log.Print(err) . . 938: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 939: tx.Rollback() . . 940: return . . 941: } . . 942: } else { . . 943: // 1st page . . 944: err := tx.Select(&items, . . 945: "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?", . . 946: user.ID, . . 947: user.ID, . . 948: ItemStatusOnSale, . . 949: ItemStatusTrading, . . 950: ItemStatusSoldOut, . . 951: ItemStatusCancel, . . 952: ItemStatusStop, . . 953: TransactionsPerPage+1, . . 954: ) . . 955: if err != nil { . . 956: log.Print(err) . . 957: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 958: tx.Rollback() . . 959: return . . 960: } . . 961: } . . 962: . . 963: itemDetails := []ItemDetail{} . . 964: for _, item := range items { . 10ms 965: seller, err := getUserSimpleByID(tx, item.SellerID) . . 966: if err != nil { . . 967: outputErrorMsg(w, http.StatusNotFound, "seller not found") . . 968: tx.Rollback() . . 969: return . . 970: } . . 971: category, err := getCategoryByID(tx, item.CategoryID) . . 972: if err != nil { . . 973: outputErrorMsg(w, http.StatusNotFound, "category not found") . . 974: tx.Rollback() . . 975: return . . 976: } . . 977: . . 978: itemDetail := ItemDetail{ . . 979: ID: item.ID, . . 980: SellerID: item.SellerID, . . 981: Seller: &seller, . . 982: // BuyerID . . 983: // Buyer . . 984: Status: item.Status, . . 985: Name: item.Name, . . 986: Price: item.Price, . . 987: Description: item.Description, . . 988: ImageURL: getImageURL(item.ImageName), . . 989: CategoryID: item.CategoryID, . . 990: // TransactionEvidenceID . . 991: // TransactionEvidenceStatus . . 992: // ShippingStatus . . 993: Category: &category, . . 994: CreatedAt: item.CreatedAt.Unix(), . . 995: } . . 996: . . 997: if item.BuyerID != 0 { . . 998: buyer, err := getUserSimpleByID(tx, item.BuyerID) . . 999: if err != nil { . . 1000: outputErrorMsg(w, http.StatusNotFound, "buyer not found") . . 1001: tx.Rollback() . . 1002: return . . 1003: } . . 1004: itemDetail.BuyerID = item.BuyerID . . 1005: itemDetail.Buyer = &buyer . . 1006: } . . 1007: . . 1008: transactionEvidence := TransactionEvidence{} . 20ms 1009: err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID) . . 1010: if err != nil && err != sql.ErrNoRows { . . 1011: // It's able to ignore ErrNoRows . . 1012: log.Print(err) . . 1013: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1014: tx.Rollback() . . 1015: return . . 1016: } . . 1017: . . 1018: if transactionEvidence.ID > 0 { . . 1019: shipping := Shipping{} . . 1020: err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID) . . 1021: if err == sql.ErrNoRows { . . 1022: outputErrorMsg(w, http.StatusNotFound, "shipping not found") . . 1023: tx.Rollback() . . 1024: return . . 1025: } . . 1026: if err != nil { . . 1027: log.Print(err) . . 1028: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1029: tx.Rollback() . . 1030: return . . 1031: } . 220ms 1032: ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{ . . 1033: ReserveID: shipping.ReserveID, . . 1034: }) . . 1035: if err != nil { . . 1036: log.Print(err) . . 1037: outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service") ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go 0 4.88s (flat, cum) 8.29% of Total . . 368: . . 369: return csrfToken.(string) . . 370:} . . 371: . . 372:func getUser(r *http.Request) (user User, errCode int, errMsg string) { . 20ms 373: session := getSession(r) . . 374: userID, ok := session.Values["user_id"] . . 375: if !ok { . . 376: return user, http.StatusNotFound, "no session" . . 377: } . . 378: . 4.86s 379: err := dbx.Get(&user, "SELECT * FROM users WHERE id = ?", userID) . . 380: if err == sql.ErrNoRows { . . 381: return user, http.StatusNotFound, "user not found" . . 382: } . . 383: if err != nil { . . 384: log.Print(err) ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go 0 270ms (flat, cum) 0.46% of Total . . 783: if err != nil || userID <= 0 { . . 784: outputErrorMsg(w, http.StatusBadRequest, "incorrect user id") . . 785: return . . 786: } . . 787: . 270ms 788: userSimple, err := getUserSimpleByID(dbx, userID) . . 789: if err != nil { . . 790: outputErrorMsg(w, http.StatusNotFound, "user not found") . . 791: return . . 792: } . . 793: ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go 0 660ms (flat, cum) 1.12% of Total . . 388: return user, http.StatusOK, "" . . 389:} . . 390: . . 391:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) { . . 392: user := User{} . 660ms 393: err = sqlx.Get(q, &user, "SELECT * FROM users WHERE id = ?", userID) . . 394: if err != nil { . . 395: return userSimple, err . . 396: } . . 397: userSimple.ID = user.ID . . 398: userSimple.AccountName = user.AccountName ROUTINE ======================== main.main in /home/isucon/isucari/webapp/go/main.go 0 20ms (flat, cum) 0.034% of Total . . 347: mux.HandleFunc(pat.Get("/transactions/:transaction_id"), getIndex) . . 348: mux.HandleFunc(pat.Get("/users/:user_id"), getIndex) . . 349: mux.HandleFunc(pat.Get("/users/setting"), getIndex) . . 350: // Assets . . 351: mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public"))) . 20ms 352: log.Fatal(http.ListenAndServe(":8000", mux)) . . 353:} . . 354: . . 355:func getSession(r *http.Request) *sessions.Session { . . 356: session, _ := store.Get(r, sessionName) . . 357: ROUTINE ======================== main.postBump in /home/isucon/isucari/webapp/go/main.go 0 10ms (flat, cum) 0.017% of Total . . 2153: log.Print(err) . . 2154: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 2155: return . . 2156: } . . 2157: . 10ms 2158: err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ?", itemID) . . 2159: if err != nil { . . 2160: log.Print(err) . . 2161: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 2162: tx.Rollback() . . 2163: return ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go 0 1.51s (flat, cum) 2.56% of Total . . 1314:} . . 1315: . . 1316:func postBuy(w http.ResponseWriter, r *http.Request) { . . 1317: rb := reqBuy{} . . 1318: . 10ms 1319: err := json.NewDecoder(r.Body).Decode(&rb) . . 1320: if err != nil { . . 1321: outputErrorMsg(w, http.StatusBadRequest, "json decode error") . . 1322: return . . 1323: } . . 1324: . 10ms 1325: if rb.CSRFToken != getCSRFToken(r) { . . 1326: outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error") . . 1327: . . 1328: return . . 1329: } . . 1330: . 1.46s 1331: buyer, errCode, errMsg := getUser(r) . . 1332: if errMsg != "" { . . 1333: outputErrorMsg(w, errCode, errMsg) . . 1334: return . . 1335: } . . 1336: . . 1337: tx := dbx.MustBegin() . . 1338: . . 1339: targetItem := Item{} . . 1340: err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", rb.ItemID) . . 1341: if err == sql.ErrNoRows { . . 1342: outputErrorMsg(w, http.StatusNotFound, "item not found") . . 1343: tx.Rollback() . . 1344: return . . 1345: } . . 1346: if err != nil { . . 1347: log.Print(err) . . 1348: . . 1349: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1350: tx.Rollback() . . 1351: return . . 1352: } . . 1353: . . 1354: if targetItem.Status != ItemStatusOnSale { . . 1355: outputErrorMsg(w, http.StatusForbidden, "item is not for sale") . . 1356: tx.Rollback() . . 1357: return . . 1358: } . . 1359: . . 1360: if targetItem.SellerID == buyer.ID { . . 1361: outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません") . . 1362: tx.Rollback() . . 1363: return . . 1364: } . . 1365: . . 1366: seller := User{} . . 1367: err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", targetItem.SellerID) . . 1368: if err == sql.ErrNoRows { . . 1369: outputErrorMsg(w, http.StatusNotFound, "seller not found") . . 1370: tx.Rollback() . . 1371: return . . 1372: } . . 1373: if err != nil { . . 1374: log.Print(err) . . 1375: . . 1376: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1377: tx.Rollback() . . 1378: return . . 1379: } . . 1380: . . 1381: category, err := getCategoryByID(tx, targetItem.CategoryID) . . 1382: if err != nil { . . 1383: log.Print(err) . . 1384: . . 1385: outputErrorMsg(w, http.StatusInternalServerError, "category id error") . . 1386: tx.Rollback() . . 1387: return . . 1388: } . . 1389: . . 1390: var transactionEvidenceID int64 . . 1391: if err := tx.Get( . . 1392: &transactionEvidenceID, . . 1393: "INSERT INTO transaction_evidences (seller_id, buyer_id, status, item_id, item_name, item_price, item_description,item_category_id,item_root_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id", . . 1394: targetItem.SellerID, . . 1395: buyer.ID, . . 1396: TransactionEvidenceStatusWaitShipping, . . 1397: targetItem.ID, . . 1398: targetItem.Name, . . 1399: targetItem.Price, . . 1400: targetItem.Description, . . 1401: category.ID, . . 1402: category.ParentID, . . 1403: ); err != nil { . . 1404: log.Print(err) . . 1405: . . 1406: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1407: tx.Rollback() . . 1408: return . . 1409: } . . 1410: . . 1411: _, err = tx.Exec("UPDATE items SET buyer_id = ?, status = ?, updated_at = ? WHERE id = ?", . . 1412: buyer.ID, . . 1413: ItemStatusTrading, . . 1414: time.Now(), . . 1415: targetItem.ID, . . 1416: ) . . 1417: if err != nil { . . 1418: log.Print(err) . . 1419: . . 1420: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1421: tx.Rollback() . . 1422: return . . 1423: } . . 1424: . 20ms 1425: scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{ . . 1426: ToAddress: buyer.Address, . . 1427: ToName: buyer.AccountName, . . 1428: FromAddress: seller.Address, . . 1429: FromName: seller.AccountName, . . 1430: }) . . 1431: if err != nil { . . 1432: log.Print(err) . . 1433: outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service") . . 1434: tx.Rollback() . . 1435: . . 1436: return . . 1437: } . . 1438: . 10ms 1439: pstr, err := APIPaymentToken(getPaymentServiceURL(), &APIPaymentServiceTokenReq{ . . 1440: ShopID: PaymentServiceIsucariShopID, . . 1441: Token: rb.Token, . . 1442: APIKey: PaymentServiceIsucariAPIKey, . . 1443: Price: targetItem.Price, . . 1444: }) ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go 0 20ms (flat, cum) 0.034% of Total . . 1861: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1862: tx.Rollback() . . 1863: return . . 1864: } . . 1865: . 20ms 1866: ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{ . . 1867: ReserveID: shipping.ReserveID, . . 1868: }) . . 1869: if err != nil { . . 1870: log.Print(err) . . 1871: outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service") ROUTINE ======================== main.postItemEdit in /home/isucon/isucari/webapp/go/main.go 0 20ms (flat, cum) 0.034% of Total . . 1183: if price < ItemMinPrice || price > ItemMaxPrice { . . 1184: outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg) . . 1185: return . . 1186: } . . 1187: . 20ms 1188: seller, errCode, errMsg := getUser(r) . . 1189: if errMsg != "" { . . 1190: outputErrorMsg(w, errCode, errMsg) . . 1191: return . . 1192: } . . 1193: ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go 0 51.47s (flat, cum) 87.39% of Total . . 2201: json.NewEncoder(w).Encode(ress) . . 2202:} . . 2203: . . 2204:func postLogin(w http.ResponseWriter, r *http.Request) { . . 2205: rl := reqLogin{} . 30ms 2206: err := json.NewDecoder(r.Body).Decode(&rl) . . 2207: if err != nil { . . 2208: outputErrorMsg(w, http.StatusBadRequest, "json decode error") . . 2209: return . . 2210: } . . 2211: . . 2212: accountName := rl.AccountName . . 2213: password := rl.Password . . 2214: . . 2215: if accountName == "" || password == "" { . . 2216: outputErrorMsg(w, http.StatusBadRequest, "all parameters are required") . . 2217: . . 2218: return . . 2219: } . . 2220: . . 2221: u := User{} . 1.77s 2222: err = dbx.Get(&u, "SELECT * FROM users WHERE account_name = ?", accountName) . . 2223: if err == sql.ErrNoRows { . . 2224: outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています") . . 2225: return . . 2226: } . . 2227: if err != nil { . . 2228: log.Print(err) . . 2229: . . 2230: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 2231: return . . 2232: } . . 2233: . 49.60s 2234: err = bcrypt.CompareHashAndPassword(u.HashedPassword, []byte(password)) . . 2235: if err == bcrypt.ErrMismatchedHashAndPassword { . . 2236: outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています") . . 2237: return . . 2238: } . . 2239: if err != nil { . . 2240: log.Print(err) . . 2241: . . 2242: outputErrorMsg(w, http.StatusInternalServerError, "crypt error") . . 2243: return . . 2244: } . . 2245: . . 2246: session := getSession(r) . . 2247: . . 2248: session.Values["user_id"] = u.ID . 10ms 2249: session.Values["csrf_token"] = secureRandomStr(20) . 40ms 2250: if err = session.Save(r, w); err != nil { . . 2251: log.Print(err) . . 2252: . . 2253: outputErrorMsg(w, http.StatusInternalServerError, "session error") . . 2254: return . . 2255: } . . 2256: . . 2257: w.Header().Set("Content-Type", "application/json;charset=utf-8") . 20ms 2258: json.NewEncoder(w).Encode(u) . . 2259:} . . 2260: . . 2261:func postRegister(w http.ResponseWriter, r *http.Request) { . . 2262: rr := reqRegister{} . . 2263: err := json.NewDecoder(r.Body).Decode(&rr) ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go 0 20ms (flat, cum) 0.034% of Total . . 1974: log.Print(categoryID, category) . . 1975: outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID") . . 1976: return . . 1977: } . . 1978: . 20ms 1979: user, errCode, errMsg := getUser(r) . . 1980: if errMsg != "" { . . 1981: outputErrorMsg(w, errCode, errMsg) . . 1982: return . . 1983: } . . 1984: ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go 0 60ms (flat, cum) 0.1% of Total . . 1511: outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error") . . 1512: . . 1513: return . . 1514: } . . 1515: . 20ms 1516: seller, errCode, errMsg := getUser(r) . . 1517: if errMsg != "" { . . 1518: outputErrorMsg(w, errCode, errMsg) . . 1519: return . . 1520: } . . 1521: . . 1522: transactionEvidence := TransactionEvidence{} . 10ms 1523: err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID) . . 1524: if err == sql.ErrNoRows { . . 1525: outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found") . . 1526: return . . 1527: } . . 1528: if err != nil { . . 1529: log.Print(err) . . 1530: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1531: . . 1532: return . . 1533: } . . 1534: . . 1535: if transactionEvidence.SellerID != seller.ID { . . 1536: outputErrorMsg(w, http.StatusForbidden, "権限がありません") . . 1537: return . . 1538: } . . 1539: . . 1540: tx := dbx.MustBegin() . . 1541: . . 1542: item := Item{} . . 1543: err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID) . . 1544: if err == sql.ErrNoRows { . . 1545: outputErrorMsg(w, http.StatusNotFound, "item not found") . . 1546: tx.Rollback() . . 1547: return . . 1548: } . . 1549: if err != nil { . . 1550: log.Print(err) . . 1551: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1552: tx.Rollback() . . 1553: return . . 1554: } . . 1555: . . 1556: if item.Status != ItemStatusTrading { . . 1557: outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません") . . 1558: tx.Rollback() . . 1559: return . . 1560: } . . 1561: . . 1562: err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID) . . 1563: if err == sql.ErrNoRows { . . 1564: outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found") . . 1565: tx.Rollback() . . 1566: return . . 1567: } . . 1568: if err != nil { . . 1569: log.Print(err) . . 1570: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1571: tx.Rollback() . . 1572: return . . 1573: } . . 1574: . . 1575: if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping { . . 1576: outputErrorMsg(w, http.StatusForbidden, "準備ができていません") . . 1577: tx.Rollback() . . 1578: return . . 1579: } . . 1580: . . 1581: shipping := Shipping{} . . 1582: err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID) . . 1583: if err == sql.ErrNoRows { . . 1584: outputErrorMsg(w, http.StatusNotFound, "shippings not found") . . 1585: tx.Rollback() . . 1586: return . . 1587: } . . 1588: if err != nil { . . 1589: log.Print(err) . . 1590: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1591: tx.Rollback() . . 1592: return . . 1593: } . . 1594: . 30ms 1595: img, err := APIShipmentRequest(getShipmentServiceURL(), &APIShipmentRequestReq{ . . 1596: ReserveID: shipping.ReserveID, . . 1597: }) . . 1598: if err != nil { . . 1599: log.Print(err) . . 1600: outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service") ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go 0 30ms (flat, cum) 0.051% of Total . . 1642: outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error") . . 1643: . . 1644: return . . 1645: } . . 1646: . 20ms 1647: seller, errCode, errMsg := getUser(r) . . 1648: if errMsg != "" { . . 1649: outputErrorMsg(w, errCode, errMsg) . . 1650: return . . 1651: } . . 1652: . . 1653: transactionEvidence := TransactionEvidence{} . . 1654: err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID) . . 1655: if err == sql.ErrNoRows { . . 1656: outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found") . . 1657: return . . 1658: } . . 1659: if err != nil { . . 1660: log.Print(err) . . 1661: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1662: . . 1663: return . . 1664: } . . 1665: . . 1666: if transactionEvidence.SellerID != seller.ID { . . 1667: outputErrorMsg(w, http.StatusForbidden, "権限がありません") . . 1668: return . . 1669: } . . 1670: . . 1671: tx := dbx.MustBegin() . . 1672: . . 1673: item := Item{} . . 1674: err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID) . . 1675: if err == sql.ErrNoRows { . . 1676: outputErrorMsg(w, http.StatusNotFound, "items not found") . . 1677: tx.Rollback() . . 1678: return . . 1679: } . . 1680: if err != nil { . . 1681: log.Print(err) . . 1682: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1683: tx.Rollback() . . 1684: return . . 1685: } . . 1686: . . 1687: if item.Status != ItemStatusTrading { . . 1688: outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません") . . 1689: tx.Rollback() . . 1690: return . . 1691: } . . 1692: . . 1693: err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID) . . 1694: if err == sql.ErrNoRows { . . 1695: outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found") . . 1696: tx.Rollback() . . 1697: return . . 1698: } . . 1699: if err != nil { . . 1700: log.Print(err) . . 1701: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1702: tx.Rollback() . . 1703: return . . 1704: } . . 1705: . . 1706: if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping { . . 1707: outputErrorMsg(w, http.StatusForbidden, "準備ができていません") . . 1708: tx.Rollback() . . 1709: return . . 1710: } . . 1711: . . 1712: shipping := Shipping{} . . 1713: err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID) . . 1714: if err == sql.ErrNoRows { . . 1715: outputErrorMsg(w, http.StatusNotFound, "shippings not found") . . 1716: tx.Rollback() . . 1717: return . . 1718: } . . 1719: if err != nil { . . 1720: log.Print(err) . . 1721: outputErrorMsg(w, http.StatusInternalServerError, "db error") . . 1722: tx.Rollback() . . 1723: return . . 1724: } . . 1725: . 10ms 1726: ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{ . . 1727: ReserveID: shipping.ReserveID, . . 1728: }) . . 1729: if err != nil { . . 1730: log.Print(err) . . 1731: outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service") ROUTINE ======================== main.secureRandomStr in /home/isucon/isucari/webapp/go/main.go 0 10ms (flat, cum) 0.017% of Total . . 2060: json.NewEncoder(w).Encode(resSell{ID: itemID}) . . 2061:} . . 2062: . . 2063:func secureRandomStr(b int) string { . . 2064: k := make([]byte, b) . 10ms 2065: if _, err := crand.Read(k); err != nil { . . 2066: panic(err) . . 2067: } . . 2068: return fmt.Sprintf("%x", k) . . 2069:} . . 2070: ```
mackee commented 1 year ago

rehashed-password + keepalive

{"pass":true,"score":11110,"campaign":1,"language":"Go","messages":["GET /new_items/20.json: リクエストに失敗しました(タイムアウトしました)","GET /new_items/40.json: リクエストに失敗しました(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 119)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1191)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1750)(
タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1952)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2907)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2914)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2988)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3112)(
タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 328)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3538)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 387)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 665)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 813)(タイ
ムアウトしました)","POST /buy: リクエストに失敗しました (item_id: 50645)(タイムアウトしました)","POST /complete: リクエストに失敗しました (item_id: 50640)(タイムアウトしました)","POST /ship: リクエストに失敗しました (item_id: 50636)(タ
イムアウトしました)"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
677 0 53 0 624 0 POST /buy 0.732 4.600 2077.356 3.068 4.548 0.000 49.000 33.288
269 0 255 0 14 0 GET /users/transactions.json 0.004 5.932 753.030 2.799 4.980 0.000 6973.000 5391.115
1652 0 1650 0 2 0 GET /new_items/\d+.json 0.008 0.784 148.818 0.090 0.368 0.000 5684.000 5513.518
1557 0 1549 0 8 0 POST /login 0.004 0.708 96.504 0.062 0.196 73.000 110.000 96.898
1549 0 1549 0 0 0 GET /settings 0.000 0.472 53.812 0.035 0.140 775.000 796.000 783.696
70 0 49 0 21 0 POST /ship_done 0.804 1.032 43.135 0.616 0.892 29.000 83.000 38.700
203 0 203 0 0 0 GET /new_items.json 0.036 0.896 41.404 0.204 0.464 5627.000 5907.000 5780.448
65 0 50 0 15 0 POST /ship 0.243 0.900 38.958 0.599 0.848 0.000 61.000 54.031
46 0 45 0 1 0 POST /complete 0.004 0.960 35.146 0.764 0.924 0.000 34.000 33.261
4635 0 4635 0 0 0 GET /items/\d+.json 0.000 0.472 32.576 0.007 0.032 1062.000 1969.000 1222.670
410 0 410 0 0 0 GET /users/\d+.json 0.000 0.212 4.616 0.011 0.076 97.000 5286.000 3166.695
84 0 63 0 21 0 POST /sell 0.004 0.592 3.772 0.045 0.188 13.000 106.000 29.833
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.264 2.088 0.036 0.168 51648.000 144457.000 82193.931
1 0 1 0 0 0 POST /initialize 1.960 1.960 1.960 1.960 1.960 31.000 31.000 31.000
13 0 13 0 0 0 POST /bump 0.000 0.212 0.552 0.042 0.212 90.000 92.000 91.154
63 0 49 0 14 0 GET /transactions/\d+.png 0.000 0.100 0.292 0.005 0.020 33.000 636.000 490.000
10 0 3 0 7 0 POST /items/edit 0.004 0.100 0.144 0.014 0.100 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.008 0.008 0.008 0.008 0.008 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.004 0.004 0.004 0.004 0.004 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 32487.000 32487.000 32487.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
tetsuzawa commented 1 year ago
{"pass":true,"score":11510,"campaign":1,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
684 0 60 0 624 0 POST /buy 1.152 6.172 2628.795 3.843 5.312 0.000 49.000 33.342
280 0 265 0 15 0 GET /users/transactions.json 0.004 6.068 779.155 2.783 4.876 0.000 6877.000 5384.746
1965 0 1965 0 0 0 GET /new_items/\d+.json 0.004 0.632 117.317 0.060 0.236 5320.000 5663.000 5521.023
1557 0 1549 0 8 0 POST /login 0.004 0.432 59.640 0.038 0.140 73.000 109.000 96.972
75 0 54 0 21 0 POST /ship_done 0.804 1.296 47.059 0.627 0.848 29.000 83.000 38.387
72 0 55 0 17 0 POST /ship 0.159 0.936 43.685 0.607 0.872 0.000 61.000 53.014
49 0 49 0 0 0 POST /complete 0.004 0.884 37.624 0.768 0.860 34.000 34.000 34.000
1549 0 1549 0 0 0 GET /settings 0.000 0.416 35.484 0.023 0.112 775.000 794.000 783.724
206 0 206 0 0 0 GET /new_items.json 0.032 0.512 32.579 0.158 0.424 5603.000 5992.000 5775.379
5311 0 5311 0 0 0 GET /items/\d+.json 0.000 0.316 24.044 0.005 0.020 1055.000 1965.000 1222.066
419 0 419 0 0 0 GET /users/\d+.json 0.000 0.240 4.812 0.011 0.092 97.000 5270.000 3216.752
87 0 66 0 21 0 POST /sell 0.004 0.452 4.396 0.051 0.272 13.000 106.000 29.253
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.244 2.408 0.042 0.188 51947.000 138361.000 77939.052
1 0 1 0 0 0 POST /initialize 2.096 2.096 2.096 2.096 2.096 31.000 31.000 31.000
13 0 13 0 0 0 POST /bump 0.004 0.244 0.364 0.028 0.244 91.000 91.000 91.000
68 0 54 0 14 0 GET /transactions/\d+.png 0.000 0.048 0.200 0.003 0.008 33.000 637.000 499.647
10 0 3 0 7 0 POST /items/edit 0.000 0.028 0.044 0.004 0.028 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.008 0.008 0.008 0.008 0.008 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.004 0.004 0.004 0.004 0.004 774.000 774.000 774.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /reports.json 0.000 0.000 0.000 0.000 0.000 36406.000 36406.000 36406.000

pprof


go tool pprof -list main. -cum -seconds 60 http://localhost:8000/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)
Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.004.pb.gz
Total: 25.63s
ROUTINE ======================== main.APIPaymentToken in /home/isucon/isucari/webapp/go/api.go
         0       10ms (flat, cum) 0.039% of Total
         .          .     74:       }
         .          .     75:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .     76:   }
         .          .     77:
         .          .     78:   pstr := &APIPaymentServiceTokenRes{}
         .       10ms     79:   err = json.NewDecoder(res.Body).Decode(pstr)
         .          .     80:   if err != nil {
         .          .     81:       return nil, err
         .          .     82:   }
         .          .     83:
         .          .     84:   return pstr, nil
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       10ms (flat, cum) 0.039% of Total
         .          .    154:   req, err := http.NewRequest(http.MethodGet, shipmentURL+"/status", bytes.NewBuffer(b))
         .          .    155:   if err != nil {
         .          .    156:       return nil, err
         .          .    157:   }
         .          .    158:
         .       10ms    159:   req.Header.Set("User-Agent", userAgent)
         .          .    160:   req.Header.Set("Content-Type", "application/json")
         .          .    161:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .    162:
         .          .    163:   res, err := http.DefaultClient.Do(req)
         .          .    164:   if err != nil {
ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go
         0      130ms (flat, cum)  0.51% of Total
         .          .    369:
         .          .    370:   return session
         .          .    371:}
         .          .    372:
         .          .    373:func getCSRFToken(r *http.Request) string {
         .      130ms    374:   session := getSession(r)
         .          .    375:
         .          .    376:   csrfToken, ok := session.Values["csrf_token"]
         .          .    377:   if !ok {
         .          .    378:       return ""
         .          .    379:   }
ROUTINE ======================== main.getConfigByName in /home/isucon/isucari/webapp/go/main.go
         0      1.38s (flat, cum)  5.38% of Total
         .          .    523:   return categories[categoryID], nil
         .          .    524:}
         .          .    525:
         .          .    526:func getConfigByName(name string) (string, error) {
         .          .    527:   config := Config{}
         .      1.38s    528:   err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name)
         .          .    529:   if err == sql.ErrNoRows {
         .          .    530:       return "", nil
         .          .    531:   }
         .          .    532:   if err != nil {
         .          .    533:       log.Print(err)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.23% of Total
         .          .   2457:       Error string `json:"error"`
         .          .   2458:   }{Error: msg})
         .          .   2459:}
         .          .   2460:
         .          .   2461:func getImageURL(imageName string) string {
         .       60ms   2462:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2463:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
      10ms      1.60s (flat, cum)  6.24% of Total
         .          .   1135:   if err != nil || itemID <= 0 {
         .          .   1136:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1137:       return
         .          .   1138:   }
         .          .   1139:
         .      1.16s   1140:   user, errCode, errMsg := getUser(r)
         .          .   1141:   if errMsg != "" {
         .          .   1142:       outputErrorMsg(w, errCode, errMsg)
         .          .   1143:       return
         .          .   1144:   }
         .          .   1145:
         .          .   1146:   item := Item{}
         .      280ms   1147:   err = dbx.Get(&item, "SELECT * FROM items WHERE id = ?", itemID)
         .          .   1148:   if err == sql.ErrNoRows {
         .          .   1149:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1150:       return
         .          .   1151:   }
         .          .   1152:   if err != nil {
         .          .   1153:       log.Print(err)
         .          .   1154:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1155:       return
         .          .   1156:   }
         .          .   1157:
         .          .   1158:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1159:   if err != nil {
         .          .   1160:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1161:       return
         .          .   1162:   }
         .          .   1163:
         .       50ms   1164:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1165:   if err != nil {
         .          .   1166:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1167:       return
         .          .   1168:   }
         .          .   1169:
         .          .   1170:   itemDetail := ItemDetail{
         .          .   1171:       ID:       item.ID,
         .          .   1172:       SellerID: item.SellerID,
         .          .   1173:       Seller:   &seller,
         .          .   1174:       // BuyerID
         .          .   1175:       // Buyer
         .          .   1176:       Status:      item.Status,
         .          .   1177:       Name:        item.Name,
         .          .   1178:       Price:       item.Price,
         .          .   1179:       Description: item.Description,
         .       10ms   1180:       ImageURL:    getImageURL(item.ImageName),
         .          .   1181:       CategoryID:  item.CategoryID,
         .          .   1182:       // TransactionEvidenceID
         .          .   1183:       // TransactionEvidenceStatus
         .          .   1184:       // ShippingStatus
         .          .   1185:       Category:  &category,
         .          .   1186:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1187:   }
         .          .   1188:
         .          .   1189:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .          .   1190:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1191:       if err != nil {
         .          .   1192:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1193:           return
         .          .   1194:       }
         .          .   1195:       itemDetail.BuyerID = item.BuyerID
         .          .   1196:       itemDetail.Buyer = &buyer
         .          .   1197:
         .          .   1198:       transactionEvidence := TransactionEvidence{}
         .       30ms   1199:       err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1200:       if err != nil && err != sql.ErrNoRows {
         .          .   1201:           // It's able to ignore ErrNoRows
         .          .   1202:           log.Print(err)
         .          .   1203:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1204:           return
         .          .   1205:       }
         .          .   1206:
         .          .   1207:       if transactionEvidence.ID > 0 {
         .          .   1208:           shipping := Shipping{}
         .          .   1209:           err = dbx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1210:           if err == sql.ErrNoRows {
         .          .   1211:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1212:               return
         .          .   1213:           }
         .          .   1214:           if err != nil {
         .          .   1215:               log.Print(err)
         .          .   1216:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1217:               return
         .          .   1218:           }
         .          .   1219:
         .          .   1220:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1221:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1222:           itemDetail.ShippingStatus = shipping.Status
         .          .   1223:       }
         .          .   1224:   }
         .          .   1225:
      10ms       10ms   1226:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       60ms   1227:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1228:}
         .          .   1229:
         .          .   1230:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1231:   rie := reqItemEdit{}
         .          .   1232:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
      10ms      3.20s (flat, cum) 12.49% of Total
         .          .    710:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .    711:   json.NewEncoder(w).Encode(rni)
         .          .    712:}
         .          .    713:
         .          .    714:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .       10ms    715:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    716:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
         .          .    717:   if err != nil || rootCategoryID <= 0 {
         .          .    718:       outputErrorMsg(w, http.StatusBadRequest, "incorrect category id")
         .          .    719:       return
         .          .    720:   }
         .          .    721:
         .          .    722:   rootCategory, err := getCategoryByID(dbx, rootCategoryID)
         .          .    723:   if err != nil || rootCategory.ParentID != 0 {
         .          .    724:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    725:       return
         .          .    726:   }
         .          .    727:
         .          .    728:   var categoryIDs []int
         .      1.03s    729:   err = dbx.Select(&categoryIDs, "SELECT id FROM categories WHERE parent_id=?", rootCategory.ID)
         .          .    730:   if err != nil {
         .          .    731:       log.Print(err)
         .          .    732:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    733:       return
         .          .    734:   }
         .          .    735:
         .       20ms    736:   query := r.URL.Query()
         .          .    737:   itemIDStr := query.Get("item_id")
         .          .    738:   var itemID int64
         .          .    739:   if itemIDStr != "" {
         .          .    740:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    741:       if err != nil || itemID <= 0 {
         .          .    742:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    743:           return
         .          .    744:       }
         .          .    745:   }
         .          .    746:
         .          .    747:   createdAtStr := query.Get("created_at")
         .          .    748:   var createdAt int64
         .          .    749:   if createdAtStr != "" {
         .          .    750:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    751:       if err != nil || createdAt <= 0 {
         .          .    752:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    753:           return
         .          .    754:       }
         .          .    755:   }
         .          .    756:
         .          .    757:   var inQuery string
         .          .    758:   var inArgs []interface{}
         .          .    759:   if itemID > 0 && createdAt > 0 {
         .          .    760:       // paging
         .       10ms    761:       inQuery, inArgs, err = sqlx.In(
         .          .    762:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    763:           ItemStatusOnSale,
         .          .    764:           ItemStatusSoldOut,
         .          .    765:           categoryIDs,
         .          .    766:           time.Unix(createdAt, 0),
         .          .    767:           time.Unix(createdAt, 0),
         .          .    768:           itemID,
         .          .    769:           ItemsPerPage+1,
         .          .    770:       )
         .          .    771:       if err != nil {
         .          .    772:           log.Print(err)
         .          .    773:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    774:           return
         .          .    775:       }
         .          .    776:   } else {
         .          .    777:       // 1st page
         .          .    778:       inQuery, inArgs, err = sqlx.In(
         .          .    779:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    780:           ItemStatusOnSale,
         .          .    781:           ItemStatusSoldOut,
         .          .    782:           categoryIDs,
         .          .    783:           ItemsPerPage+1,
         .          .    784:       )
         .          .    785:       if err != nil {
         .          .    786:           log.Print(err)
         .          .    787:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    788:           return
         .          .    789:       }
         .          .    790:   }
         .          .    791:
         .          .    792:   items := []Item{}
         .      560ms    793:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    794:
         .          .    795:   if err != nil {
         .          .    796:       log.Print(err)
         .          .    797:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    798:       return
         .          .    799:   }
         .          .    800:
         .          .    801:   itemSimples := []ItemSimple{}
         .          .    802:   for _, item := range items {
         .      1.32s    803:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    804:       if err != nil {
         .          .    805:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    806:           return
         .          .    807:       }
         .       20ms    808:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    809:       if err != nil {
         .          .    810:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    811:           return
         .          .    812:       }
         .       10ms    813:       itemSimples = append(itemSimples, ItemSimple{
         .          .    814:           ID:         item.ID,
         .          .    815:           SellerID:   item.SellerID,
         .          .    816:           Seller:     &seller,
         .          .    817:           Status:     item.Status,
      10ms       10ms    818:           Name:       item.Name,
         .          .    819:           Price:      item.Price,
         .       40ms    820:           ImageURL:   getImageURL(item.ImageName),
         .          .    821:           CategoryID: item.CategoryID,
         .          .    822:           Category:   &category,
         .          .    823:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    824:       })
         .          .    825:   }
         .          .    826:
         .          .    827:   hasNext := false
         .          .    828:   if len(itemSimples) > ItemsPerPage {
         .          .    829:       hasNext = true
         .          .    830:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    831:   }
         .          .    832:
         .          .    833:   rni := resNewItems{
         .          .    834:       RootCategoryID:   rootCategory.ID,
         .          .    835:       RootCategoryName: rootCategory.CategoryName,
         .          .    836:       Items:            itemSimples,
         .          .    837:       HasNext:          hasNext,
         .          .    838:   }
         .          .    839:
         .          .    840:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      170ms    841:   json.NewEncoder(w).Encode(rni)
         .          .    842:
         .          .    843:}
         .          .    844:
         .          .    845:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    846:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      540ms (flat, cum)  2.11% of Total
         .          .    614:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .    615:   json.NewEncoder(w).Encode(res)
         .          .    616:}
         .          .    617:
         .          .    618:func getNewItems(w http.ResponseWriter, r *http.Request) {
         .       10ms    619:   query := r.URL.Query()
         .          .    620:   itemIDStr := query.Get("item_id")
         .          .    621:   var itemID int64
         .          .    622:   var err error
         .          .    623:   if itemIDStr != "" {
         .          .    624:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    625:       if err != nil || itemID <= 0 {
         .          .    626:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    627:           return
         .          .    628:       }
         .          .    629:   }
         .          .    630:
         .          .    631:   createdAtStr := query.Get("created_at")
         .          .    632:   var createdAt int64
         .          .    633:   if createdAtStr != "" {
         .          .    634:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    635:       if err != nil || createdAt <= 0 {
         .          .    636:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    637:           return
         .          .    638:       }
         .          .    639:   }
         .          .    640:
         .          .    641:   items := []Item{}
         .          .    642:   if itemID > 0 && createdAt > 0 {
         .          .    643:       // paging
         .      240ms    644:       err := dbx.Select(&items,
         .          .    645:           "SELECT * FROM items WHERE status IN (?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    646:           ItemStatusOnSale,
         .          .    647:           ItemStatusSoldOut,
         .          .    648:           time.Unix(createdAt, 0),
         .          .    649:           time.Unix(createdAt, 0),
         .          .    650:           itemID,
         .          .    651:           ItemsPerPage+1,
         .          .    652:       )
         .          .    653:       if err != nil {
         .          .    654:           log.Print(err)
         .          .    655:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    656:           return
         .          .    657:       }
         .          .    658:   } else {
         .          .    659:       // 1st page
         .       20ms    660:       err := dbx.Select(&items,
         .          .    661:           "SELECT * FROM items WHERE status IN (?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    662:           ItemStatusOnSale,
         .          .    663:           ItemStatusSoldOut,
         .          .    664:           ItemsPerPage+1,
         .          .    665:       )
         .          .    666:       if err != nil {
         .          .    667:           log.Print(err)
         .          .    668:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    669:           return
         .          .    670:       }
         .          .    671:   }
         .          .    672:
         .          .    673:   itemSimples := []ItemSimple{}
         .       10ms    674:   for _, item := range items {
         .      220ms    675:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    676:       if err != nil {
         .          .    677:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    678:           return
         .          .    679:       }
         .          .    680:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    681:       if err != nil {
         .          .    682:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    683:           return
         .          .    684:       }
         .          .    685:       itemSimples = append(itemSimples, ItemSimple{
         .          .    686:           ID:         item.ID,
         .          .    687:           SellerID:   item.SellerID,
         .          .    688:           Seller:     &seller,
         .          .    689:           Status:     item.Status,
         .          .    690:           Name:       item.Name,
         .          .    691:           Price:      item.Price,
         .       10ms    692:           ImageURL:   getImageURL(item.ImageName),
         .          .    693:           CategoryID: item.CategoryID,
         .          .    694:           Category:   &category,
         .          .    695:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    696:       })
         .          .    697:   }
         .          .    698:
         .          .    699:   hasNext := false
         .          .    700:   if len(itemSimples) > ItemsPerPage {
         .          .    701:       hasNext = true
         .          .    702:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    703:   }
         .          .    704:
         .          .    705:   rni := resNewItems{
         .          .    706:       Items:   itemSimples,
         .          .    707:       HasNext: hasNext,
         .          .    708:   }
         .          .    709:
         .          .    710:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       30ms    711:   json.NewEncoder(w).Encode(rni)
         .          .    712:}
         .          .    713:
         .          .    714:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    715:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    716:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getPaymentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0      140ms (flat, cum)  0.55% of Total
         .          .    535:   }
         .          .    536:   return config.Val, err
         .          .    537:}
         .          .    538:
         .          .    539:func getPaymentServiceURL() string {
         .      140ms    540:   val, _ := getConfigByName("payment_service_url")
         .          .    541:   if val == "" {
         .          .    542:       return DefaultPaymentServiceURL
         .          .    543:   }
         .          .    544:   return val
         .          .    545:}
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      280ms (flat, cum)  1.09% of Total
         .          .    363:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    364:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    365:}
         .          .    366:
         .          .    367:func getSession(r *http.Request) *sessions.Session {
         .      280ms    368:   session, _ := store.Get(r, sessionName)
         .          .    369:
         .          .    370:   return session
         .          .    371:}
         .          .    372:
         .          .    373:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0      1.73s (flat, cum)  6.75% of Total
         .          .   2248:       ItemUpdatedAt: targetItem.UpdatedAt.Unix(),
         .          .   2249:   })
         .          .   2250:}
         .          .   2251:
         .          .   2252:func getSettings(w http.ResponseWriter, r *http.Request) {
         .       70ms   2253:   csrfToken := getCSRFToken(r)
         .          .   2254:
         .      1.39s   2255:   user, _, errMsg := getUser(r)
         .          .   2256:
         .          .   2257:   ress := resSetting{}
         .          .   2258:   ress.CSRFToken = csrfToken
         .          .   2259:   if errMsg == "" {
         .          .   2260:       ress.User = &user
         .          .   2261:   }
         .          .   2262:
         .       70ms   2263:   ress.PaymentServiceURL = getPaymentServiceURL()
         .          .   2264:
         .          .   2265:   categories := []Category{}
         .          .   2266:
         .      140ms   2267:   err := dbx.Select(&categories, "SELECT * FROM categories")
         .          .   2268:   if err != nil {
         .          .   2269:       log.Print(err)
         .          .   2270:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2271:       return
         .          .   2272:   }
         .          .   2273:   ress.Categories = categories
         .          .   2274:
         .          .   2275:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       60ms   2276:   json.NewEncoder(w).Encode(ress)
         .          .   2277:}
         .          .   2278:
         .          .   2279:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2280:   rl := reqLogin{}
         .          .   2281:   err := json.NewDecoder(r.Body).Decode(&rl)
ROUTINE ======================== main.getShipmentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0      1.24s (flat, cum)  4.84% of Total
         .          .    543:   }
         .          .    544:   return val
         .          .    545:}
         .          .    546:
         .          .    547:func getShipmentServiceURL() string {
         .      1.24s    548:   val, _ := getConfigByName("shipment_service_url")
         .          .    549:   if val == "" {
         .          .    550:       return DefaultShipmentServiceURL
         .          .    551:   }
         .          .    552:   return val
         .          .    553:}
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      1.29s (flat, cum)  5.03% of Total
         .          .    950:   json.NewEncoder(w).Encode(rui)
         .          .    951:}
         .          .    952:
         .          .    953:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    954:
         .       60ms    955:   user, errCode, errMsg := getUser(r)
         .          .    956:   if errMsg != "" {
         .          .    957:       outputErrorMsg(w, errCode, errMsg)
         .          .    958:       return
         .          .    959:   }
         .          .    960:
         .          .    961:   query := r.URL.Query()
         .          .    962:   itemIDStr := query.Get("item_id")
         .          .    963:   var err error
         .          .    964:   var itemID int64
         .          .    965:   if itemIDStr != "" {
         .          .    966:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    967:       if err != nil || itemID <= 0 {
         .          .    968:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    969:           return
         .          .    970:       }
         .          .    971:   }
         .          .    972:
         .          .    973:   createdAtStr := query.Get("created_at")
         .          .    974:   var createdAt int64
         .          .    975:   if createdAtStr != "" {
         .          .    976:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    977:       if err != nil || createdAt <= 0 {
         .          .    978:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    979:           return
         .          .    980:       }
         .          .    981:   }
         .          .    982:
         .          .    983:   tx := dbx.MustBegin()
         .          .    984:   items := []Item{}
         .          .    985:   if itemID > 0 && createdAt > 0 {
         .          .    986:       // paging
         .       10ms    987:       err := tx.Select(&items,
         .          .    988:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    989:           user.ID,
         .          .    990:           user.ID,
         .          .    991:           ItemStatusOnSale,
         .          .    992:           ItemStatusTrading,
         .          .    993:           ItemStatusSoldOut,
         .          .    994:           ItemStatusCancel,
         .          .    995:           ItemStatusStop,
         .          .    996:           time.Unix(createdAt, 0),
         .          .    997:           time.Unix(createdAt, 0),
         .          .    998:           itemID,
         .          .    999:           TransactionsPerPage+1,
         .          .   1000:       )
         .          .   1001:       if err != nil {
         .          .   1002:           log.Print(err)
         .          .   1003:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1004:           tx.Rollback()
         .          .   1005:           return
         .          .   1006:       }
         .          .   1007:   } else {
         .          .   1008:       // 1st page
         .       20ms   1009:       err := tx.Select(&items,
         .          .   1010:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1011:           user.ID,
         .          .   1012:           user.ID,
         .          .   1013:           ItemStatusOnSale,
         .          .   1014:           ItemStatusTrading,
         .          .   1015:           ItemStatusSoldOut,
         .          .   1016:           ItemStatusCancel,
         .          .   1017:           ItemStatusStop,
         .          .   1018:           TransactionsPerPage+1,
         .          .   1019:       )
         .          .   1020:       if err != nil {
         .          .   1021:           log.Print(err)
         .          .   1022:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1023:           tx.Rollback()
         .          .   1024:           return
         .          .   1025:       }
         .          .   1026:   }
         .          .   1027:
         .          .   1028:   itemDetails := []ItemDetail{}
         .          .   1029:   for _, item := range items {
         .       10ms   1030:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .   1031:       if err != nil {
         .          .   1032:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1033:           tx.Rollback()
         .          .   1034:           return
         .          .   1035:       }
         .          .   1036:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .   1037:       if err != nil {
         .          .   1038:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1039:           tx.Rollback()
         .          .   1040:           return
         .          .   1041:       }
         .          .   1042:
         .          .   1043:       itemDetail := ItemDetail{
         .          .   1044:           ID:       item.ID,
         .          .   1045:           SellerID: item.SellerID,
         .          .   1046:           Seller:   &seller,
         .          .   1047:           // BuyerID
         .          .   1048:           // Buyer
         .          .   1049:           Status:      item.Status,
         .          .   1050:           Name:        item.Name,
         .          .   1051:           Price:       item.Price,
         .          .   1052:           Description: item.Description,
         .          .   1053:           ImageURL:    getImageURL(item.ImageName),
         .          .   1054:           CategoryID:  item.CategoryID,
         .          .   1055:           // TransactionEvidenceID
         .          .   1056:           // TransactionEvidenceStatus
         .          .   1057:           // ShippingStatus
         .          .   1058:           Category:  &category,
         .          .   1059:           CreatedAt: item.CreatedAt.Unix(),
         .          .   1060:       }
         .          .   1061:
         .          .   1062:       if item.BuyerID != 0 {
         .       10ms   1063:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .   1064:           if err != nil {
         .          .   1065:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1066:               tx.Rollback()
         .          .   1067:               return
         .          .   1068:           }
         .          .   1069:           itemDetail.BuyerID = item.BuyerID
         .          .   1070:           itemDetail.Buyer = &buyer
         .          .   1071:       }
         .          .   1072:
         .          .   1073:       transactionEvidence := TransactionEvidence{}
         .      120ms   1074:       err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1075:       if err != nil && err != sql.ErrNoRows {
         .          .   1076:           // It's able to ignore ErrNoRows
         .          .   1077:           log.Print(err)
         .          .   1078:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1079:           tx.Rollback()
         .          .   1080:           return
         .          .   1081:       }
         .          .   1082:
         .          .   1083:       if transactionEvidence.ID > 0 {
         .          .   1084:           shipping := Shipping{}
         .       30ms   1085:           err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1086:           if err == sql.ErrNoRows {
         .          .   1087:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1088:               tx.Rollback()
         .          .   1089:               return
         .          .   1090:           }
         .          .   1091:           if err != nil {
         .          .   1092:               log.Print(err)
         .          .   1093:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1094:               tx.Rollback()
         .          .   1095:               return
         .          .   1096:           }
         .      1.01s   1097:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1098:               ReserveID: shipping.ReserveID,
         .          .   1099:           })
         .          .   1100:           if err != nil {
         .          .   1101:               log.Print(err)
         .          .   1102:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1103:               tx.Rollback()
         .          .   1104:               return
         .          .   1105:           }
         .          .   1106:
         .          .   1107:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1108:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1109:           itemDetail.ShippingStatus = ssr.Status
         .          .   1110:       }
         .          .   1111:
         .          .   1112:       itemDetails = append(itemDetails, itemDetail)
         .          .   1113:   }
         .          .   1114:   tx.Commit()
         .          .   1115:
         .          .   1116:   hasNext := false
         .          .   1117:   if len(itemDetails) > TransactionsPerPage {
         .          .   1118:       hasNext = true
         .          .   1119:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1120:   }
         .          .   1121:
         .          .   1122:   rts := resTransactions{
         .          .   1123:       Items:   itemDetails,
         .          .   1124:       HasNext: hasNext,
         .          .   1125:   }
         .          .   1126:
         .          .   1127:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1128:   json.NewEncoder(w).Encode(rts)
         .          .   1129:
         .          .   1130:}
         .          .   1131:
         .          .   1132:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1133:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
      10ms      5.52s (flat, cum) 21.54% of Total
         .          .    380:
         .          .    381:   return csrfToken.(string)
         .          .    382:}
         .          .    383:
         .          .    384:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      150ms    385:   session := getSession(r)
         .          .    386:   userID, ok := session.Values["user_id"]
         .          .    387:   if !ok {
         .          .    388:       return user, http.StatusNotFound, "no session"
         .          .    389:   }
         .          .    390:
      10ms      5.37s    391:   err := dbx.Get(&user, "SELECT * FROM users WHERE id = ?", userID)
         .          .    392:   if err == sql.ErrNoRows {
         .          .    393:       return user, http.StatusNotFound, "user not found"
         .          .    394:   }
         .          .    395:   if err != nil {
         .          .    396:       log.Print(err)
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
         0      220ms (flat, cum)  0.86% of Total
         .          .    848:   if err != nil || userID <= 0 {
         .          .    849:       outputErrorMsg(w, http.StatusBadRequest, "incorrect user id")
         .          .    850:       return
         .          .    851:   }
         .          .    852:
         .       20ms    853:   userSimple, err := getUserSimpleByID(dbx, userID)
         .          .    854:   if err != nil {
         .          .    855:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .    856:       return
         .          .    857:   }
         .          .    858:
         .          .    859:   query := r.URL.Query()
         .          .    860:   itemIDStr := query.Get("item_id")
         .          .    861:   var itemID int64
         .          .    862:   if itemIDStr != "" {
         .          .    863:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    864:       if err != nil || itemID <= 0 {
         .          .    865:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    866:           return
         .          .    867:       }
         .          .    868:   }
         .          .    869:
         .          .    870:   createdAtStr := query.Get("created_at")
         .          .    871:   var createdAt int64
         .          .    872:   if createdAtStr != "" {
         .          .    873:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    874:       if err != nil || createdAt <= 0 {
         .          .    875:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    876:           return
         .          .    877:       }
         .          .    878:   }
         .          .    879:
         .          .    880:   items := []Item{}
         .          .    881:   if itemID > 0 && createdAt > 0 {
         .          .    882:       // paging
         .       70ms    883:       err := dbx.Select(&items,
         .          .    884:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    885:           userSimple.ID,
         .          .    886:           ItemStatusOnSale,
         .          .    887:           ItemStatusTrading,
         .          .    888:           ItemStatusSoldOut,
         .          .    889:           time.Unix(createdAt, 0),
         .          .    890:           time.Unix(createdAt, 0),
         .          .    891:           itemID,
         .          .    892:           ItemsPerPage+1,
         .          .    893:       )
         .          .    894:       if err != nil {
         .          .    895:           log.Print(err)
         .          .    896:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    897:           return
         .          .    898:       }
         .          .    899:   } else {
         .          .    900:       // 1st page
         .      120ms    901:       err := dbx.Select(&items,
         .          .    902:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    903:           userSimple.ID,
         .          .    904:           ItemStatusOnSale,
         .          .    905:           ItemStatusTrading,
         .          .    906:           ItemStatusSoldOut,
         .          .    907:           ItemsPerPage+1,
         .          .    908:       )
         .          .    909:       if err != nil {
         .          .    910:           log.Print(err)
         .          .    911:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    912:           return
         .          .    913:       }
         .          .    914:   }
         .          .    915:
         .          .    916:   itemSimples := []ItemSimple{}
         .          .    917:   for _, item := range items {
         .          .    918:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    919:       if err != nil {
         .          .    920:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    921:           return
         .          .    922:       }
         .          .    923:       itemSimples = append(itemSimples, ItemSimple{
         .          .    924:           ID:         item.ID,
         .          .    925:           SellerID:   item.SellerID,
         .          .    926:           Seller:     &userSimple,
         .          .    927:           Status:     item.Status,
         .          .    928:           Name:       item.Name,
         .          .    929:           Price:      item.Price,
         .          .    930:           ImageURL:   getImageURL(item.ImageName),
         .          .    931:           CategoryID: item.CategoryID,
         .          .    932:           Category:   &category,
         .          .    933:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    934:       })
         .          .    935:   }
         .          .    936:
         .          .    937:   hasNext := false
         .          .    938:   if len(itemSimples) > ItemsPerPage {
         .          .    939:       hasNext = true
         .          .    940:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    941:   }
         .          .    942:
         .          .    943:   rui := resUserItems{
         .          .    944:       User:    &userSimple,
         .          .    945:       Items:   itemSimples,
         .          .    946:       HasNext: hasNext,
         .          .    947:   }
         .          .    948:
         .          .    949:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms    950:   json.NewEncoder(w).Encode(rui)
         .          .    951:}
         .          .    952:
         .          .    953:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    954:
         .          .    955:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      1.63s (flat, cum)  6.36% of Total
         .          .    424:   }
         .          .    425:   return nil
         .          .    426:}
         .          .    427:
         .          .    428:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       10ms    429:   if us, ok := UserSimpleCache[userID]; ok {
         .      1.62s    430:       numsellStr, err := redisClient.Get(context.Background(), fmt.Sprintf(UserSimpleCacheKeyFmt, userID)).Result()
         .          .    431:       if err != nil {
         .          .    432:           return us, err
         .          .    433:       }
         .          .    434:       numsell, err := strconv.Atoi(numsellStr)
         .          .    435:       if err != nil {
ROUTINE ======================== main.initializeSimpleUserCache in /home/isucon/isucari/webapp/go/main.go
         0       50ms (flat, cum)   0.2% of Total
         .          .    402:
         .          .    403:var UserSimpleCache = map[int64]UserSimple{}
         .          .    404:
         .          .    405:func initializeSimpleUserCache(db *sqlx.DB) error {
         .          .    406:   var users []User
         .       10ms    407:   err := db.Select(&users, "SELECT * FROM users")
         .          .    408:   if err != nil {
         .          .    409:       return err
         .          .    410:   }
         .          .    411:   for _, u := range users {
         .          .    412:       UserSimpleCache[u.ID] = UserSimple{
         .          .    413:           ID:           u.ID,
         .          .    414:           AccountName:  u.AccountName,
         .          .    415:           NumSellItems: u.NumSellItems,
         .          .    416:       }
         .          .    417:
         .          .    418:       // numsell item set to redis
         .       10ms    419:       key := fmt.Sprintf(UserSimpleCacheKeyFmt, u.ID)
         .       30ms    420:       err = redisClient.Set(context.Background(), key, u.NumSellItems, 0).Err()
         .          .    421:       if err != nil {
         .          .    422:           return err
         .          .    423:       }
         .          .    424:   }
         .          .    425:   return nil
ROUTINE ======================== main.postBump in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.039% of Total
         .          .   2157:   if csrfToken != getCSRFToken(r) {
         .          .   2158:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   2159:       return
         .          .   2160:   }
         .          .   2161:
         .       10ms   2162:   user, errCode, errMsg := getUser(r)
         .          .   2163:   if errMsg != "" {
         .          .   2164:       outputErrorMsg(w, errCode, errMsg)
         .          .   2165:       return
         .          .   2166:   }
         .          .   2167:
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0      3.09s (flat, cum) 12.06% of Total
         .          .   1379:}
         .          .   1380:
         .          .   1381:func postBuy(w http.ResponseWriter, r *http.Request) {
         .          .   1382:   rb := reqBuy{}
         .          .   1383:
         .       30ms   1384:   err := json.NewDecoder(r.Body).Decode(&rb)
         .          .   1385:   if err != nil {
         .          .   1386:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1387:       return
         .          .   1388:   }
         .          .   1389:
         .       40ms   1390:   if rb.CSRFToken != getCSRFToken(r) {
         .          .   1391:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1392:
         .          .   1393:       return
         .          .   1394:   }
         .          .   1395:
         .      2.75s   1396:   buyer, errCode, errMsg := getUser(r)
         .          .   1397:   if errMsg != "" {
         .          .   1398:       outputErrorMsg(w, errCode, errMsg)
         .          .   1399:       return
         .          .   1400:   }
         .          .   1401:
         .       20ms   1402:   tx := dbx.MustBegin()
         .          .   1403:
         .          .   1404:   targetItem := Item{}
         .       40ms   1405:   err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", rb.ItemID)
         .          .   1406:   if err == sql.ErrNoRows {
         .          .   1407:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1408:       tx.Rollback()
         .          .   1409:       return
         .          .   1410:   }
         .          .   1411:   if err != nil {
         .          .   1412:       log.Print(err)
         .          .   1413:
         .          .   1414:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1415:       tx.Rollback()
         .          .   1416:       return
         .          .   1417:   }
         .          .   1418:
         .          .   1419:   if targetItem.Status != ItemStatusOnSale {
         .          .   1420:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .       40ms   1421:       tx.Rollback()
         .          .   1422:       return
         .          .   1423:   }
         .          .   1424:
         .          .   1425:   if targetItem.SellerID == buyer.ID {
         .          .   1426:       outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません")
         .          .   1427:       tx.Rollback()
         .          .   1428:       return
         .          .   1429:   }
         .          .   1430:
         .          .   1431:   seller := User{}
         .          .   1432:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", targetItem.SellerID)
         .          .   1433:   if err == sql.ErrNoRows {
         .          .   1434:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1435:       tx.Rollback()
         .          .   1436:       return
         .          .   1437:   }
         .          .   1438:   if err != nil {
         .          .   1439:       log.Print(err)
         .          .   1440:
         .          .   1441:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1442:       tx.Rollback()
         .          .   1443:       return
         .          .   1444:   }
         .          .   1445:
         .          .   1446:   category, err := getCategoryByID(tx, targetItem.CategoryID)
         .          .   1447:   if err != nil {
         .          .   1448:       log.Print(err)
         .          .   1449:
         .          .   1450:       outputErrorMsg(w, http.StatusInternalServerError, "category id error")
         .          .   1451:       tx.Rollback()
         .          .   1452:       return
         .          .   1453:   }
         .          .   1454:
         .          .   1455:   var transactionEvidenceID int64
         .          .   1456:   if err := tx.Get(
         .          .   1457:       &transactionEvidenceID,
         .          .   1458:       "INSERT INTO transaction_evidences (seller_id, buyer_id, status, item_id, item_name, item_price, item_description,item_category_id,item_root_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   1459:       targetItem.SellerID,
         .          .   1460:       buyer.ID,
         .          .   1461:       TransactionEvidenceStatusWaitShipping,
         .          .   1462:       targetItem.ID,
         .          .   1463:       targetItem.Name,
         .          .   1464:       targetItem.Price,
         .          .   1465:       targetItem.Description,
         .          .   1466:       category.ID,
         .          .   1467:       category.ParentID,
         .          .   1468:   ); err != nil {
         .          .   1469:       log.Print(err)
         .          .   1470:
         .          .   1471:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1472:       tx.Rollback()
         .          .   1473:       return
         .          .   1474:   }
         .          .   1475:
         .          .   1476:   _, err = tx.Exec("UPDATE items SET buyer_id = ?, status = ?, updated_at = ? WHERE id = ?",
         .          .   1477:       buyer.ID,
         .          .   1478:       ItemStatusTrading,
         .          .   1479:       time.Now(),
         .          .   1480:       targetItem.ID,
         .          .   1481:   )
         .          .   1482:   if err != nil {
         .          .   1483:       log.Print(err)
         .          .   1484:
         .          .   1485:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1486:       tx.Rollback()
         .          .   1487:       return
         .          .   1488:   }
         .          .   1489:
         .       80ms   1490:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1491:       ToAddress:   buyer.Address,
         .          .   1492:       ToName:      buyer.AccountName,
         .          .   1493:       FromAddress: seller.Address,
         .          .   1494:       FromName:    seller.AccountName,
         .          .   1495:   })
         .          .   1496:   if err != nil {
         .          .   1497:       log.Print(err)
         .          .   1498:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1499:       tx.Rollback()
         .          .   1500:
         .          .   1501:       return
         .          .   1502:   }
         .          .   1503:
         .       80ms   1504:   pstr, err := APIPaymentToken(getPaymentServiceURL(), &APIPaymentServiceTokenReq{
         .          .   1505:       ShopID: PaymentServiceIsucariShopID,
         .          .   1506:       Token:  rb.Token,
         .          .   1507:       APIKey: PaymentServiceIsucariAPIKey,
         .          .   1508:       Price:  targetItem.Price,
         .          .   1509:   })
         .          .   1510:   if err != nil {
         .          .   1511:       log.Print(err)
         .          .   1512:
         .          .   1513:       outputErrorMsg(w, http.StatusInternalServerError, "payment service is failed")
         .          .   1514:       tx.Rollback()
         .          .   1515:       return
         .          .   1516:   }
         .          .   1517:
         .          .   1518:   if pstr.Status == "invalid" {
         .          .   1519:       outputErrorMsg(w, http.StatusBadRequest, "カード情報に誤りがあります")
         .          .   1520:       tx.Rollback()
         .          .   1521:       return
         .          .   1522:   }
         .          .   1523:
         .          .   1524:   if pstr.Status == "fail" {
         .          .   1525:       outputErrorMsg(w, http.StatusBadRequest, "カードの残高が足りません")
         .          .   1526:       tx.Rollback()
         .          .   1527:       return
         .          .   1528:   }
         .          .   1529:
         .          .   1530:   if pstr.Status != "ok" {
         .          .   1531:       outputErrorMsg(w, http.StatusBadRequest, "想定外のエラー")
         .          .   1532:       tx.Rollback()
         .          .   1533:       return
         .          .   1534:   }
         .          .   1535:
         .       10ms   1536:   _, err = tx.Exec("INSERT INTO shippings (transaction_evidence_id, status, item_name, item_id, reserve_id, reserve_time, to_address, to_name, from_address, from_name, img_binary) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
         .          .   1537:       transactionEvidenceID,
         .          .   1538:       ShippingsStatusInitial,
         .          .   1539:       targetItem.Name,
         .          .   1540:       targetItem.ID,
         .          .   1541:       scr.ReserveID,
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       50ms (flat, cum)   0.2% of Total
         .          .   1853:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1854:
         .          .   1855:       return
         .          .   1856:   }
         .          .   1857:
         .       10ms   1858:   buyer, errCode, errMsg := getUser(r)
         .          .   1859:   if errMsg != "" {
         .          .   1860:       outputErrorMsg(w, errCode, errMsg)
         .          .   1861:       return
         .          .   1862:   }
         .          .   1863:
         .          .   1864:   transactionEvidence := TransactionEvidence{}
         .          .   1865:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1866:   if err == sql.ErrNoRows {
         .          .   1867:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1868:       return
         .          .   1869:   }
         .          .   1870:   if err != nil {
         .          .   1871:       log.Print(err)
         .          .   1872:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1873:
         .          .   1874:       return
         .          .   1875:   }
         .          .   1876:
         .          .   1877:   if transactionEvidence.BuyerID != buyer.ID {
         .          .   1878:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1879:       return
         .          .   1880:   }
         .          .   1881:
         .          .   1882:   tx := dbx.MustBegin()
         .          .   1883:   item := Item{}
         .          .   1884:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1885:   if err == sql.ErrNoRows {
         .          .   1886:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1887:       tx.Rollback()
         .          .   1888:       return
         .          .   1889:   }
         .          .   1890:   if err != nil {
         .          .   1891:       log.Print(err)
         .          .   1892:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1893:       tx.Rollback()
         .          .   1894:       return
         .          .   1895:   }
         .          .   1896:
         .          .   1897:   if item.Status != ItemStatusTrading {
         .          .   1898:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1899:       tx.Rollback()
         .          .   1900:       return
         .          .   1901:   }
         .          .   1902:
         .          .   1903:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ? FOR UPDATE", itemID)
         .          .   1904:   if err == sql.ErrNoRows {
         .          .   1905:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1906:       tx.Rollback()
         .          .   1907:       return
         .          .   1908:   }
         .          .   1909:   if err != nil {
         .          .   1910:       log.Print(err)
         .          .   1911:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1912:       tx.Rollback()
         .          .   1913:       return
         .          .   1914:   }
         .          .   1915:
         .          .   1916:   if transactionEvidence.Status != TransactionEvidenceStatusWaitDone {
         .          .   1917:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1918:       tx.Rollback()
         .          .   1919:       return
         .          .   1920:   }
         .          .   1921:
         .          .   1922:   shipping := Shipping{}
         .          .   1923:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1924:   if err != nil {
         .          .   1925:       log.Print(err)
         .          .   1926:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1927:       tx.Rollback()
         .          .   1928:       return
         .          .   1929:   }
         .          .   1930:
         .       30ms   1931:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1932:       ReserveID: shipping.ReserveID,
         .          .   1933:   })
         .          .   1934:   if err != nil {
         .          .   1935:       log.Print(err)
         .          .   1936:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1937:       tx.Rollback()
         .          .   1938:
         .          .   1939:       return
         .          .   1940:   }
         .          .   1941:
         .          .   1942:   if !(ssr.Status == ShippingsStatusDone) {
         .          .   1943:       outputErrorMsg(w, http.StatusBadRequest, "shipment service側で配送完了になっていません")
         .          .   1944:       tx.Rollback()
         .          .   1945:       return
         .          .   1946:   }
         .          .   1947:
         .          .   1948:   _, err = tx.Exec("UPDATE shippings SET status = ?, updated_at = ? WHERE transaction_evidence_id = ?",
         .          .   1949:       ShippingsStatusDone,
         .          .   1950:       time.Now(),
         .          .   1951:       transactionEvidence.ID,
         .          .   1952:   )
         .          .   1953:   if err != nil {
         .          .   1954:       log.Print(err)
         .          .   1955:
         .          .   1956:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1957:       tx.Rollback()
         .          .   1958:       return
         .          .   1959:   }
         .          .   1960:
         .          .   1961:   _, err = tx.Exec("UPDATE transaction_evidences SET status = ?, updated_at = ? WHERE id = ?",
         .          .   1962:       TransactionEvidenceStatusDone,
         .          .   1963:       time.Now(),
         .          .   1964:       transactionEvidence.ID,
         .          .   1965:   )
         .          .   1966:   if err != nil {
         .          .   1967:       log.Print(err)
         .          .   1968:
         .          .   1969:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1970:       tx.Rollback()
         .          .   1971:       return
         .          .   1972:   }
         .          .   1973:
         .          .   1974:   _, err = tx.Exec("UPDATE items SET status = ?, updated_at = ? WHERE id = ?",
         .          .   1975:       ItemStatusSoldOut,
         .          .   1976:       time.Now(),
         .          .   1977:       itemID,
         .          .   1978:   )
         .          .   1979:   if err != nil {
         .          .   1980:       log.Print(err)
         .          .   1981:
         .          .   1982:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1983:       tx.Rollback()
         .          .   1984:       return
         .          .   1985:   }
         .          .   1986:
         .       10ms   1987:   tx.Commit()
         .          .   1988:
         .          .   1989:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   1990:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   1991:}
         .          .   1992:
ROUTINE ======================== main.postInitialize in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.23% of Total
         .          .    572:   if err != nil {
         .          .    573:       outputErrorMsg(w, http.StatusInternalServerError, "exec init.sh error")
         .          .    574:       return
         .          .    575:   }
         .          .    576:
         .       10ms    577:   _, err = dbx.Exec(
         .          .    578:       "INSERT INTO configs (name, val) VALUES (?, ?) ON CONFLICT (NAME) DO UPDATE SET val = ?",
         .          .    579:       "payment_service_url",
         .          .    580:       ri.PaymentServiceURL,
         .          .    581:       ri.PaymentServiceURL,
         .          .    582:   )
         .          .    583:   if err != nil {
         .          .    584:       log.Print(err)
         .          .    585:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    586:       return
         .          .    587:   }
         .          .    588:   _, err = dbx.Exec(
         .          .    589:       "INSERT INTO configs (name, val) VALUES (?, ?) ON CONFLICT (NAME) DO UPDATE SET val = ?",
         .          .    590:       "shipment_service_url",
         .          .    591:       ri.ShipmentServiceURL,
         .          .    592:       ri.ShipmentServiceURL,
         .          .    593:   )
         .          .    594:   if err != nil {
         .          .    595:       log.Print(err)
         .          .    596:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    597:       return
         .          .    598:   }
         .          .    599:
         .          .    600:   // cache
         .       50ms    601:   err = initializeSimpleUserCache(dbx)
         .          .    602:   if err != nil {
         .          .    603:       log.Print(err)
         .          .    604:       outputErrorMsg(w, http.StatusInternalServerError, "cache initialize error")
         .          .    605:   }
         .          .    606:
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0     11.10s (flat, cum) 43.31% of Total
         .          .   2292:
         .          .   2293:       return
         .          .   2294:   }
         .          .   2295:
         .          .   2296:   u := User{}
         .      2.29s   2297:   err = dbx.Get(&u, "SELECT * FROM users WHERE account_name = ?", accountName)
         .          .   2298:   if err == sql.ErrNoRows {
         .          .   2299:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2300:       return
         .          .   2301:   }
         .          .   2302:   if err != nil {
         .          .   2303:       log.Print(err)
         .          .   2304:
         .          .   2305:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2306:       return
         .          .   2307:   }
         .          .   2308:   hashedPassword := u.HashedPassword
         .          .   2309:   var rehashedPassword []byte
         .          .   2310:   var rehashed bool
         .       60ms   2311:   if err := dbx.Get(&rehashedPassword, "SELECT hashed_password FROM user_password WHERE user_id = ?", u.ID); err != nil && err != sql.ErrNoRows {
         .          .   2312:       log.Print(err)
         .          .   2313:
         .          .   2314:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2315:       return
         .          .   2316:   } else if err == nil {
         .          .   2317:       rehashed = true
         .          .   2318:       hashedPassword = rehashedPassword
         .          .   2319:   }
         .          .   2320:
         .      8.50s   2321:   err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
         .          .   2322:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2323:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2324:       return
         .          .   2325:   }
         .          .   2326:   if err != nil {
         .          .   2327:       log.Print(err)
         .          .   2328:
         .          .   2329:       outputErrorMsg(w, http.StatusInternalServerError, "crypt error")
         .          .   2330:       return
         .          .   2331:   }
         .          .   2332:
         .          .   2333:   if !rehashed {
         .      140ms   2334:       rehashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
         .          .   2335:       if err != nil {
         .          .   2336:           log.Print(err)
         .          .   2337:
         .          .   2338:           outputErrorMsg(w, http.StatusInternalServerError, "error")
         .          .   2339:           return
         .          .   2340:       }
         .      100ms   2341:       if _, err := dbx.Exec(
         .          .   2342:           "INSERT INTO user_password (user_id, hashed_password) VALUES (?, ?) ON CONFLICT (user_id) DO UPDATE SET hashed_password = ?",
         .          .   2343:           u.ID,
         .          .   2344:           rehashedPassword,
         .          .   2345:           rehashedPassword,
         .          .   2346:       ); err != nil {
         .          .   2347:           log.Print(err)
         .          .   2348:
         .          .   2349:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2350:           return
         .          .   2351:       }
         .          .   2352:   }
         .          .   2353:
         .          .   2354:   session := getSession(r)
         .          .   2355:
         .          .   2356:   session.Values["user_id"] = u.ID
         .          .   2357:   session.Values["csrf_token"] = secureRandomStr(20)
         .       10ms   2358:   if err = session.Save(r, w); err != nil {
         .          .   2359:       log.Print(err)
         .          .   2360:
         .          .   2361:       outputErrorMsg(w, http.StatusInternalServerError, "session error")
         .          .   2362:       return
         .          .   2363:   }
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0      180ms (flat, cum)   0.7% of Total
         .          .   1989:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   1990:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   1991:}
         .          .   1992:
         .          .   1993:func postSell(w http.ResponseWriter, r *http.Request) {
         .       10ms   1994:   csrfToken := r.FormValue("csrf_token")
         .          .   1995:   name := r.FormValue("name")
         .          .   1996:   description := r.FormValue("description")
         .          .   1997:   priceStr := r.FormValue("price")
         .          .   1998:   categoryIDStr := r.FormValue("category_id")
         .          .   1999:
         .          .   2000:   f, header, err := r.FormFile("image")
         .          .   2001:   if err != nil {
         .          .   2002:       log.Print(err)
         .          .   2003:       outputErrorMsg(w, http.StatusBadRequest, "image error")
         .          .   2004:       return
         .          .   2005:   }
         .          .   2006:   defer f.Close()
         .          .   2007:
         .       20ms   2008:   if csrfToken != getCSRFToken(r) {
         .          .   2009:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   2010:       return
         .          .   2011:   }
         .          .   2012:
         .          .   2013:   categoryID, err := strconv.Atoi(categoryIDStr)
         .          .   2014:   if err != nil || categoryID < 0 {
         .          .   2015:       outputErrorMsg(w, http.StatusBadRequest, "category id error")
         .          .   2016:       return
         .          .   2017:   }
         .          .   2018:
         .          .   2019:   price, err := strconv.Atoi(priceStr)
         .          .   2020:   if err != nil {
         .          .   2021:       outputErrorMsg(w, http.StatusBadRequest, "price error")
         .          .   2022:       return
         .          .   2023:   }
         .          .   2024:
         .          .   2025:   if name == "" || description == "" || price == 0 || categoryID == 0 {
         .          .   2026:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2027:
         .          .   2028:       return
         .          .   2029:   }
         .          .   2030:
         .          .   2031:   if price < ItemMinPrice || price > ItemMaxPrice {
         .          .   2032:       outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg)
         .          .   2033:
         .          .   2034:       return
         .          .   2035:   }
         .          .   2036:
         .          .   2037:   category, err := getCategoryByID(dbx, categoryID)
         .          .   2038:   if err != nil || category.ParentID == 0 {
         .          .   2039:       log.Print(categoryID, category)
         .          .   2040:       outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID")
         .          .   2041:       return
         .          .   2042:   }
         .          .   2043:
         .      120ms   2044:   user, errCode, errMsg := getUser(r)
         .          .   2045:   if errMsg != "" {
         .          .   2046:       outputErrorMsg(w, errCode, errMsg)
         .          .   2047:       return
         .          .   2048:   }
         .          .   2049:
         .       10ms   2050:   img, err := ioutil.ReadAll(f)
         .          .   2051:   if err != nil {
         .          .   2052:       log.Print(err)
         .          .   2053:       outputErrorMsg(w, http.StatusInternalServerError, "image error")
         .          .   2054:       return
         .          .   2055:   }
         .          .   2056:
         .          .   2057:   ext := filepath.Ext(header.Filename)
         .          .   2058:
         .          .   2059:   if !(ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif") {
         .          .   2060:       outputErrorMsg(w, http.StatusBadRequest, "unsupported image format error")
         .          .   2061:       return
         .          .   2062:   }
         .          .   2063:
         .          .   2064:   if ext == ".jpeg" {
         .          .   2065:       ext = ".jpg"
         .          .   2066:   }
         .          .   2067:
         .          .   2068:   imgName := fmt.Sprintf("%s%s", secureRandomStr(16), ext)
         .          .   2069:   err = ioutil.WriteFile(fmt.Sprintf("../public/upload/%s", imgName), img, 0644)
         .          .   2070:   if err != nil {
         .          .   2071:       log.Print(err)
         .          .   2072:       outputErrorMsg(w, http.StatusInternalServerError, "Saving image failed")
         .          .   2073:       return
         .          .   2074:   }
         .          .   2075:
         .          .   2076:   tx := dbx.MustBegin()
         .          .   2077:
         .          .   2078:   seller := User{}
         .          .   2079:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", user.ID)
         .          .   2080:   if err == sql.ErrNoRows {
         .          .   2081:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .   2082:       tx.Rollback()
         .          .   2083:       return
         .          .   2084:   }
         .          .   2085:   if err != nil {
         .          .   2086:       log.Print(err)
         .          .   2087:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2088:       tx.Rollback()
         .          .   2089:       return
         .          .   2090:   }
         .          .   2091:
         .          .   2092:   var itemID int64
         .          .   2093:   if err := tx.Get(
         .          .   2094:       &itemID,
         .          .   2095:       "INSERT INTO items (seller_id, status, name, price, description,image_name,category_id) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   2096:       seller.ID,
         .          .   2097:       ItemStatusOnSale,
         .          .   2098:       name,
         .          .   2099:       price,
         .          .   2100:       description,
         .          .   2101:       imgName,
         .          .   2102:       category.ID,
         .          .   2103:   ); err != nil {
         .          .   2104:       log.Print(err)
         .          .   2105:
         .          .   2106:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2107:       return
         .          .   2108:   }
         .          .   2109:
         .          .   2110:   // cache
         .          .   2111:   key := fmt.Sprintf(UserSimpleCacheKeyFmt, user.ID)
         .          .   2112:   err = redisClient.Incr(context.Background(), key).Err()
         .          .   2113:   if err != nil {
         .          .   2114:       log.Print(err)
         .          .   2115:
         .          .   2116:       outputErrorMsg(w, http.StatusInternalServerError, "user simple num sell cache update reds error")
         .          .   2117:       return
         .          .   2118:   }
         .          .   2119:
         .          .   2120:   now := time.Now()
         .       20ms   2121:   _, err = tx.Exec("UPDATE users SET num_sell_items=?, last_bump=? WHERE id=?",
         .          .   2122:       seller.NumSellItems+1,
         .          .   2123:       now,
         .          .   2124:       seller.ID,
         .          .   2125:   )
         .          .   2126:   if err != nil {
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.31% of Total
         .          .   1642:       tx.Rollback()
         .          .   1643:       return
         .          .   1644:   }
         .          .   1645:
         .          .   1646:   shipping := Shipping{}
         .       10ms   1647:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1648:   if err == sql.ErrNoRows {
         .          .   1649:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1650:       tx.Rollback()
         .          .   1651:       return
         .          .   1652:   }
         .          .   1653:   if err != nil {
         .          .   1654:       log.Print(err)
         .          .   1655:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1656:       tx.Rollback()
         .          .   1657:       return
         .          .   1658:   }
         .          .   1659:
         .       70ms   1660:   img, err := APIShipmentRequest(getShipmentServiceURL(), &APIShipmentRequestReq{
         .          .   1661:       ReserveID: shipping.ReserveID,
         .          .   1662:   })
         .          .   1663:   if err != nil {
         .          .   1664:       log.Print(err)
         .          .   1665:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go
         0      110ms (flat, cum)  0.43% of Total
         .          .   1707:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1708:
         .          .   1709:       return
         .          .   1710:   }
         .          .   1711:
         .       20ms   1712:   seller, errCode, errMsg := getUser(r)
         .          .   1713:   if errMsg != "" {
         .          .   1714:       outputErrorMsg(w, errCode, errMsg)
         .          .   1715:       return
         .          .   1716:   }
         .          .   1717:
         .          .   1718:   transactionEvidence := TransactionEvidence{}
         .       10ms   1719:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1720:   if err == sql.ErrNoRows {
         .          .   1721:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1722:       return
         .          .   1723:   }
         .          .   1724:   if err != nil {
         .          .   1725:       log.Print(err)
         .          .   1726:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1727:
         .          .   1728:       return
         .          .   1729:   }
         .          .   1730:
         .          .   1731:   if transactionEvidence.SellerID != seller.ID {
         .          .   1732:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1733:       return
         .          .   1734:   }
         .          .   1735:
         .          .   1736:   tx := dbx.MustBegin()
         .          .   1737:
         .          .   1738:   item := Item{}
         .       10ms   1739:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1740:   if err == sql.ErrNoRows {
         .          .   1741:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1742:       tx.Rollback()
         .          .   1743:       return
         .          .   1744:   }
         .          .   1745:   if err != nil {
         .          .   1746:       log.Print(err)
         .          .   1747:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1748:       tx.Rollback()
         .          .   1749:       return
         .          .   1750:   }
         .          .   1751:
         .          .   1752:   if item.Status != ItemStatusTrading {
         .          .   1753:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1754:       tx.Rollback()
         .          .   1755:       return
         .          .   1756:   }
         .          .   1757:
         .          .   1758:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1759:   if err == sql.ErrNoRows {
         .          .   1760:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1761:       tx.Rollback()
         .          .   1762:       return
         .          .   1763:   }
         .          .   1764:   if err != nil {
         .          .   1765:       log.Print(err)
         .          .   1766:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1767:       tx.Rollback()
         .          .   1768:       return
         .          .   1769:   }
         .          .   1770:
         .          .   1771:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1772:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1773:       tx.Rollback()
         .          .   1774:       return
         .          .   1775:   }
         .          .   1776:
         .          .   1777:   shipping := Shipping{}
         .          .   1778:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1779:   if err == sql.ErrNoRows {
         .          .   1780:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1781:       tx.Rollback()
         .          .   1782:       return
         .          .   1783:   }
         .          .   1784:   if err != nil {
         .          .   1785:       log.Print(err)
         .          .   1786:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1787:       tx.Rollback()
         .          .   1788:       return
         .          .   1789:   }
         .          .   1790:
         .       70ms   1791:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1792:       ReserveID: shipping.ReserveID,
         .          .   1793:   })
         .          .   1794:   if err != nil {
         .          .   1795:       log.Print(err)
         .          .   1796:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
mackee commented 1 year ago

category cache

{"pass":true,"score":11370,"campaign":2,"language":"Go","messages":[]}
tetsuzawa commented 1 year ago
{"pass":true,"score":12210,"campaign":1,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
683 0 59 0 624 0 POST /buy 1.266 4.808 2311.914 3.385 4.768 0.000 49.000 33.294
274 0 261 0 13 0 GET /users/transactions.json 0.004 6.411 792.159 2.891 5.128 0.000 6958.000 5457.507
2117 0 2113 0 4 0 GET /new_items/\d+.json 0.002 0.628 114.199 0.054 0.220 0.000 5682.000 5508.114
76 0 55 0 21 0 POST /ship_done 0.804 0.880 47.279 0.622 0.848 29.000 83.000 38.329
73 0 59 0 14 0 POST /ship 0.804 1.260 46.451 0.636 0.876 29.000 61.000 55.630
1557 0 1549 0 8 0 POST /login 0.000 0.304 45.221 0.029 0.108 73.000 109.000 96.839
52 0 51 0 1 0 POST /complete 0.652 0.848 39.570 0.761 0.844 0.000 34.000 33.346
209 0 209 0 0 0 GET /new_items.json 0.036 0.539 32.061 0.153 0.348 5603.000 6006.000 5782.330
1549 0 1549 0 0 0 GET /settings 0.000 0.188 19.788 0.013 0.076 774.000 794.000 783.524
5444 0 5444 0 0 0 GET /items/\d+.json 0.000 0.152 15.614 0.003 0.008 1060.000 1994.000 1219.269
390 0 390 0 0 0 GET /users/\d+.json 0.004 0.216 3.179 0.008 0.040 96.000 5261.000 3303.267
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.248 2.828 0.049 0.196 51662.000 140633.000 83527.845
89 0 68 0 21 0 POST /sell 0.004 0.532 2.724 0.031 0.152 13.000 106.000 28.888
1 0 1 0 0 0 POST /initialize 2.072 2.072 2.072 2.072 2.072 31.000 31.000 31.000
13 0 13 0 0 0 POST /bump 0.000 0.092 0.168 0.013 0.092 90.000 92.000 90.923
69 0 55 0 14 0 GET /transactions/\d+.png 0.000 0.060 0.144 0.002 0.008 33.000 640.000 501.768
10 0 3 0 7 0 POST /items/edit 0.004 0.044 0.096 0.010 0.044 58.000 93.000 68.400
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.008 0.008 0.008 0.008 0.008 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.004 0.004 0.004 0.004 0.004 774.000 774.000 774.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.004 0.004 0.004 0.004 0.004 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /reports.json 0.000 0.000 0.000 0.000 0.000 35785.000 35785.000 35785.000
$ go tool pprof -list main. -cum -seconds 60 http://localhost:8000/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)

Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.005.pb.gz
Total: 18.34s
ROUTINE ======================== main.APIPaymentToken in /home/isucon/isucari/webapp/go/api.go
         0       10ms (flat, cum) 0.055% of Total
         .          .     74:       }
         .          .     75:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .     76:   }
         .          .     77:
         .          .     78:   pstr := &APIPaymentServiceTokenRes{}
         .       10ms     79:   err = json.NewDecoder(res.Body).Decode(pstr)
         .          .     80:   if err != nil {
         .          .     81:       return nil, err
         .          .     82:   }
         .          .     83:
         .          .     84:   return pstr, nil
ROUTINE ======================== main.APIShipmentCreate in /home/isucon/isucari/webapp/go/api.go
         0       10ms (flat, cum) 0.055% of Total
         .          .     85:}
         .          .     86:
         .          .     87:func APIShipmentCreate(shipmentURL string, param *APIShipmentCreateReq) (*APIShipmentCreateRes, error) {
         .          .     88:   b, _ := json.Marshal(param)
         .          .     89:
         .       10ms     90:   req, err := http.NewRequest(http.MethodPost, shipmentURL+"/create", bytes.NewBuffer(b))
         .          .     91:   if err != nil {
         .          .     92:       return nil, err
         .          .     93:   }
         .          .     94:
         .          .     95:   req.Header.Set("User-Agent", userAgent)
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       30ms (flat, cum)  0.16% of Total
         .          .    158:
         .          .    159:   req.Header.Set("User-Agent", userAgent)
         .          .    160:   req.Header.Set("Content-Type", "application/json")
         .          .    161:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .    162:
         .       20ms    163:   res, err := http.DefaultClient.Do(req)
         .          .    164:   if err != nil {
         .          .    165:       return nil, err
         .          .    166:   }
         .          .    167:   defer res.Body.Close()
         .          .    168:
         .          .    169:   if res.StatusCode != http.StatusOK {
         .          .    170:       b, err := ioutil.ReadAll(res.Body)
         .          .    171:       if err != nil {
         .          .    172:           return nil, fmt.Errorf("failed to read res.Body and the status code of the response from shipment service was not 200: %v", err)
         .          .    173:       }
         .          .    174:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .    175:   }
         .          .    176:
         .          .    177:   ssr := &APIShipmentStatusRes{}
         .       10ms    178:   err = json.NewDecoder(res.Body).Decode(&ssr)
         .          .    179:   if err != nil {
         .          .    180:       return nil, err
         .          .    181:   }
         .          .    182:
         .          .    183:   return ssr, nil
ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go
         0      210ms (flat, cum)  1.15% of Total
         .          .    373:
         .          .    374:   return session
         .          .    375:}
         .          .    376:
         .          .    377:func getCSRFToken(r *http.Request) string {
         .      210ms    378:   session := getSession(r)
         .          .    379:
         .          .    380:   csrfToken, ok := session.Values["csrf_token"]
         .          .    381:   if !ok {
         .          .    382:       return ""
         .          .    383:   }
ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go
      10ms       10ms (flat, cum) 0.055% of Total
         .          .    478://         category.ParentCategoryName = parentCategory.CategoryName
         .          .    479://     }
         .          .    480://     return category, err
         .          .    481:// }
         .          .    482:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) {
      10ms       10ms    483:   return getCategoryByIDOnMemory(q, categoryID)
         .          .    484:}
         .          .    485:
         .          .    486:var categories = map[int]Category{
         .          .    487:   1:  {ID: 1, ParentID: 0, CategoryName: "ソファー", ParentCategoryName: "nan"},
         .          .    488:   2:  {ID: 2, ParentID: 1, CategoryName: "一人掛けソファー", ParentCategoryName: "ソファー"},
ROUTINE ======================== main.getConfigByName in /home/isucon/isucari/webapp/go/main.go
         0      2.34s (flat, cum) 12.76% of Total
         .          .    533:   return categories[categoryID], nil
         .          .    534:}
         .          .    535:
         .          .    536:func getConfigByName(name string) (string, error) {
         .          .    537:   config := Config{}
         .      2.34s    538:   err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name)
         .          .    539:   if err == sql.ErrNoRows {
         .          .    540:       return "", nil
         .          .    541:   }
         .          .    542:   if err != nil {
         .          .    543:       log.Print(err)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.44% of Total
         .          .   2469:       Error string `json:"error"`
         .          .   2470:   }{Error: msg})
         .          .   2471:}
         .          .   2472:
         .          .   2473:func getImageURL(imageName string) string {
         .       80ms   2474:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2475:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
         0      1.37s (flat, cum)  7.47% of Total
         .          .   1145:   if err != nil || itemID <= 0 {
         .          .   1146:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1147:       return
         .          .   1148:   }
         .          .   1149:
         .      360ms   1150:   user, errCode, errMsg := getUser(r)
         .          .   1151:   if errMsg != "" {
         .          .   1152:       outputErrorMsg(w, errCode, errMsg)
         .          .   1153:       return
         .          .   1154:   }
         .          .   1155:
         .          .   1156:   item := Item{}
         .      850ms   1157:   err = dbx.Get(&item, "SELECT * FROM items WHERE id = ?", itemID)
         .          .   1158:   if err == sql.ErrNoRows {
         .          .   1159:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1160:       return
         .          .   1161:   }
         .          .   1162:   if err != nil {
         .          .   1163:       log.Print(err)
         .          .   1164:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1165:       return
         .          .   1166:   }
         .          .   1167:
         .          .   1168:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1169:   if err != nil {
         .          .   1170:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1171:       return
         .          .   1172:   }
         .          .   1173:
         .       80ms   1174:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1175:   if err != nil {
         .          .   1176:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1177:       return
         .          .   1178:   }
         .          .   1179:
         .          .   1180:   itemDetail := ItemDetail{
         .          .   1181:       ID:       item.ID,
         .          .   1182:       SellerID: item.SellerID,
         .          .   1183:       Seller:   &seller,
         .          .   1184:       // BuyerID
         .          .   1185:       // Buyer
         .          .   1186:       Status:      item.Status,
         .          .   1187:       Name:        item.Name,
         .          .   1188:       Price:       item.Price,
         .          .   1189:       Description: item.Description,
         .       10ms   1190:       ImageURL:    getImageURL(item.ImageName),
         .          .   1191:       CategoryID:  item.CategoryID,
         .          .   1192:       // TransactionEvidenceID
         .          .   1193:       // TransactionEvidenceStatus
         .          .   1194:       // ShippingStatus
         .          .   1195:       Category:  &category,
         .          .   1196:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1197:   }
         .          .   1198:
         .          .   1199:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .          .   1200:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1201:       if err != nil {
         .          .   1202:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1203:           return
         .          .   1204:       }
         .          .   1205:       itemDetail.BuyerID = item.BuyerID
         .          .   1206:       itemDetail.Buyer = &buyer
         .          .   1207:
         .          .   1208:       transactionEvidence := TransactionEvidence{}
         .       10ms   1209:       err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1210:       if err != nil && err != sql.ErrNoRows {
         .          .   1211:           // It's able to ignore ErrNoRows
         .          .   1212:           log.Print(err)
         .          .   1213:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1214:           return
         .          .   1215:       }
         .          .   1216:
         .          .   1217:       if transactionEvidence.ID > 0 {
         .          .   1218:           shipping := Shipping{}
         .          .   1219:           err = dbx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1220:           if err == sql.ErrNoRows {
         .          .   1221:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1222:               return
         .          .   1223:           }
         .          .   1224:           if err != nil {
         .          .   1225:               log.Print(err)
         .          .   1226:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1227:               return
         .          .   1228:           }
         .          .   1229:
         .          .   1230:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1231:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1232:           itemDetail.ShippingStatus = shipping.Status
         .          .   1233:       }
         .          .   1234:   }
         .          .   1235:
         .          .   1236:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       60ms   1237:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1238:}
         .          .   1239:
         .          .   1240:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1241:   rie := reqItemEdit{}
         .          .   1242:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
      10ms      3.64s (flat, cum) 19.85% of Total
         .          .    734:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    735:       return
         .          .    736:   }
         .          .    737:
         .          .    738:   var categoryIDs []int
         .      830ms    739:   err = dbx.Select(&categoryIDs, "SELECT id FROM categories WHERE parent_id=?", rootCategory.ID)
         .          .    740:   if err != nil {
         .          .    741:       log.Print(err)
         .          .    742:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    743:       return
         .          .    744:   }
         .          .    745:
         .          .    746:   query := r.URL.Query()
         .          .    747:   itemIDStr := query.Get("item_id")
         .          .    748:   var itemID int64
         .          .    749:   if itemIDStr != "" {
         .          .    750:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    751:       if err != nil || itemID <= 0 {
         .          .    752:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    753:           return
         .          .    754:       }
         .          .    755:   }
         .          .    756:
         .          .    757:   createdAtStr := query.Get("created_at")
         .          .    758:   var createdAt int64
         .          .    759:   if createdAtStr != "" {
         .          .    760:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    761:       if err != nil || createdAt <= 0 {
         .          .    762:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    763:           return
         .          .    764:       }
         .          .    765:   }
         .          .    766:
         .          .    767:   var inQuery string
         .          .    768:   var inArgs []interface{}
         .          .    769:   if itemID > 0 && createdAt > 0 {
         .          .    770:       // paging
         .          .    771:       inQuery, inArgs, err = sqlx.In(
         .          .    772:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    773:           ItemStatusOnSale,
         .          .    774:           ItemStatusSoldOut,
         .          .    775:           categoryIDs,
         .          .    776:           time.Unix(createdAt, 0),
         .          .    777:           time.Unix(createdAt, 0),
         .          .    778:           itemID,
         .          .    779:           ItemsPerPage+1,
         .          .    780:       )
         .          .    781:       if err != nil {
         .          .    782:           log.Print(err)
         .          .    783:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    784:           return
         .          .    785:       }
         .          .    786:   } else {
         .          .    787:       // 1st page
         .          .    788:       inQuery, inArgs, err = sqlx.In(
         .          .    789:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    790:           ItemStatusOnSale,
         .          .    791:           ItemStatusSoldOut,
         .          .    792:           categoryIDs,
         .          .    793:           ItemsPerPage+1,
         .          .    794:       )
         .          .    795:       if err != nil {
         .          .    796:           log.Print(err)
         .          .    797:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    798:           return
         .          .    799:       }
         .          .    800:   }
         .          .    801:
         .          .    802:   items := []Item{}
         .      670ms    803:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    804:
         .          .    805:   if err != nil {
         .          .    806:       log.Print(err)
         .          .    807:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    808:       return
         .          .    809:   }
         .          .    810:
         .          .    811:   itemSimples := []ItemSimple{}
         .       10ms    812:   for _, item := range items {
         .      1.78s    813:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    814:       if err != nil {
         .          .    815:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    816:           return
         .          .    817:       }
         .       10ms    818:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    819:       if err != nil {
         .          .    820:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    821:           return
         .          .    822:       }
         .       20ms    823:       itemSimples = append(itemSimples, ItemSimple{
         .          .    824:           ID:         item.ID,
         .          .    825:           SellerID:   item.SellerID,
         .          .    826:           Seller:     &seller,
         .          .    827:           Status:     item.Status,
         .          .    828:           Name:       item.Name,
         .          .    829:           Price:      item.Price,
         .       70ms    830:           ImageURL:   getImageURL(item.ImageName),
         .          .    831:           CategoryID: item.CategoryID,
         .          .    832:           Category:   &category,
      10ms       10ms    833:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    834:       })
         .          .    835:   }
         .          .    836:
         .          .    837:   hasNext := false
         .          .    838:   if len(itemSimples) > ItemsPerPage {
         .          .    839:       hasNext = true
         .          .    840:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    841:   }
         .          .    842:
         .          .    843:   rni := resNewItems{
         .          .    844:       RootCategoryID:   rootCategory.ID,
         .          .    845:       RootCategoryName: rootCategory.CategoryName,
         .          .    846:       Items:            itemSimples,
         .          .    847:       HasNext:          hasNext,
         .          .    848:   }
         .          .    849:
         .          .    850:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      240ms    851:   json.NewEncoder(w).Encode(rni)
         .          .    852:
         .          .    853:}
         .          .    854:
         .          .    855:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    856:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      360ms (flat, cum)  1.96% of Total
         .          .    649:   }
         .          .    650:
         .          .    651:   items := []Item{}
         .          .    652:   if itemID > 0 && createdAt > 0 {
         .          .    653:       // paging
         .      180ms    654:       err := dbx.Select(&items,
         .          .    655:           "SELECT * FROM items WHERE status IN (?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    656:           ItemStatusOnSale,
         .          .    657:           ItemStatusSoldOut,
         .          .    658:           time.Unix(createdAt, 0),
         .          .    659:           time.Unix(createdAt, 0),
         .          .    660:           itemID,
         .          .    661:           ItemsPerPage+1,
         .          .    662:       )
         .          .    663:       if err != nil {
         .          .    664:           log.Print(err)
         .          .    665:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    666:           return
         .          .    667:       }
         .          .    668:   } else {
         .          .    669:       // 1st page
         .       30ms    670:       err := dbx.Select(&items,
         .          .    671:           "SELECT * FROM items WHERE status IN (?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    672:           ItemStatusOnSale,
         .          .    673:           ItemStatusSoldOut,
         .          .    674:           ItemsPerPage+1,
         .          .    675:       )
         .          .    676:       if err != nil {
         .          .    677:           log.Print(err)
         .          .    678:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    679:           return
         .          .    680:       }
         .          .    681:   }
         .          .    682:
         .          .    683:   itemSimples := []ItemSimple{}
         .          .    684:   for _, item := range items {
         .      120ms    685:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    686:       if err != nil {
         .          .    687:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    688:           return
         .          .    689:       }
         .       10ms    690:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    691:       if err != nil {
         .          .    692:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    693:           return
         .          .    694:       }
         .          .    695:       itemSimples = append(itemSimples, ItemSimple{
         .          .    696:           ID:         item.ID,
         .          .    697:           SellerID:   item.SellerID,
         .          .    698:           Seller:     &seller,
         .          .    699:           Status:     item.Status,
         .          .    700:           Name:       item.Name,
         .          .    701:           Price:      item.Price,
         .          .    702:           ImageURL:   getImageURL(item.ImageName),
         .          .    703:           CategoryID: item.CategoryID,
         .          .    704:           Category:   &category,
         .          .    705:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    706:       })
         .          .    707:   }
         .          .    708:
         .          .    709:   hasNext := false
         .          .    710:   if len(itemSimples) > ItemsPerPage {
         .          .    711:       hasNext = true
         .          .    712:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    713:   }
         .          .    714:
         .          .    715:   rni := resNewItems{
         .          .    716:       Items:   itemSimples,
         .          .    717:       HasNext: hasNext,
         .          .    718:   }
         .          .    719:
         .          .    720:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms    721:   json.NewEncoder(w).Encode(rni)
         .          .    722:}
         .          .    723:
         .          .    724:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    725:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    726:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getPaymentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0      1.01s (flat, cum)  5.51% of Total
         .          .    545:   }
         .          .    546:   return config.Val, err
         .          .    547:}
         .          .    548:
         .          .    549:func getPaymentServiceURL() string {
         .      1.01s    550:   val, _ := getConfigByName("payment_service_url")
         .          .    551:   if val == "" {
         .          .    552:       return DefaultPaymentServiceURL
         .          .    553:   }
         .          .    554:   return val
         .          .    555:}
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      570ms (flat, cum)  3.11% of Total
         .          .    367:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    368:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    369:}
         .          .    370:
         .          .    371:func getSession(r *http.Request) *sessions.Session {
         .      570ms    372:   session, _ := store.Get(r, sessionName)
         .          .    373:
         .          .    374:   return session
         .          .    375:}
         .          .    376:
         .          .    377:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0      1.16s (flat, cum)  6.32% of Total
         .          .   2258:       ItemUpdatedAt: targetItem.UpdatedAt.Unix(),
         .          .   2259:   })
         .          .   2260:}
         .          .   2261:
         .          .   2262:func getSettings(w http.ResponseWriter, r *http.Request) {
         .       90ms   2263:   csrfToken := getCSRFToken(r)
         .          .   2264:
         .          .   2265:   user, _, errMsg := getUser(r)
         .          .   2266:
         .          .   2267:   ress := resSetting{}
         .          .   2268:   ress.CSRFToken = csrfToken
         .          .   2269:   if errMsg == "" {
         .          .   2270:       ress.User = &user
         .          .   2271:   }
         .          .   2272:
         .      960ms   2273:   ress.PaymentServiceURL = getPaymentServiceURL()
         .          .   2274:
         .          .   2275:   categories := []Category{}
         .          .   2276:
         .       60ms   2277:   err := dbx.Select(&categories, "SELECT * FROM categories")
         .          .   2278:   if err != nil {
         .          .   2279:       log.Print(err)
         .          .   2280:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2281:       return
         .          .   2282:   }
         .          .   2283:   ress.Categories = categories
         .          .   2284:
         .          .   2285:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       50ms   2286:   json.NewEncoder(w).Encode(ress)
         .          .   2287:}
         .          .   2288:
         .          .   2289:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2290:   rl := reqLogin{}
         .          .   2291:   err := json.NewDecoder(r.Body).Decode(&rl)
ROUTINE ======================== main.getShipmentServiceURL in /home/isucon/isucari/webapp/go/main.go
         0      1.33s (flat, cum)  7.25% of Total
         .          .    553:   }
         .          .    554:   return val
         .          .    555:}
         .          .    556:
         .          .    557:func getShipmentServiceURL() string {
         .      1.33s    558:   val, _ := getConfigByName("shipment_service_url")
         .          .    559:   if val == "" {
         .          .    560:       return DefaultShipmentServiceURL
         .          .    561:   }
         .          .    562:   return val
         .          .    563:}
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      1.46s (flat, cum)  7.96% of Total
         .          .    988:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    989:           return
         .          .    990:       }
         .          .    991:   }
         .          .    992:
         .       50ms    993:   tx := dbx.MustBegin()
         .          .    994:   items := []Item{}
         .          .    995:   if itemID > 0 && createdAt > 0 {
         .          .    996:       // paging
         .       20ms    997:       err := tx.Select(&items,
         .          .    998:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    999:           user.ID,
         .          .   1000:           user.ID,
         .          .   1001:           ItemStatusOnSale,
         .          .   1002:           ItemStatusTrading,
         .          .   1003:           ItemStatusSoldOut,
         .          .   1004:           ItemStatusCancel,
         .          .   1005:           ItemStatusStop,
         .          .   1006:           time.Unix(createdAt, 0),
         .          .   1007:           time.Unix(createdAt, 0),
         .          .   1008:           itemID,
         .          .   1009:           TransactionsPerPage+1,
         .          .   1010:       )
         .          .   1011:       if err != nil {
         .          .   1012:           log.Print(err)
         .          .   1013:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1014:           tx.Rollback()
         .          .   1015:           return
         .          .   1016:       }
         .          .   1017:   } else {
         .          .   1018:       // 1st page
         .          .   1019:       err := tx.Select(&items,
         .          .   1020:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1021:           user.ID,
         .          .   1022:           user.ID,
         .          .   1023:           ItemStatusOnSale,
         .          .   1024:           ItemStatusTrading,
         .          .   1025:           ItemStatusSoldOut,
         .          .   1026:           ItemStatusCancel,
         .          .   1027:           ItemStatusStop,
         .          .   1028:           TransactionsPerPage+1,
         .          .   1029:       )
         .          .   1030:       if err != nil {
         .          .   1031:           log.Print(err)
         .          .   1032:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1033:           tx.Rollback()
         .          .   1034:           return
         .          .   1035:       }
         .          .   1036:   }
         .          .   1037:
         .          .   1038:   itemDetails := []ItemDetail{}
         .          .   1039:   for _, item := range items {
         .       40ms   1040:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .   1041:       if err != nil {
         .          .   1042:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1043:           tx.Rollback()
         .          .   1044:           return
         .          .   1045:       }
         .          .   1046:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .   1047:       if err != nil {
         .          .   1048:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1049:           tx.Rollback()
         .          .   1050:           return
         .          .   1051:       }
         .          .   1052:
         .          .   1053:       itemDetail := ItemDetail{
         .          .   1054:           ID:       item.ID,
         .          .   1055:           SellerID: item.SellerID,
         .          .   1056:           Seller:   &seller,
         .          .   1057:           // BuyerID
         .          .   1058:           // Buyer
         .          .   1059:           Status:      item.Status,
         .          .   1060:           Name:        item.Name,
         .          .   1061:           Price:       item.Price,
         .          .   1062:           Description: item.Description,
         .          .   1063:           ImageURL:    getImageURL(item.ImageName),
         .          .   1064:           CategoryID:  item.CategoryID,
         .          .   1065:           // TransactionEvidenceID
         .          .   1066:           // TransactionEvidenceStatus
         .          .   1067:           // ShippingStatus
         .          .   1068:           Category:  &category,
         .          .   1069:           CreatedAt: item.CreatedAt.Unix(),
         .          .   1070:       }
         .          .   1071:
         .          .   1072:       if item.BuyerID != 0 {
         .       30ms   1073:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .   1074:           if err != nil {
         .          .   1075:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1076:               tx.Rollback()
         .          .   1077:               return
         .          .   1078:           }
         .          .   1079:           itemDetail.BuyerID = item.BuyerID
         .          .   1080:           itemDetail.Buyer = &buyer
         .          .   1081:       }
         .          .   1082:
         .          .   1083:       transactionEvidence := TransactionEvidence{}
         .      150ms   1084:       err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1085:       if err != nil && err != sql.ErrNoRows {
         .          .   1086:           // It's able to ignore ErrNoRows
         .          .   1087:           log.Print(err)
         .          .   1088:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1089:           tx.Rollback()
         .          .   1090:           return
         .          .   1091:       }
         .          .   1092:
         .          .   1093:       if transactionEvidence.ID > 0 {
         .          .   1094:           shipping := Shipping{}
         .       10ms   1095:           err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1096:           if err == sql.ErrNoRows {
         .          .   1097:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1098:               tx.Rollback()
         .          .   1099:               return
         .          .   1100:           }
         .          .   1101:           if err != nil {
         .          .   1102:               log.Print(err)
         .          .   1103:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1104:               tx.Rollback()
         .          .   1105:               return
         .          .   1106:           }
         .      1.12s   1107:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1108:               ReserveID: shipping.ReserveID,
         .          .   1109:           })
         .          .   1110:           if err != nil {
         .          .   1111:               log.Print(err)
         .          .   1112:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1113:               tx.Rollback()
         .          .   1114:               return
         .          .   1115:           }
         .          .   1116:
         .          .   1117:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1118:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1119:           itemDetail.ShippingStatus = ssr.Status
         .          .   1120:       }
         .          .   1121:
         .          .   1122:       itemDetails = append(itemDetails, itemDetail)
         .          .   1123:   }
         .       20ms   1124:   tx.Commit()
         .          .   1125:
         .          .   1126:   hasNext := false
         .          .   1127:   if len(itemDetails) > TransactionsPerPage {
         .          .   1128:       hasNext = true
         .          .   1129:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1130:   }
         .          .   1131:
         .          .   1132:   rts := resTransactions{
         .          .   1133:       Items:   itemDetails,
         .          .   1134:       HasNext: hasNext,
         .          .   1135:   }
         .          .   1136:
         .          .   1137:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1138:   json.NewEncoder(w).Encode(rts)
         .          .   1139:
         .          .   1140:}
         .          .   1141:
         .          .   1142:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1143:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
         0      360ms (flat, cum)  1.96% of Total
         .          .    384:
         .          .    385:   return csrfToken.(string)
         .          .    386:}
         .          .    387:
         .          .    388:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      360ms    389:   session := getSession(r)
         .          .    390:   userSess, ok := session.Values["user"]
         .          .    391:   //userID, ok := session.Values["user_id"]
         .          .    392:   if !ok {
         .          .    393:       return user, http.StatusNotFound, "no session"
         .          .    394:   }
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
         0      170ms (flat, cum)  0.93% of Total
         .          .    888:   }
         .          .    889:
         .          .    890:   items := []Item{}
         .          .    891:   if itemID > 0 && createdAt > 0 {
         .          .    892:       // paging
         .      110ms    893:       err := dbx.Select(&items,
         .          .    894:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    895:           userSimple.ID,
         .          .    896:           ItemStatusOnSale,
         .          .    897:           ItemStatusTrading,
         .          .    898:           ItemStatusSoldOut,
         .          .    899:           time.Unix(createdAt, 0),
         .          .    900:           time.Unix(createdAt, 0),
         .          .    901:           itemID,
         .          .    902:           ItemsPerPage+1,
         .          .    903:       )
         .          .    904:       if err != nil {
         .          .    905:           log.Print(err)
         .          .    906:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    907:           return
         .          .    908:       }
         .          .    909:   } else {
         .          .    910:       // 1st page
         .       50ms    911:       err := dbx.Select(&items,
         .          .    912:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    913:           userSimple.ID,
         .          .    914:           ItemStatusOnSale,
         .          .    915:           ItemStatusTrading,
         .          .    916:           ItemStatusSoldOut,
         .          .    917:           ItemsPerPage+1,
         .          .    918:       )
         .          .    919:       if err != nil {
         .          .    920:           log.Print(err)
         .          .    921:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    922:           return
         .          .    923:       }
         .          .    924:   }
         .          .    925:
         .          .    926:   itemSimples := []ItemSimple{}
         .          .    927:   for _, item := range items {
         .          .    928:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    929:       if err != nil {
         .          .    930:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    931:           return
         .          .    932:       }
         .          .    933:       itemSimples = append(itemSimples, ItemSimple{
         .          .    934:           ID:         item.ID,
         .          .    935:           SellerID:   item.SellerID,
         .          .    936:           Seller:     &userSimple,
         .          .    937:           Status:     item.Status,
         .          .    938:           Name:       item.Name,
         .          .    939:           Price:      item.Price,
         .          .    940:           ImageURL:   getImageURL(item.ImageName),
         .          .    941:           CategoryID: item.CategoryID,
         .          .    942:           Category:   &category,
         .          .    943:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    944:       })
         .          .    945:   }
         .          .    946:
         .          .    947:   hasNext := false
         .          .    948:   if len(itemSimples) > ItemsPerPage {
         .          .    949:       hasNext = true
         .          .    950:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    951:   }
         .          .    952:
         .          .    953:   rui := resUserItems{
         .          .    954:       User:    &userSimple,
         .          .    955:       Items:   itemSimples,
         .          .    956:       HasNext: hasNext,
         .          .    957:   }
         .          .    958:
         .          .    959:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms    960:   json.NewEncoder(w).Encode(rui)
         .          .    961:}
         .          .    962:
         .          .    963:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    964:
         .          .    965:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      2.03s (flat, cum) 11.07% of Total
         .          .    434:   }
         .          .    435:   return nil
         .          .    436:}
         .          .    437:
         .          .    438:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       20ms    439:   if us, ok := UserSimpleCache[userID]; ok {
         .      2.01s    440:       numsellStr, err := redisClient.Get(context.Background(), fmt.Sprintf(UserSimpleCacheKeyFmt, userID)).Result()
         .          .    441:       if err != nil {
         .          .    442:           return us, err
         .          .    443:       }
         .          .    444:       numsell, err := strconv.Atoi(numsellStr)
         .          .    445:       if err != nil {
ROUTINE ======================== main.main in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.11% of Total
         .          .    363:   mux.HandleFunc(pat.Get("/transactions/:transaction_id"), getIndex)
         .          .    364:   mux.HandleFunc(pat.Get("/users/:user_id"), getIndex)
         .          .    365:   mux.HandleFunc(pat.Get("/users/setting"), getIndex)
         .          .    366:   // Assets
         .          .    367:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .       20ms    368:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    369:}
         .          .    370:
         .          .    371:func getSession(r *http.Request) *sessions.Session {
         .          .    372:   session, _ := store.Get(r, sessionName)
         .          .    373:
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0      3.08s (flat, cum) 16.79% of Total
         .          .   1395:   if err != nil {
         .          .   1396:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1397:       return
         .          .   1398:   }
         .          .   1399:
         .       70ms   1400:   if rb.CSRFToken != getCSRFToken(r) {
         .          .   1401:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1402:
         .          .   1403:       return
         .          .   1404:   }
         .          .   1405:
         .          .   1406:   buyer, errCode, errMsg := getUser(r)
         .          .   1407:   if errMsg != "" {
         .          .   1408:       outputErrorMsg(w, errCode, errMsg)
         .          .   1409:       return
         .          .   1410:   }
         .          .   1411:
         .      2.77s   1412:   tx := dbx.MustBegin()
         .          .   1413:
         .          .   1414:   targetItem := Item{}
         .       20ms   1415:   err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", rb.ItemID)
         .          .   1416:   if err == sql.ErrNoRows {
         .          .   1417:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1418:       tx.Rollback()
         .          .   1419:       return
         .          .   1420:   }
         .          .   1421:   if err != nil {
         .          .   1422:       log.Print(err)
         .          .   1423:
         .          .   1424:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1425:       tx.Rollback()
         .          .   1426:       return
         .          .   1427:   }
         .          .   1428:
         .          .   1429:   if targetItem.Status != ItemStatusOnSale {
         .          .   1430:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .       40ms   1431:       tx.Rollback()
         .          .   1432:       return
         .          .   1433:   }
         .          .   1434:
         .          .   1435:   if targetItem.SellerID == buyer.ID {
         .          .   1436:       outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません")
         .          .   1437:       tx.Rollback()
         .          .   1438:       return
         .          .   1439:   }
         .          .   1440:
         .          .   1441:   seller := User{}
         .          .   1442:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", targetItem.SellerID)
         .          .   1443:   if err == sql.ErrNoRows {
         .          .   1444:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1445:       tx.Rollback()
         .          .   1446:       return
         .          .   1447:   }
         .          .   1448:   if err != nil {
         .          .   1449:       log.Print(err)
         .          .   1450:
         .          .   1451:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1452:       tx.Rollback()
         .          .   1453:       return
         .          .   1454:   }
         .          .   1455:
         .          .   1456:   category, err := getCategoryByID(tx, targetItem.CategoryID)
         .          .   1457:   if err != nil {
         .          .   1458:       log.Print(err)
         .          .   1459:
         .          .   1460:       outputErrorMsg(w, http.StatusInternalServerError, "category id error")
         .          .   1461:       tx.Rollback()
         .          .   1462:       return
         .          .   1463:   }
         .          .   1464:
         .          .   1465:   var transactionEvidenceID int64
         .          .   1466:   if err := tx.Get(
         .          .   1467:       &transactionEvidenceID,
         .          .   1468:       "INSERT INTO transaction_evidences (seller_id, buyer_id, status, item_id, item_name, item_price, item_description,item_category_id,item_root_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   1469:       targetItem.SellerID,
         .          .   1470:       buyer.ID,
         .          .   1471:       TransactionEvidenceStatusWaitShipping,
         .          .   1472:       targetItem.ID,
         .          .   1473:       targetItem.Name,
         .          .   1474:       targetItem.Price,
         .          .   1475:       targetItem.Description,
         .          .   1476:       category.ID,
         .          .   1477:       category.ParentID,
         .          .   1478:   ); err != nil {
         .          .   1479:       log.Print(err)
         .          .   1480:
         .          .   1481:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1482:       tx.Rollback()
         .          .   1483:       return
         .          .   1484:   }
         .          .   1485:
         .          .   1486:   _, err = tx.Exec("UPDATE items SET buyer_id = ?, status = ?, updated_at = ? WHERE id = ?",
         .          .   1487:       buyer.ID,
         .          .   1488:       ItemStatusTrading,
         .          .   1489:       time.Now(),
         .          .   1490:       targetItem.ID,
         .          .   1491:   )
         .          .   1492:   if err != nil {
         .          .   1493:       log.Print(err)
         .          .   1494:
         .          .   1495:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1496:       tx.Rollback()
         .          .   1497:       return
         .          .   1498:   }
         .          .   1499:
         .      120ms   1500:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1501:       ToAddress:   buyer.Address,
         .          .   1502:       ToName:      buyer.AccountName,
         .          .   1503:       FromAddress: seller.Address,
         .          .   1504:       FromName:    seller.AccountName,
         .          .   1505:   })
         .          .   1506:   if err != nil {
         .          .   1507:       log.Print(err)
         .          .   1508:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1509:       tx.Rollback()
         .          .   1510:
         .          .   1511:       return
         .          .   1512:   }
         .          .   1513:
         .       60ms   1514:   pstr, err := APIPaymentToken(getPaymentServiceURL(), &APIPaymentServiceTokenReq{
         .          .   1515:       ShopID: PaymentServiceIsucariShopID,
         .          .   1516:       Token:  rb.Token,
         .          .   1517:       APIKey: PaymentServiceIsucariAPIKey,
         .          .   1518:       Price:  targetItem.Price,
         .          .   1519:   })
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       50ms (flat, cum)  0.27% of Total
         .          .   1870:       outputErrorMsg(w, errCode, errMsg)
         .          .   1871:       return
         .          .   1872:   }
         .          .   1873:
         .          .   1874:   transactionEvidence := TransactionEvidence{}
         .       10ms   1875:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1876:   if err == sql.ErrNoRows {
         .          .   1877:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1878:       return
         .          .   1879:   }
         .          .   1880:   if err != nil {
         .          .   1881:       log.Print(err)
         .          .   1882:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1883:
         .          .   1884:       return
         .          .   1885:   }
         .          .   1886:
         .          .   1887:   if transactionEvidence.BuyerID != buyer.ID {
         .          .   1888:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1889:       return
         .          .   1890:   }
         .          .   1891:
         .          .   1892:   tx := dbx.MustBegin()
         .          .   1893:   item := Item{}
         .          .   1894:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1895:   if err == sql.ErrNoRows {
         .          .   1896:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1897:       tx.Rollback()
         .          .   1898:       return
         .          .   1899:   }
         .          .   1900:   if err != nil {
         .          .   1901:       log.Print(err)
         .          .   1902:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1903:       tx.Rollback()
         .          .   1904:       return
         .          .   1905:   }
         .          .   1906:
         .          .   1907:   if item.Status != ItemStatusTrading {
         .          .   1908:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1909:       tx.Rollback()
         .          .   1910:       return
         .          .   1911:   }
         .          .   1912:
         .          .   1913:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ? FOR UPDATE", itemID)
         .          .   1914:   if err == sql.ErrNoRows {
         .          .   1915:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1916:       tx.Rollback()
         .          .   1917:       return
         .          .   1918:   }
         .          .   1919:   if err != nil {
         .          .   1920:       log.Print(err)
         .          .   1921:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1922:       tx.Rollback()
         .          .   1923:       return
         .          .   1924:   }
         .          .   1925:
         .          .   1926:   if transactionEvidence.Status != TransactionEvidenceStatusWaitDone {
         .          .   1927:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1928:       tx.Rollback()
         .          .   1929:       return
         .          .   1930:   }
         .          .   1931:
         .          .   1932:   shipping := Shipping{}
         .          .   1933:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1934:   if err != nil {
         .          .   1935:       log.Print(err)
         .          .   1936:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1937:       tx.Rollback()
         .          .   1938:       return
         .          .   1939:   }
         .          .   1940:
         .       40ms   1941:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1942:       ReserveID: shipping.ReserveID,
         .          .   1943:   })
         .          .   1944:   if err != nil {
         .          .   1945:       log.Print(err)
         .          .   1946:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0      4.49s (flat, cum) 24.48% of Total
         .          .   2286:   json.NewEncoder(w).Encode(ress)
         .          .   2287:}
         .          .   2288:
         .          .   2289:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2290:   rl := reqLogin{}
         .       10ms   2291:   err := json.NewDecoder(r.Body).Decode(&rl)
         .          .   2292:   if err != nil {
         .          .   2293:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   2294:       return
         .          .   2295:   }
         .          .   2296:
         .          .   2297:   accountName := rl.AccountName
         .          .   2298:   password := rl.Password
         .          .   2299:
         .          .   2300:   if accountName == "" || password == "" {
         .          .   2301:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2302:
         .          .   2303:       return
         .          .   2304:   }
         .          .   2305:
         .          .   2306:   u := User{}
         .      2.52s   2307:   err = dbx.Get(&u, "SELECT * FROM users WHERE account_name = ?", accountName)
         .          .   2308:   if err == sql.ErrNoRows {
         .          .   2309:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2310:       return
         .          .   2311:   }
         .          .   2312:   if err != nil {
         .          .   2313:       log.Print(err)
         .          .   2314:
         .          .   2315:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2316:       return
         .          .   2317:   }
         .          .   2318:   hashedPassword := u.HashedPassword
         .          .   2319:   var rehashedPassword []byte
         .          .   2320:   var rehashed bool
         .       10ms   2321:   if err := dbx.Get(&rehashedPassword, "SELECT hashed_password FROM user_password WHERE user_id = ?", u.ID); err != nil && err != sql.ErrNoRows {
         .          .   2322:       log.Print(err)
         .          .   2323:
         .          .   2324:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2325:       return
         .          .   2326:   } else if err == nil {
         .          .   2327:       rehashed = true
         .          .   2328:       hashedPassword = rehashedPassword
         .          .   2329:   }
         .          .   2330:
         .      1.88s   2331:   err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
         .          .   2332:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2333:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2334:       return
         .          .   2335:   }
         .          .   2336:   if err != nil {
         .          .   2337:       log.Print(err)
         .          .   2338:
         .          .   2339:       outputErrorMsg(w, http.StatusInternalServerError, "crypt error")
         .          .   2340:       return
         .          .   2341:   }
         .          .   2342:
         .          .   2343:   if !rehashed {
         .          .   2344:       rehashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
         .          .   2345:       if err != nil {
         .          .   2346:           log.Print(err)
         .          .   2347:
         .          .   2348:           outputErrorMsg(w, http.StatusInternalServerError, "error")
         .          .   2349:           return
         .          .   2350:       }
         .          .   2351:       if _, err := dbx.Exec(
         .          .   2352:           "INSERT INTO user_password (user_id, hashed_password) VALUES (?, ?) ON CONFLICT (user_id) DO UPDATE SET hashed_password = ?",
         .          .   2353:           u.ID,
         .          .   2354:           rehashedPassword,
         .          .   2355:           rehashedPassword,
         .          .   2356:       ); err != nil {
         .          .   2357:           log.Print(err)
         .          .   2358:
         .          .   2359:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2360:           return
         .          .   2361:       }
         .          .   2362:   }
         .          .   2363:
         .          .   2364:   session := getSession(r)
         .          .   2365:
         .          .   2366:   session.Values["user_id"] = u.ID
         .          .   2367:   session.Values["user"] = u
         .          .   2368:   session.Values["csrf_token"] = secureRandomStr(20)
         .       60ms   2369:   if err = session.Save(r, w); err != nil {
         .          .   2370:       log.Print(err)
         .          .   2371:
         .          .   2372:       outputErrorMsg(w, http.StatusInternalServerError, "session error")
         .          .   2373:       return
         .          .   2374:   }
         .          .   2375:
         .          .   2376:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms   2377:   json.NewEncoder(w).Encode(u)
         .          .   2378:}
         .          .   2379:
         .          .   2380:func postRegister(w http.ResponseWriter, r *http.Request) {
         .          .   2381:   rr := reqRegister{}
         .          .   2382:   err := json.NewDecoder(r.Body).Decode(&rr)
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0       90ms (flat, cum)  0.49% of Total
         .          .   1999:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   2000:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   2001:}
         .          .   2002:
         .          .   2003:func postSell(w http.ResponseWriter, r *http.Request) {
         .       20ms   2004:   csrfToken := r.FormValue("csrf_token")
         .          .   2005:   name := r.FormValue("name")
         .          .   2006:   description := r.FormValue("description")
         .          .   2007:   priceStr := r.FormValue("price")
         .          .   2008:   categoryIDStr := r.FormValue("category_id")
         .          .   2009:
         .          .   2010:   f, header, err := r.FormFile("image")
         .          .   2011:   if err != nil {
         .          .   2012:       log.Print(err)
         .          .   2013:       outputErrorMsg(w, http.StatusBadRequest, "image error")
         .          .   2014:       return
         .          .   2015:   }
         .          .   2016:   defer f.Close()
         .          .   2017:
         .       10ms   2018:   if csrfToken != getCSRFToken(r) {
         .          .   2019:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   2020:       return
         .          .   2021:   }
         .          .   2022:
         .          .   2023:   categoryID, err := strconv.Atoi(categoryIDStr)
         .          .   2024:   if err != nil || categoryID < 0 {
         .          .   2025:       outputErrorMsg(w, http.StatusBadRequest, "category id error")
         .          .   2026:       return
         .          .   2027:   }
         .          .   2028:
         .          .   2029:   price, err := strconv.Atoi(priceStr)
         .          .   2030:   if err != nil {
         .          .   2031:       outputErrorMsg(w, http.StatusBadRequest, "price error")
         .          .   2032:       return
         .          .   2033:   }
         .          .   2034:
         .          .   2035:   if name == "" || description == "" || price == 0 || categoryID == 0 {
         .          .   2036:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2037:
         .          .   2038:       return
         .          .   2039:   }
         .          .   2040:
         .          .   2041:   if price < ItemMinPrice || price > ItemMaxPrice {
         .          .   2042:       outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg)
         .          .   2043:
         .          .   2044:       return
         .          .   2045:   }
         .          .   2046:
         .          .   2047:   category, err := getCategoryByID(dbx, categoryID)
         .          .   2048:   if err != nil || category.ParentID == 0 {
         .          .   2049:       log.Print(categoryID, category)
         .          .   2050:       outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID")
         .          .   2051:       return
         .          .   2052:   }
         .          .   2053:
         .          .   2054:   user, errCode, errMsg := getUser(r)
         .          .   2055:   if errMsg != "" {
         .          .   2056:       outputErrorMsg(w, errCode, errMsg)
         .          .   2057:       return
         .          .   2058:   }
         .          .   2059:
         .       10ms   2060:   img, err := ioutil.ReadAll(f)
         .          .   2061:   if err != nil {
         .          .   2062:       log.Print(err)
         .          .   2063:       outputErrorMsg(w, http.StatusInternalServerError, "image error")
         .          .   2064:       return
         .          .   2065:   }
         .          .   2066:
         .          .   2067:   ext := filepath.Ext(header.Filename)
         .          .   2068:
         .          .   2069:   if !(ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif") {
         .          .   2070:       outputErrorMsg(w, http.StatusBadRequest, "unsupported image format error")
         .          .   2071:       return
         .          .   2072:   }
         .          .   2073:
         .          .   2074:   if ext == ".jpeg" {
         .          .   2075:       ext = ".jpg"
         .          .   2076:   }
         .          .   2077:
         .          .   2078:   imgName := fmt.Sprintf("%s%s", secureRandomStr(16), ext)
         .       20ms   2079:   err = ioutil.WriteFile(fmt.Sprintf("../public/upload/%s", imgName), img, 0644)
         .          .   2080:   if err != nil {
         .          .   2081:       log.Print(err)
         .          .   2082:       outputErrorMsg(w, http.StatusInternalServerError, "Saving image failed")
         .          .   2083:       return
         .          .   2084:   }
         .          .   2085:
         .       30ms   2086:   tx := dbx.MustBegin()
         .          .   2087:
         .          .   2088:   seller := User{}
         .          .   2089:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", user.ID)
         .          .   2090:   if err == sql.ErrNoRows {
         .          .   2091:       outputErrorMsg(w, http.StatusNotFound, "user not found")
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.33% of Total
         .          .   1580:   }
         .          .   1581:
         .          .   1582:   csrfToken := reqps.CSRFToken
         .          .   1583:   itemID := reqps.ItemID
         .          .   1584:
         .       10ms   1585:   if csrfToken != getCSRFToken(r) {
         .          .   1586:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1587:
         .          .   1588:       return
         .          .   1589:   }
         .          .   1590:
         .          .   1591:   seller, errCode, errMsg := getUser(r)
         .          .   1592:   if errMsg != "" {
         .          .   1593:       outputErrorMsg(w, errCode, errMsg)
         .          .   1594:       return
         .          .   1595:   }
         .          .   1596:
         .          .   1597:   transactionEvidence := TransactionEvidence{}
         .          .   1598:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1599:   if err == sql.ErrNoRows {
         .          .   1600:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1601:       return
         .          .   1602:   }
         .          .   1603:   if err != nil {
         .          .   1604:       log.Print(err)
         .          .   1605:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1606:
         .          .   1607:       return
         .          .   1608:   }
         .          .   1609:
         .          .   1610:   if transactionEvidence.SellerID != seller.ID {
         .          .   1611:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1612:       return
         .          .   1613:   }
         .          .   1614:
         .          .   1615:   tx := dbx.MustBegin()
         .          .   1616:
         .          .   1617:   item := Item{}
         .          .   1618:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1619:   if err == sql.ErrNoRows {
         .          .   1620:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1621:       tx.Rollback()
         .          .   1622:       return
         .          .   1623:   }
         .          .   1624:   if err != nil {
         .          .   1625:       log.Print(err)
         .          .   1626:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1627:       tx.Rollback()
         .          .   1628:       return
         .          .   1629:   }
         .          .   1630:
         .          .   1631:   if item.Status != ItemStatusTrading {
         .          .   1632:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1633:       tx.Rollback()
         .          .   1634:       return
         .          .   1635:   }
         .          .   1636:
         .          .   1637:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1638:   if err == sql.ErrNoRows {
         .          .   1639:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1640:       tx.Rollback()
         .          .   1641:       return
         .          .   1642:   }
         .          .   1643:   if err != nil {
         .          .   1644:       log.Print(err)
         .          .   1645:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1646:       tx.Rollback()
         .          .   1647:       return
         .          .   1648:   }
         .          .   1649:
         .          .   1650:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1651:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1652:       tx.Rollback()
         .          .   1653:       return
         .          .   1654:   }
         .          .   1655:
         .          .   1656:   shipping := Shipping{}
         .       10ms   1657:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1658:   if err == sql.ErrNoRows {
         .          .   1659:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1660:       tx.Rollback()
         .          .   1661:       return
         .          .   1662:   }
         .          .   1663:   if err != nil {
         .          .   1664:       log.Print(err)
         .          .   1665:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1666:       tx.Rollback()
         .          .   1667:       return
         .          .   1668:   }
         .          .   1669:
         .       40ms   1670:   img, err := APIShipmentRequest(getShipmentServiceURL(), &APIShipmentRequestReq{
         .          .   1671:       ReserveID: shipping.ReserveID,
         .          .   1672:   })
         .          .   1673:   if err != nil {
         .          .   1674:       log.Print(err)
         .          .   1675:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go
         0      100ms (flat, cum)  0.55% of Total
         .          .   1711:   }
         .          .   1712:
         .          .   1713:   csrfToken := reqpsd.CSRFToken
         .          .   1714:   itemID := reqpsd.ItemID
         .          .   1715:
         .       30ms   1716:   if csrfToken != getCSRFToken(r) {
         .          .   1717:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1718:
         .          .   1719:       return
         .          .   1720:   }
         .          .   1721:
         .          .   1722:   seller, errCode, errMsg := getUser(r)
         .          .   1723:   if errMsg != "" {
         .          .   1724:       outputErrorMsg(w, errCode, errMsg)
         .          .   1725:       return
         .          .   1726:   }
         .          .   1727:
         .          .   1728:   transactionEvidence := TransactionEvidence{}
         .       10ms   1729:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1730:   if err == sql.ErrNoRows {
         .          .   1731:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1732:       return
         .          .   1733:   }
         .          .   1734:   if err != nil {
         .          .   1735:       log.Print(err)
         .          .   1736:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1737:
         .          .   1738:       return
         .          .   1739:   }
         .          .   1740:
         .          .   1741:   if transactionEvidence.SellerID != seller.ID {
         .          .   1742:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1743:       return
         .          .   1744:   }
         .          .   1745:
         .          .   1746:   tx := dbx.MustBegin()
         .          .   1747:
         .          .   1748:   item := Item{}
         .          .   1749:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1750:   if err == sql.ErrNoRows {
         .          .   1751:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1752:       tx.Rollback()
         .          .   1753:       return
         .          .   1754:   }
         .          .   1755:   if err != nil {
         .          .   1756:       log.Print(err)
         .          .   1757:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1758:       tx.Rollback()
         .          .   1759:       return
         .          .   1760:   }
         .          .   1761:
         .          .   1762:   if item.Status != ItemStatusTrading {
         .          .   1763:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1764:       tx.Rollback()
         .          .   1765:       return
         .          .   1766:   }
         .          .   1767:
         .          .   1768:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1769:   if err == sql.ErrNoRows {
         .          .   1770:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1771:       tx.Rollback()
         .          .   1772:       return
         .          .   1773:   }
         .          .   1774:   if err != nil {
         .          .   1775:       log.Print(err)
         .          .   1776:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1777:       tx.Rollback()
         .          .   1778:       return
         .          .   1779:   }
         .          .   1780:
         .          .   1781:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1782:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1783:       tx.Rollback()
         .          .   1784:       return
         .          .   1785:   }
         .          .   1786:
         .          .   1787:   shipping := Shipping{}
         .          .   1788:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1789:   if err == sql.ErrNoRows {
         .          .   1790:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1791:       tx.Rollback()
         .          .   1792:       return
         .          .   1793:   }
         .          .   1794:   if err != nil {
         .          .   1795:       log.Print(err)
         .          .   1796:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1797:       tx.Rollback()
         .          .   1798:       return
         .          .   1799:   }
         .          .   1800:
         .       50ms   1801:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1802:       ReserveID: shipping.ReserveID,
         .          .   1803:   })
         .          .   1804:   if err != nil {
         .          .   1805:       log.Print(err)
         .          .   1806:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1807:       tx.Rollback()
         .          .   1808:
         .          .   1809:       return
         .          .   1810:   }
         .          .   1811:
         .          .   1812:   if !(ssr.Status == ShippingsStatusShipping || ssr.Status == ShippingsStatusDone) {
         .          .   1813:       outputErrorMsg(w, http.StatusForbidden, "shipment service側で配送中か配送完了になっていません")
         .          .   1814:       tx.Rollback()
         .          .   1815:       return
         .          .   1816:   }
         .          .   1817:
         .       10ms   1818:   _, err = tx.Exec("UPDATE shippings SET status = ?, updated_at = ? WHERE transaction_evidence_id = ?",
         .          .   1819:       ssr.Status,
         .          .   1820:       time.Now(),
         .          .   1821:       transactionEvidence.ID,
         .          .   1822:   )
         .          .   1823:   if err != nil {
soudai commented 1 year ago
pid granted datname locktype relation relation transactionid mode
8808 true null virtualxid null null null ExclusiveLock
8808 true isucari relation 16595 shippings_pkey null AccessShareLock
8808 true isucari relation 16578 transaction_evidences null AccessShareLock
8808 true isucari relation 17037 items_status_created_at_index null AccessShareLock
8808 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
8808 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
8808 true isucari relation 16577 items_category_id_index null AccessShareLock
8808 true isucari relation 16575 items_pk null AccessShareLock
8808 true isucari relation 16567 items null AccessShareLock
8808 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
8808 true isucari relation 16588 shippings null AccessShareLock
9085 true isucari relation 16575 items_pk null AccessShareLock
9085 true isucari relation 16577 items_category_id_index null AccessShareLock
9085 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9085 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9085 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9085 true isucari relation 16578 transaction_evidences null AccessShareLock
9085 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9085 true isucari relation 16588 shippings null AccessShareLock
9085 true isucari relation 16595 shippings_pkey null AccessShareLock
9085 true null virtualxid null null null ExclusiveLock
9085 true isucari relation 16567 items null AccessShareLock
9117 true isucari relation 17036 items_seller_id_created_at_index null RowExclusiveLock
9117 true isucari relation 17036 items_seller_id_created_at_index null RowShareLock
9117 true null transactionid null null 20150 ExclusiveLock
9117 true isucari relation 17037 items_status_created_at_index null RowExclusiveLock
9117 true isucari relation 17037 items_status_created_at_index null RowShareLock
9117 true isucari relation 16578 transaction_evidences null RowExclusiveLock
9117 true isucari relation 16867 transaction_evidences_id_seq null RowExclusiveLock
9117 true isucari relation 16577 items_category_id_index null RowShareLock
9117 true isucari relation 16577 items_category_id_index null RowExclusiveLock
9117 true isucari relation 16575 items_pk null RowShareLock
9117 true isucari relation 16575 items_pk null RowExclusiveLock
9117 true isucari relation 16567 items null RowShareLock
9117 true isucari relation 16567 items null RowExclusiveLock
9117 true null virtualxid null null null ExclusiveLock
9117 true isucari relation 17035 items_buyer_id_created_at_index null RowExclusiveLock
9117 true isucari relation 17035 items_buyer_id_created_at_index null RowShareLock
9159 true isucari relation 16588 shippings null AccessShareLock
9159 true isucari relation 16578 transaction_evidences null AccessShareLock
9159 true null virtualxid null null null ExclusiveLock
9159 true isucari relation 16567 items null AccessShareLock
9159 true isucari relation 16575 items_pk null AccessShareLock
9159 true isucari relation 16577 items_category_id_index null AccessShareLock
9159 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9159 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9159 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9159 true isucari relation 16595 shippings_pkey null AccessShareLock
9159 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9242 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9242 true isucari relation 16578 transaction_evidences null AccessShareLock
9242 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9242 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9242 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9242 true isucari relation 16577 items_category_id_index null AccessShareLock
9242 true isucari relation 16575 items_pk null AccessShareLock
9242 true isucari relation 16567 items null AccessShareLock
9242 true null virtualxid null null null ExclusiveLock
9242 true isucari relation 16595 shippings_pkey null AccessShareLock
9242 true isucari relation 16588 shippings null AccessShareLock
9244 true isucari relation 16575 items_pk null AccessShareLock
9244 true isucari relation 16567 items null AccessShareLock
9244 true isucari relation 16577 items_category_id_index null AccessShareLock
9244 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9244 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9244 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9244 true isucari relation 16578 transaction_evidences null AccessShareLock
9244 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9244 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9244 true isucari relation 16588 shippings null AccessShareLock
9244 true isucari relation 16595 shippings_pkey null AccessShareLock
9244 true null virtualxid null null null ExclusiveLock
9307 true isucari relation 16578 transaction_evidences null AccessShareLock
9307 true isucari relation 16595 shippings_pkey null AccessShareLock
9307 true null virtualxid null null null ExclusiveLock
9307 true isucari relation 16567 items null AccessShareLock
9307 true isucari relation 16575 items_pk null AccessShareLock
9307 true isucari relation 16577 items_category_id_index null AccessShareLock
9307 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9307 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9307 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9307 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9307 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9307 true isucari relation 16588 shippings null AccessShareLock
9350 true null virtualxid null null null ExclusiveLock
9350 true isucari relation 16567 items null AccessShareLock
9350 true isucari relation 16575 items_pk null AccessShareLock
9350 true isucari relation 16577 items_category_id_index null AccessShareLock
9350 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9350 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9350 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9350 true isucari relation 16578 transaction_evidences null AccessShareLock
9350 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9350 true isucari relation 16595 shippings_pkey null AccessShareLock
9350 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9350 true isucari relation 16588 shippings null AccessShareLock
9392 true null virtualxid null null null ExclusiveLock
9392 true isucari relation 16567 items null AccessShareLock
9392 true isucari relation 16575 items_pk null AccessShareLock
9392 true isucari relation 16577 items_category_id_index null AccessShareLock
9392 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9392 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9392 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9392 true isucari relation 16578 transaction_evidences null AccessShareLock
9392 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9392 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9392 true isucari relation 16588 shippings null AccessShareLock
9392 true isucari relation 16595 shippings_pkey null AccessShareLock
9400 true isucari relation 16567 items null AccessShareLock
9400 true isucari relation 16595 shippings_pkey null AccessShareLock
9400 true isucari relation 16588 shippings null AccessShareLock
9400 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9400 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9400 true isucari relation 16578 transaction_evidences null AccessShareLock
9400 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9400 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9400 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9400 true isucari relation 16577 items_category_id_index null AccessShareLock
9400 true isucari relation 16575 items_pk null AccessShareLock
9400 true null virtualxid null null null ExclusiveLock
9420 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9420 true isucari relation 16595 shippings_pkey null AccessShareLock
9420 true isucari relation 16588 shippings null AccessShareLock
9420 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9420 true isucari relation 16578 transaction_evidences null AccessShareLock
9420 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9420 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9420 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9420 true isucari relation 16577 items_category_id_index null AccessShareLock
9420 true isucari relation 16575 items_pk null AccessShareLock
9420 true isucari relation 16567 items null AccessShareLock
9420 true null virtualxid null null null ExclusiveLock
9437 true isucari relation 16595 shippings_pkey null RowShareLock
9437 true null virtualxid null null null ExclusiveLock
9437 true isucari relation 16567 items null RowShareLock
9437 true isucari relation 16575 items_pk null RowShareLock
9437 true isucari relation 16577 items_category_id_index null RowShareLock
9437 true isucari relation 17035 items_buyer_id_created_at_index null RowShareLock
9437 true isucari relation 17036 items_seller_id_created_at_index null RowShareLock
9437 true isucari relation 17037 items_status_created_at_index null RowShareLock
9437 true isucari relation 16578 transaction_evidences null RowShareLock
9437 true isucari relation 16585 transaction_evidences_pkey null RowShareLock
9437 true isucari relation 16587 transaction_evidences_item_id_uindex null RowShareLock
9437 true isucari relation 16588 shippings null RowShareLock
9437 true null transactionid null null 20151 ExclusiveLock
9443 true isucari relation 16575 items_pk null AccessShareLock
9443 true isucari relation 16577 items_category_id_index null AccessShareLock
9443 true isucari relation 17035 items_buyer_id_created_at_index null AccessShareLock
9443 true isucari relation 17036 items_seller_id_created_at_index null AccessShareLock
9443 true isucari relation 17037 items_status_created_at_index null AccessShareLock
9443 true isucari relation 16578 transaction_evidences null AccessShareLock
9443 true isucari relation 16588 shippings null AccessShareLock
9443 true isucari relation 16585 transaction_evidences_pkey null AccessShareLock
9443 true isucari relation 16587 transaction_evidences_item_id_uindex null AccessShareLock
9443 true isucari relation 16595 shippings_pkey null AccessShareLock
9443 true null virtualxid null null null ExclusiveLock
9443 true isucari relation 16567 items null AccessShareLock
9482 true isucari relation 16585 transaction_evidences_pkey null RowShareLock
9482 true isucari relation 16587 transaction_evidences_item_id_uindex null RowShareLock
9482 true isucari relation 16578 transaction_evidences null RowShareLock
9482 true isucari relation 17037 items_status_created_at_index null RowShareLock
9482 true isucari relation 17036 items_seller_id_created_at_index null RowShareLock
9482 true isucari relation 17035 items_buyer_id_created_at_index null RowShareLock
9482 true isucari relation 16577 items_category_id_index null RowShareLock
9482 true isucari relation 16575 items_pk null RowShareLock
9482 true isucari relation 16567 items null RowShareLock
9482 true null virtualxid null null null ExclusiveLock
9482 true null transactionid null null 20147 ExclusiveLock
9482 true isucari relation 16595 shippings_pkey null RowShareLock
9482 true isucari relation 16588 shippings null RowShareLock
9490 true isucari relation 16577 items_category_id_index null RowShareLock
9490 true isucari relation 17035 items_buyer_id_created_at_index null RowShareLock
9490 true isucari relation 17036 items_seller_id_created_at_index null RowShareLock
9490 true isucari relation 17037 items_status_created_at_index null RowShareLock
9490 true isucari relation 16578 transaction_evidences null RowShareLock
9490 true isucari relation 16585 transaction_evidences_pkey null RowShareLock
9490 true isucari relation 16587 transaction_evidences_item_id_uindex null RowShareLock
9490 true isucari relation 16588 shippings null RowShareLock
9490 true isucari relation 16595 shippings_pkey null RowShareLock
9490 true null transactionid null null 20146 ExclusiveLock
9490 true null virtualxid null null null ExclusiveLock
9490 true isucari relation 16567 items null RowShareLock
9490 true isucari relation 16575 items_pk null RowShareLock
9504 true isucari relation 16587 transaction_evidences_item_id_uindex null RowShareLock
9504 true null transactionid null null 20149 ExclusiveLock
9504 true null virtualxid null null null ExclusiveLock
9504 true isucari relation 16567 items null RowShareLock
9504 true isucari relation 16575 items_pk null RowShareLock
9504 true isucari relation 16577 items_category_id_index null RowShareLock
9504 true isucari relation 17035 items_buyer_id_created_at_index null RowShareLock
9504 true isucari relation 17036 items_seller_id_created_at_index null RowShareLock
9504 true isucari relation 17037 items_status_created_at_index null RowShareLock
9504 true isucari relation 16578 transaction_evidences null RowShareLock
9504 true isucari relation 16585 transaction_evidences_pkey null RowShareLock
9504 true isucari relation 16595 shippings_pkey null RowShareLock
9504 true isucari relation 16588 shippings null RowShareLock
tetsuzawa commented 1 year ago

https://github.com/tetsuzawa/isucon9-qualify-suburi-20230114/pull/17

2023/01/14 07:51:11 main.go:212: 11790 0
{"pass":true,"score":11790,"campaign":1,"language":"Go","messages":["GET /new_items.json: リクエストに失敗しました(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1518)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2033)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2377)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2421)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2431)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2474)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2673)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2833)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3053)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3128)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3265)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3384)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3622)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3657)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 665)(タイムアウトしました)","POST /buy: リクエストに失敗しました (item_id: 51394)(タイムアウトしました)","POST /buy: リクエストに失敗しました (item_id: 51395)(タイムアウトしました)","POST /sell: リクエストに失敗しました(タイムアウトしました)","POST /ship: リクエストに失敗しました (item_id: 51393)(タイムアウトしました)"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
683 0 58 0 625 0 POST /buy 1.398 4.320 2245.929 3.288 4.288 0.000 49.000 33.245
276 0 260 0 16 0 GET /users/transactions.json 0.004 5.688 799.185 2.896 4.824 0.000 6937.000 5335.290
1965 0 1965 0 0 0 GET /new_items/\d+.json 0.004 0.452 119.538 0.061 0.220 5313.000 5714.000 5522.028
1558 0 1550 0 8 0 POST /login 0.004 0.304 52.503 0.034 0.096 73.000 109.000 96.914
73 0 52 0 21 0 POST /ship_done 0.804 0.844 44.408 0.608 0.824 29.000 83.000 38.507
70 0 55 0 15 0 POST /ship 0.674 0.848 42.781 0.611 0.824 0.000 61.000 54.529
50 0 50 0 0 0 POST /complete 0.800 0.872 38.004 0.760 0.824 34.000 34.000 34.000
206 0 206 0 0 0 GET /new_items.json 0.028 0.720 35.315 0.171 0.348 5598.000 5991.000 5786.485
5289 0 5289 0 0 0 GET /items/\d+.json 0.000 0.144 10.332 0.002 0.008 1060.000 1936.000 1219.960
87 0 66 0 21 0 POST /sell 0.004 0.284 2.128 0.024 0.152 13.000 106.000 29.253
417 0 417 0 0 0 GET /users/\d+.json 0.000 0.112 2.088 0.005 0.020 96.000 5283.000 3205.830
1 0 1 0 0 0 POST /initialize 2.080 2.080 2.080 2.080 2.080 31.000 31.000 31.000
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.180 1.612 0.028 0.124 51757.000 135848.000 86642.690
1550 0 1550 0 0 0 GET /settings 0.000 0.028 1.276 0.001 0.004 850.000 872.000 860.696
13 0 13 0 0 0 POST /bump 0.004 0.092 0.156 0.012 0.092 89.000 92.000 91.000
10 0 3 0 7 0 POST /items/edit 0.004 0.124 0.140 0.014 0.124 58.000 93.000 68.200
66 0 52 0 14 0 GET /transactions/\d+.png 0.000 0.016 0.100 0.002 0.004 33.000 633.000 495.030
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 35804.000 35804.000 35804.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.000 0.000 0.000 0.000 0.000 17072.000 17072.000 17072.000
tetsuzawa commented 1 year ago
{"pass":true,"score":13700,"campaign":3,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
702 0 79 0 623 0 POST /buy 1.612 4.296 2022.148 2.881 4.264 29.000 49.000 33.385
429 0 408 0 21 0 GET /users/transactions.json 0.004 6.420 1239.890 2.890 4.840 0.000 7159.000 5410.669
2621 0 2616 0 5 0 GET /new_items/\d+.json 0.001 0.820 244.494 0.093 0.312 0.000 5692.000 5514.153
1571 0 1563 0 8 0 POST /login 0.000 0.456 85.228 0.054 0.160 73.000 110.000 97.008
337 0 337 0 0 0 GET /new_items.json 0.036 1.156 78.792 0.234 0.632 5609.000 6007.000 5772.407
95 0 72 0 23 0 POST /ship_done 0.327 1.020 61.763 0.650 0.828 0.000 83.000 36.747
92 0 77 0 15 0 POST /ship 0.739 0.904 60.794 0.661 0.828 0.000 61.000 56.076
68 0 65 0 3 0 POST /complete 0.143 0.888 51.473 0.757 0.836 0.000 34.000 32.500
7198 0 7198 0 0 0 GET /items/\d+.json 0.000 0.516 29.927 0.004 0.012 1060.000 1945.000 1222.350
555 0 555 0 0 0 GET /users/\d+.json 0.000 0.204 7.068 0.013 0.068 95.000 5320.000 3109.652
114 0 93 0 21 0 POST /sell 0.032 0.316 5.268 0.046 0.268 13.000 106.000 25.404
1 0 1 0 0 0 POST /initialize 2.080 2.080 2.080 2.080 2.080 31.000 31.000 31.000
57 0 57 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.140 1.724 0.030 0.132 52559.000 130279.000 78650.825
1563 0 1563 0 0 0 GET /settings 0.004 0.032 1.580 0.001 0.004 851.000 874.000 860.750
13 0 13 0 0 0 POST /bump 0.004 0.088 0.276 0.021 0.088 90.000 91.000 90.846
88 0 74 0 14 0 GET /transactions/\d+.png 0.000 0.020 0.144 0.002 0.008 33.000 634.000 526.500
11 0 4 0 7 0 POST /items/edit 0.000 0.012 0.032 0.003 0.012 58.000 93.000 70.545
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.004 0.004 0.004 0.004 0.004 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.004 0.004 0.004 0.004 0.004 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
1 0 1 0 0 0 GET /reports.json 0.000 0.000 0.000 0.000 0.000 46198.000 46198.000 46198.000
tetsuzawa commented 1 year ago
$  go tool pprof -list main. -cum -seconds 60 http://localhost:8000/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)
Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.007.pb.gz
Total: 21.61s
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       70ms (flat, cum)  0.32% of Total
         .          .    158:
         .          .    159:   req.Header.Set("User-Agent", userAgent)
         .          .    160:   req.Header.Set("Content-Type", "application/json")
         .          .    161:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .    162:
         .       20ms    163:   res, err := http.DefaultClient.Do(req)
         .          .    164:   if err != nil {
         .          .    165:       return nil, err
         .          .    166:   }
         .          .    167:   defer res.Body.Close()
         .          .    168:
         .          .    169:   if res.StatusCode != http.StatusOK {
         .          .    170:       b, err := ioutil.ReadAll(res.Body)
         .          .    171:       if err != nil {
         .          .    172:           return nil, fmt.Errorf("failed to read res.Body and the status code of the response from shipment service was not 200: %v", err)
         .          .    173:       }
         .          .    174:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .    175:   }
         .          .    176:
         .          .    177:   ssr := &APIShipmentStatusRes{}
         .       40ms    178:   err = json.NewDecoder(res.Body).Decode(&ssr)
         .          .    179:   if err != nil {
         .          .    180:       return nil, err
         .          .    181:   }
         .          .    182:
         .       10ms    183:   return ssr, nil
         .          .    184:}
ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go
         0      120ms (flat, cum)  0.56% of Total
         .          .    375:
         .          .    376:   return session
         .          .    377:}
         .          .    378:
         .          .    379:func getCSRFToken(r *http.Request) string {
         .      120ms    380:   session := getSession(r)
         .          .    381:
         .          .    382:   csrfToken, ok := session.Values["csrf_token"]
         .          .    383:   if !ok {
         .          .    384:       return ""
         .          .    385:   }
ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go
      10ms       20ms (flat, cum) 0.093% of Total
         .          .    480://         category.ParentCategoryName = parentCategory.CategoryName
         .          .    481://     }
         .          .    482://     return category, err
         .          .    483:// }
         .          .    484:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) {
      10ms       20ms    485:   return getCategoryByIDOnMemory(q, categoryID)
         .          .    486:}
         .          .    487:
         .          .    488:var categories = []Category{
         .          .    489:   {ID: 1, ParentID: 0, CategoryName: "ソファー", ParentCategoryName: "nan"},
         .          .    490:   {ID: 2, ParentID: 1, CategoryName: "一人掛けソファー", ParentCategoryName: "ソファー"},
ROUTINE ======================== main.getCategoryByIDOnMemory in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.046% of Total
         .          .    546:       }
         .          .    547:   }
         .          .    548:}
         .          .    549:
         .          .    550:func getCategoryByIDOnMemory(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .       10ms    551:   return categoryMap[categoryID], nil
         .          .    552:}
         .          .    553:
         .          .    554:func getConfigByName(name string) (string, error) {
         .          .    555:   config := Config{}
         .          .    556:   err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum) 0.093% of Total
         .          .   2498:       Error string `json:"error"`
         .          .   2499:   }{Error: msg})
         .          .   2500:}
         .          .   2501:
         .          .   2502:func getImageURL(imageName string) string {
         .       20ms   2503:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2504:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
         0      1.79s (flat, cum)  8.28% of Total
         .          .   1169:   if err != nil || itemID <= 0 {
         .          .   1170:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1171:       return
         .          .   1172:   }
         .          .   1173:
         .      380ms   1174:   user, errCode, errMsg := getUser(r)
         .          .   1175:   if errMsg != "" {
         .          .   1176:       outputErrorMsg(w, errCode, errMsg)
         .          .   1177:       return
         .          .   1178:   }
         .          .   1179:
         .          .   1180:   item := Item{}
         .      1.07s   1181:   err = dbx.Get(&item, "SELECT * FROM items WHERE id = ?", itemID)
         .          .   1182:   if err == sql.ErrNoRows {
         .          .   1183:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1184:       return
         .          .   1185:   }
         .          .   1186:   if err != nil {
         .          .   1187:       log.Print(err)
         .          .   1188:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1189:       return
         .          .   1190:   }
         .          .   1191:
         .       20ms   1192:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1193:   if err != nil {
         .          .   1194:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1195:       return
         .          .   1196:   }
         .          .   1197:
         .      200ms   1198:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1199:   if err != nil {
         .          .   1200:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1201:       return
         .          .   1202:   }
         .          .   1203:
         .          .   1204:   itemDetail := ItemDetail{
         .          .   1205:       ID:       item.ID,
         .          .   1206:       SellerID: item.SellerID,
         .          .   1207:       Seller:   &seller,
         .          .   1208:       // BuyerID
         .          .   1209:       // Buyer
         .          .   1210:       Status:      item.Status,
         .          .   1211:       Name:        item.Name,
         .          .   1212:       Price:       item.Price,
         .          .   1213:       Description: item.Description,
         .          .   1214:       ImageURL:    getImageURL(item.ImageName),
         .          .   1215:       CategoryID:  item.CategoryID,
         .          .   1216:       // TransactionEvidenceID
         .          .   1217:       // TransactionEvidenceStatus
         .          .   1218:       // ShippingStatus
         .          .   1219:       Category:  &category,
         .          .   1220:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1221:   }
         .          .   1222:
         .          .   1223:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .          .   1224:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1225:       if err != nil {
         .          .   1226:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1227:           return
         .          .   1228:       }
         .          .   1229:       itemDetail.BuyerID = item.BuyerID
         .          .   1230:       itemDetail.Buyer = &buyer
         .          .   1231:
         .          .   1232:       transactionEvidence := TransactionEvidence{}
         .       10ms   1233:       err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1234:       if err != nil && err != sql.ErrNoRows {
         .          .   1235:           // It's able to ignore ErrNoRows
         .          .   1236:           log.Print(err)
         .          .   1237:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1238:           return
         .          .   1239:       }
         .          .   1240:
         .          .   1241:       if transactionEvidence.ID > 0 {
         .          .   1242:           shipping := Shipping{}
         .          .   1243:           err = dbx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1244:           if err == sql.ErrNoRows {
         .          .   1245:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1246:               return
         .          .   1247:           }
         .          .   1248:           if err != nil {
         .          .   1249:               log.Print(err)
         .          .   1250:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1251:               return
         .          .   1252:           }
         .          .   1253:
         .          .   1254:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1255:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1256:           itemDetail.ShippingStatus = shipping.Status
         .          .   1257:       }
         .          .   1258:   }
         .          .   1259:
         .          .   1260:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      110ms   1261:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1262:}
         .          .   1263:
         .          .   1264:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1265:   rie := reqItemEdit{}
         .          .   1266:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
      20ms      4.52s (flat, cum) 20.92% of Total
         .          .    750:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .    751:   json.NewEncoder(w).Encode(rni)
         .          .    752:}
         .          .    753:
         .          .    754:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .       10ms    755:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    756:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
         .          .    757:   if err != nil || rootCategoryID <= 0 {
         .          .    758:       outputErrorMsg(w, http.StatusBadRequest, "incorrect category id")
         .          .    759:       return
         .          .    760:   }
         .          .    761:
         .          .    762:   rootCategory, err := getCategoryByID(dbx, rootCategoryID)
         .          .    763:   if err != nil || rootCategory.ParentID != 0 {
         .          .    764:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    765:       return
         .          .    766:   }
         .          .    767:
         .          .    768:   categoryIDs := categoryIDsByParentIDMap[rootCategory.ID]
         .          .    769:
         .          .    770:   query := r.URL.Query()
         .          .    771:   itemIDStr := query.Get("item_id")
         .          .    772:   var itemID int64
         .          .    773:   if itemIDStr != "" {
         .          .    774:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    775:       if err != nil || itemID <= 0 {
         .          .    776:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    777:           return
         .          .    778:       }
         .          .    779:   }
         .          .    780:
         .          .    781:   createdAtStr := query.Get("created_at")
         .          .    782:   var createdAt int64
         .          .    783:   if createdAtStr != "" {
         .          .    784:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    785:       if err != nil || createdAt <= 0 {
         .          .    786:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    787:           return
         .          .    788:       }
         .          .    789:   }
         .          .    790:
         .          .    791:   var inQuery string
         .          .    792:   var inArgs []interface{}
         .          .    793:   if itemID > 0 && createdAt > 0 {
         .          .    794:       // paging
         .       30ms    795:       inQuery, inArgs, err = sqlx.In(
         .          .    796:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    797:           ItemStatusOnSale,
         .          .    798:           ItemStatusSoldOut,
         .          .    799:           categoryIDs,
         .          .    800:           time.Unix(createdAt, 0),
         .          .    801:           time.Unix(createdAt, 0),
         .          .    802:           itemID,
         .          .    803:           ItemsPerPage+1,
         .          .    804:       )
         .          .    805:       if err != nil {
         .          .    806:           log.Print(err)
         .          .    807:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    808:           return
         .          .    809:       }
         .          .    810:   } else {
         .          .    811:       // 1st page
         .          .    812:       inQuery, inArgs, err = sqlx.In(
         .          .    813:           "SELECT * FROM items WHERE status IN (?,?) AND category_id IN (?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    814:           ItemStatusOnSale,
         .          .    815:           ItemStatusSoldOut,
         .          .    816:           categoryIDs,
         .          .    817:           ItemsPerPage+1,
         .          .    818:       )
         .          .    819:       if err != nil {
         .          .    820:           log.Print(err)
         .          .    821:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    822:           return
         .          .    823:       }
         .          .    824:   }
         .          .    825:
         .          .    826:   items := []Item{}
         .      2.11s    827:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    828:
         .          .    829:   if err != nil {
         .          .    830:       log.Print(err)
         .          .    831:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    832:       return
         .          .    833:   }
         .          .    834:
         .          .    835:   itemSimples := []ItemSimple{}
      10ms       10ms    836:   for _, item := range items {
         .      1.95s    837:       seller, err := getUserSimpleByID(dbx, item.SellerID)
      10ms       10ms    838:       if err != nil {
         .          .    839:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    840:           return
         .          .    841:       }
         .       20ms    842:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    843:       if err != nil {
         .          .    844:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    845:           return
         .          .    846:       }
         .          .    847:       itemSimples = append(itemSimples, ItemSimple{
         .          .    848:           ID:         item.ID,
         .          .    849:           SellerID:   item.SellerID,
         .          .    850:           Seller:     &seller,
         .          .    851:           Status:     item.Status,
         .          .    852:           Name:       item.Name,
         .          .    853:           Price:      item.Price,
         .       20ms    854:           ImageURL:   getImageURL(item.ImageName),
         .          .    855:           CategoryID: item.CategoryID,
         .          .    856:           Category:   &category,
         .       10ms    857:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    858:       })
         .          .    859:   }
         .          .    860:
         .          .    861:   hasNext := false
         .          .    862:   if len(itemSimples) > ItemsPerPage {
         .          .    863:       hasNext = true
         .          .    864:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    865:   }
         .          .    866:
         .          .    867:   rni := resNewItems{
         .          .    868:       RootCategoryID:   rootCategory.ID,
         .          .    869:       RootCategoryName: rootCategory.CategoryName,
         .          .    870:       Items:            itemSimples,
         .          .    871:       HasNext:          hasNext,
         .          .    872:   }
         .          .    873:
         .          .    874:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      350ms    875:   json.NewEncoder(w).Encode(rni)
         .          .    876:
         .          .    877:}
         .          .    878:
         .          .    879:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    880:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      350ms (flat, cum)  1.62% of Total
         .          .    679:   }
         .          .    680:
         .          .    681:   items := []Item{}
         .          .    682:   if itemID > 0 && createdAt > 0 {
         .          .    683:       // paging
         .      120ms    684:       err := dbx.Select(&items,
         .          .    685:           "SELECT * FROM items WHERE status IN (?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    686:           ItemStatusOnSale,
         .          .    687:           ItemStatusSoldOut,
         .          .    688:           time.Unix(createdAt, 0),
         .       10ms    689:           time.Unix(createdAt, 0),
         .          .    690:           itemID,
         .          .    691:           ItemsPerPage+1,
         .          .    692:       )
         .          .    693:       if err != nil {
         .          .    694:           log.Print(err)
         .          .    695:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    696:           return
         .          .    697:       }
         .          .    698:   } else {
         .          .    699:       // 1st page
         .       30ms    700:       err := dbx.Select(&items,
         .          .    701:           "SELECT * FROM items WHERE status IN (?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    702:           ItemStatusOnSale,
         .          .    703:           ItemStatusSoldOut,
         .          .    704:           ItemsPerPage+1,
         .          .    705:       )
         .          .    706:       if err != nil {
         .          .    707:           log.Print(err)
         .          .    708:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    709:           return
         .          .    710:       }
         .          .    711:   }
         .          .    712:
         .          .    713:   itemSimples := []ItemSimple{}
         .          .    714:   for _, item := range items {
         .      150ms    715:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    716:       if err != nil {
         .          .    717:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    718:           return
         .          .    719:       }
         .          .    720:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    721:       if err != nil {
         .          .    722:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    723:           return
         .          .    724:       }
         .          .    725:       itemSimples = append(itemSimples, ItemSimple{
         .          .    726:           ID:         item.ID,
         .          .    727:           SellerID:   item.SellerID,
         .          .    728:           Seller:     &seller,
         .          .    729:           Status:     item.Status,
         .          .    730:           Name:       item.Name,
         .          .    731:           Price:      item.Price,
         .          .    732:           ImageURL:   getImageURL(item.ImageName),
         .          .    733:           CategoryID: item.CategoryID,
         .          .    734:           Category:   &category,
         .          .    735:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    736:       })
         .          .    737:   }
         .          .    738:
         .          .    739:   hasNext := false
         .          .    740:   if len(itemSimples) > ItemsPerPage {
         .          .    741:       hasNext = true
         .          .    742:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    743:   }
         .          .    744:
         .          .    745:   rni := resNewItems{
         .          .    746:       Items:   itemSimples,
         .          .    747:       HasNext: hasNext,
         .          .    748:   }
         .          .    749:
         .          .    750:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       40ms    751:   json.NewEncoder(w).Encode(rni)
         .          .    752:}
         .          .    753:
         .          .    754:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    755:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    756:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      490ms (flat, cum)  2.27% of Total
         .          .    369:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    370:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    371:}
         .          .    372:
         .          .    373:func getSession(r *http.Request) *sessions.Session {
         .      490ms    374:   session, _ := store.Get(r, sessionName)
         .          .    375:
         .          .    376:   return session
         .          .    377:}
         .          .    378:
         .          .    379:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0      100ms (flat, cum)  0.46% of Total
         .          .   2296:       ItemUpdatedAt: targetItem.UpdatedAt.Unix(),
         .          .   2297:   })
         .          .   2298:}
         .          .   2299:
         .          .   2300:func getSettings(w http.ResponseWriter, r *http.Request) {
         .       50ms   2301:   csrfToken := getCSRFToken(r)
         .          .   2302:
         .          .   2303:   user, _, errMsg := getUser(r)
         .          .   2304:
         .          .   2305:   ress := resSetting{}
         .          .   2306:   ress.CSRFToken = csrfToken
         .          .   2307:   if errMsg == "" {
         .          .   2308:       ress.User = &user
         .          .   2309:   }
         .          .   2310:
         .          .   2311:   ress.PaymentServiceURL = getPaymentServiceURL()
         .          .   2312:   ress.Categories = categories
         .          .   2313:
         .          .   2314:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       50ms   2315:   json.NewEncoder(w).Encode(ress)
         .          .   2316:}
         .          .   2317:
         .          .   2318:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2319:   rl := reqLogin{}
         .          .   2320:   err := json.NewDecoder(r.Body).Decode(&rl)
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      500ms (flat, cum)  2.31% of Total
         .          .   1012:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .   1013:           return
         .          .   1014:       }
         .          .   1015:   }
         .          .   1016:
         .       30ms   1017:   tx := dbx.MustBegin()
         .          .   1018:   items := []Item{}
         .          .   1019:   if itemID > 0 && createdAt > 0 {
         .          .   1020:       // paging
         .       30ms   1021:       err := tx.Select(&items,
         .          .   1022:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1023:           user.ID,
         .          .   1024:           user.ID,
         .          .   1025:           ItemStatusOnSale,
         .          .   1026:           ItemStatusTrading,
         .          .   1027:           ItemStatusSoldOut,
         .          .   1028:           ItemStatusCancel,
         .          .   1029:           ItemStatusStop,
         .          .   1030:           time.Unix(createdAt, 0),
         .          .   1031:           time.Unix(createdAt, 0),
         .          .   1032:           itemID,
         .          .   1033:           TransactionsPerPage+1,
         .          .   1034:       )
         .          .   1035:       if err != nil {
         .          .   1036:           log.Print(err)
         .          .   1037:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1038:           tx.Rollback()
         .          .   1039:           return
         .          .   1040:       }
         .          .   1041:   } else {
         .          .   1042:       // 1st page
         .       10ms   1043:       err := tx.Select(&items,
         .          .   1044:           "SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1045:           user.ID,
         .          .   1046:           user.ID,
         .          .   1047:           ItemStatusOnSale,
         .          .   1048:           ItemStatusTrading,
         .          .   1049:           ItemStatusSoldOut,
         .          .   1050:           ItemStatusCancel,
         .          .   1051:           ItemStatusStop,
         .          .   1052:           TransactionsPerPage+1,
         .          .   1053:       )
         .          .   1054:       if err != nil {
         .          .   1055:           log.Print(err)
         .          .   1056:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1057:           tx.Rollback()
         .          .   1058:           return
         .          .   1059:       }
         .          .   1060:   }
         .          .   1061:
         .          .   1062:   itemDetails := []ItemDetail{}
         .          .   1063:   for _, item := range items {
         .       70ms   1064:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .   1065:       if err != nil {
         .          .   1066:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1067:           tx.Rollback()
         .          .   1068:           return
         .          .   1069:       }
         .          .   1070:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .   1071:       if err != nil {
         .          .   1072:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1073:           tx.Rollback()
         .          .   1074:           return
         .          .   1075:       }
         .          .   1076:
         .          .   1077:       itemDetail := ItemDetail{
         .          .   1078:           ID:       item.ID,
         .          .   1079:           SellerID: item.SellerID,
         .          .   1080:           Seller:   &seller,
         .          .   1081:           // BuyerID
         .          .   1082:           // Buyer
         .          .   1083:           Status:      item.Status,
         .          .   1084:           Name:        item.Name,
         .          .   1085:           Price:       item.Price,
         .          .   1086:           Description: item.Description,
         .          .   1087:           ImageURL:    getImageURL(item.ImageName),
         .          .   1088:           CategoryID:  item.CategoryID,
         .          .   1089:           // TransactionEvidenceID
         .          .   1090:           // TransactionEvidenceStatus
         .          .   1091:           // ShippingStatus
         .          .   1092:           Category:  &category,
         .          .   1093:           CreatedAt: item.CreatedAt.Unix(),
         .          .   1094:       }
         .          .   1095:
         .          .   1096:       if item.BuyerID != 0 {
         .       20ms   1097:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .   1098:           if err != nil {
         .          .   1099:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1100:               tx.Rollback()
         .          .   1101:               return
         .          .   1102:           }
         .          .   1103:           itemDetail.BuyerID = item.BuyerID
         .          .   1104:           itemDetail.Buyer = &buyer
         .          .   1105:       }
         .          .   1106:
         .          .   1107:       transactionEvidence := TransactionEvidence{}
         .      140ms   1108:       err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1109:       if err != nil && err != sql.ErrNoRows {
         .          .   1110:           // It's able to ignore ErrNoRows
         .          .   1111:           log.Print(err)
         .          .   1112:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1113:           tx.Rollback()
         .          .   1114:           return
         .          .   1115:       }
         .          .   1116:
         .          .   1117:       if transactionEvidence.ID > 0 {
         .          .   1118:           shipping := Shipping{}
         .      130ms   1119:           err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1120:           if err == sql.ErrNoRows {
         .          .   1121:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1122:               tx.Rollback()
         .          .   1123:               return
         .          .   1124:           }
         .          .   1125:           if err != nil {
         .          .   1126:               log.Print(err)
         .          .   1127:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1128:               tx.Rollback()
         .          .   1129:               return
         .          .   1130:           }
         .       60ms   1131:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1132:               ReserveID: shipping.ReserveID,
         .          .   1133:           })
         .          .   1134:           if err != nil {
         .          .   1135:               log.Print(err)
         .          .   1136:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1137:               tx.Rollback()
         .          .   1138:               return
         .          .   1139:           }
         .          .   1140:
         .          .   1141:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1142:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1143:           itemDetail.ShippingStatus = ssr.Status
         .          .   1144:       }
         .          .   1145:
         .          .   1146:       itemDetails = append(itemDetails, itemDetail)
         .          .   1147:   }
         .          .   1148:   tx.Commit()
         .          .   1149:
         .          .   1150:   hasNext := false
         .          .   1151:   if len(itemDetails) > TransactionsPerPage {
         .          .   1152:       hasNext = true
         .          .   1153:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1154:   }
         .          .   1155:
         .          .   1156:   rts := resTransactions{
         .          .   1157:       Items:   itemDetails,
         .          .   1158:       HasNext: hasNext,
         .          .   1159:   }
         .          .   1160:
         .          .   1161:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms   1162:   json.NewEncoder(w).Encode(rts)
         .          .   1163:
         .          .   1164:}
         .          .   1165:
         .          .   1166:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1167:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
      10ms      380ms (flat, cum)  1.76% of Total
         .          .    385:   }
         .          .    386:
         .          .    387:   return csrfToken.(string)
         .          .    388:}
         .          .    389:
      10ms       10ms    390:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      360ms    391:   session := getSession(r)
         .       10ms    392:   userSess, ok := session.Values["user"]
         .          .    393:   //userID, ok := session.Values["user_id"]
         .          .    394:   if !ok {
         .          .    395:       return user, http.StatusNotFound, "no session"
         .          .    396:   }
         .          .    397:
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
         0      270ms (flat, cum)  1.25% of Total
         .          .    888:   if err != nil {
         .          .    889:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .    890:       return
         .          .    891:   }
         .          .    892:
         .       10ms    893:   query := r.URL.Query()
         .          .    894:   itemIDStr := query.Get("item_id")
         .          .    895:   var itemID int64
         .          .    896:   if itemIDStr != "" {
         .          .    897:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    898:       if err != nil || itemID <= 0 {
         .          .    899:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    900:           return
         .          .    901:       }
         .          .    902:   }
         .          .    903:
         .          .    904:   createdAtStr := query.Get("created_at")
         .          .    905:   var createdAt int64
         .          .    906:   if createdAtStr != "" {
         .          .    907:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    908:       if err != nil || createdAt <= 0 {
         .          .    909:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    910:           return
         .          .    911:       }
         .          .    912:   }
         .          .    913:
         .          .    914:   items := []Item{}
         .          .    915:   if itemID > 0 && createdAt > 0 {
         .          .    916:       // paging
         .      110ms    917:       err := dbx.Select(&items,
         .          .    918:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    919:           userSimple.ID,
         .          .    920:           ItemStatusOnSale,
         .          .    921:           ItemStatusTrading,
         .          .    922:           ItemStatusSoldOut,
         .          .    923:           time.Unix(createdAt, 0),
         .          .    924:           time.Unix(createdAt, 0),
         .          .    925:           itemID,
         .          .    926:           ItemsPerPage+1,
         .          .    927:       )
         .          .    928:       if err != nil {
         .          .    929:           log.Print(err)
         .          .    930:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    931:           return
         .          .    932:       }
         .          .    933:   } else {
         .          .    934:       // 1st page
         .      120ms    935:       err := dbx.Select(&items,
         .          .    936:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    937:           userSimple.ID,
         .          .    938:           ItemStatusOnSale,
         .          .    939:           ItemStatusTrading,
         .          .    940:           ItemStatusSoldOut,
         .          .    941:           ItemsPerPage+1,
         .          .    942:       )
         .          .    943:       if err != nil {
         .          .    944:           log.Print(err)
         .          .    945:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    946:           return
         .          .    947:       }
         .          .    948:   }
         .          .    949:
         .          .    950:   itemSimples := []ItemSimple{}
         .          .    951:   for _, item := range items {
         .          .    952:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    953:       if err != nil {
         .          .    954:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    955:           return
         .          .    956:       }
         .          .    957:       itemSimples = append(itemSimples, ItemSimple{
         .          .    958:           ID:         item.ID,
         .          .    959:           SellerID:   item.SellerID,
         .          .    960:           Seller:     &userSimple,
         .          .    961:           Status:     item.Status,
         .          .    962:           Name:       item.Name,
         .          .    963:           Price:      item.Price,
         .          .    964:           ImageURL:   getImageURL(item.ImageName),
         .          .    965:           CategoryID: item.CategoryID,
         .          .    966:           Category:   &category,
         .          .    967:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    968:       })
         .          .    969:   }
         .          .    970:
         .          .    971:   hasNext := false
         .          .    972:   if len(itemSimples) > ItemsPerPage {
         .          .    973:       hasNext = true
         .          .    974:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    975:   }
         .          .    976:
         .          .    977:   rui := resUserItems{
         .          .    978:       User:    &userSimple,
         .          .    979:       Items:   itemSimples,
         .          .    980:       HasNext: hasNext,
         .          .    981:   }
         .          .    982:
         .          .    983:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       30ms    984:   json.NewEncoder(w).Encode(rui)
         .          .    985:}
         .          .    986:
         .          .    987:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .    988:
         .          .    989:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      2.37s (flat, cum) 10.97% of Total
         .          .    436:   }
         .          .    437:   return nil
         .          .    438:}
         .          .    439:
         .          .    440:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       10ms    441:   if us, ok := UserSimpleCache[userID]; ok {
         .      2.36s    442:       numsellStr, err := redisClient.Get(context.Background(), fmt.Sprintf(UserSimpleCacheKeyFmt, userID)).Result()
         .          .    443:       if err != nil {
         .          .    444:           return us, err
         .          .    445:       }
         .          .    446:       numsell, err := strconv.Atoi(numsellStr)
         .          .    447:       if err != nil {
ROUTINE ======================== main.postBump in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.046% of Total
         .          .   2211:   if errMsg != "" {
         .          .   2212:       outputErrorMsg(w, errCode, errMsg)
         .          .   2213:       return
         .          .   2214:   }
         .          .   2215:
         .       10ms   2216:   tx := dbx.MustBegin()
         .          .   2217:
         .          .   2218:   targetItem := Item{}
         .          .   2219:   err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   2220:   if err == sql.ErrNoRows {
         .          .   2221:       outputErrorMsg(w, http.StatusNotFound, "item not found")
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0      5.23s (flat, cum) 24.20% of Total
         .          .   1413:}
         .          .   1414:
         .          .   1415:func postBuy(w http.ResponseWriter, r *http.Request) {
         .          .   1416:   rb := reqBuy{}
         .          .   1417:
         .       10ms   1418:   err := json.NewDecoder(r.Body).Decode(&rb)
         .          .   1419:   if err != nil {
         .          .   1420:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1421:       return
         .          .   1422:   }
         .          .   1423:
         .       70ms   1424:   if rb.CSRFToken != getCSRFToken(r) {
         .          .   1425:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1426:
         .          .   1427:       return
         .          .   1428:   }
         .          .   1429:
         .          .   1430:   buyer, errCode, errMsg := getUser(r)
         .          .   1431:   if errMsg != "" {
         .          .   1432:       outputErrorMsg(w, errCode, errMsg)
         .          .   1433:       return
         .          .   1434:   }
         .          .   1435:
         .          .   1436:   var targetItem Item
         .      2.56s   1437:   err = dbx.Get(&targetItem, "SELECT * FROM items WHERE id = ?", rb.ItemID)
         .          .   1438:   if err == sql.ErrNoRows {
         .          .   1439:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1440:       return
         .          .   1441:   }
         .          .   1442:   if err != nil {
         .          .   1443:       log.Print(err)
         .          .   1444:
         .          .   1445:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1446:       return
         .          .   1447:   }
         .          .   1448:   if targetItem.Status != ItemStatusOnSale {
         .          .   1449:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .          .   1450:       return
         .          .   1451:   }
         .          .   1452:
         .          .   1453:   seller := User{}
         .       30ms   1454:   err = dbx.Get(&seller, "SELECT * FROM users WHERE id = ?", targetItem.SellerID)
         .          .   1455:   if err == sql.ErrNoRows {
         .          .   1456:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1457:       return
         .          .   1458:   }
         .          .   1459:   if err != nil {
         .          .   1460:       log.Print(err)
         .          .   1461:
         .          .   1462:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1463:       return
         .          .   1464:   }
         .          .   1465:
         .          .   1466:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1467:       ToAddress:   buyer.Address,
         .          .   1468:       ToName:      buyer.AccountName,
         .          .   1469:       FromAddress: seller.Address,
         .          .   1470:       FromName:    seller.AccountName,
         .          .   1471:   })
         .          .   1472:   if err != nil {
         .          .   1473:       log.Print(err)
         .          .   1474:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1475:
         .          .   1476:       return
         .          .   1477:   }
         .          .   1478:
         .      2.52s   1479:   tx := dbx.MustBegin()
         .          .   1480:
         .          .   1481:   // get exclusive-lock
         .       20ms   1482:   err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", targetItem.ID)
         .          .   1483:   if err == sql.ErrNoRows {
         .          .   1484:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1485:       tx.Rollback()
         .          .   1486:       return
         .          .   1487:   }
         .          .   1488:   if err != nil {
         .          .   1489:       log.Print(err)
         .          .   1490:
         .          .   1491:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1492:       tx.Rollback()
         .          .   1493:       return
         .          .   1494:   }
         .          .   1495:
         .          .   1496:   if targetItem.Status != ItemStatusOnSale {
         .          .   1497:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .       10ms   1498:       tx.Rollback()
         .          .   1499:       return
         .          .   1500:   }
         .          .   1501:
         .          .   1502:   if targetItem.SellerID == buyer.ID {
         .          .   1503:       outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません")
         .          .   1504:       tx.Rollback()
         .          .   1505:       return
         .          .   1506:   }
         .          .   1507:
         .          .   1508:   category, err := getCategoryByID(tx, targetItem.CategoryID)
         .          .   1509:   if err != nil {
         .          .   1510:       log.Print(err)
         .          .   1511:
         .          .   1512:       outputErrorMsg(w, http.StatusInternalServerError, "category id error")
         .          .   1513:       tx.Rollback()
         .          .   1514:       return
         .          .   1515:   }
         .          .   1516:
         .          .   1517:   var transactionEvidenceID int64
         .          .   1518:   if err := tx.Get(
         .          .   1519:       &transactionEvidenceID,
         .          .   1520:       "INSERT INTO transaction_evidences (seller_id, buyer_id, status, item_id, item_name, item_price, item_description,item_category_id,item_root_category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   1521:       targetItem.SellerID,
         .          .   1522:       buyer.ID,
         .          .   1523:       TransactionEvidenceStatusWaitShipping,
         .          .   1524:       targetItem.ID,
         .          .   1525:       targetItem.Name,
         .          .   1526:       targetItem.Price,
         .          .   1527:       targetItem.Description,
         .          .   1528:       category.ID,
         .          .   1529:       category.ParentID,
         .          .   1530:   ); err != nil {
         .          .   1531:       log.Print(err)
         .          .   1532:
         .          .   1533:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1534:       tx.Rollback()
         .          .   1535:       return
         .          .   1536:   }
         .          .   1537:
         .          .   1538:   _, err = tx.Exec("UPDATE items SET buyer_id = ?, status = ?, updated_at = ? WHERE id = ?",
         .          .   1539:       buyer.ID,
         .          .   1540:       ItemStatusTrading,
         .          .   1541:       time.Now(),
         .          .   1542:       targetItem.ID,
         .          .   1543:   )
         .          .   1544:   if err != nil {
         .          .   1545:       log.Print(err)
         .          .   1546:
         .          .   1547:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1548:       tx.Rollback()
         .          .   1549:       return
         .          .   1550:   }
         .          .   1551:
         .          .   1552:   pstr, err := APIPaymentToken(getPaymentServiceURL(), &APIPaymentServiceTokenReq{
         .          .   1553:       ShopID: PaymentServiceIsucariShopID,
         .          .   1554:       Token:  rb.Token,
         .          .   1555:       APIKey: PaymentServiceIsucariAPIKey,
         .          .   1556:       Price:  targetItem.Price,
         .          .   1557:   })
         .          .   1558:   if err != nil {
         .          .   1559:       log.Print(err)
         .          .   1560:
         .          .   1561:       outputErrorMsg(w, http.StatusInternalServerError, "payment service is failed")
         .          .   1562:       tx.Rollback()
         .          .   1563:       return
         .          .   1564:   }
         .          .   1565:
         .          .   1566:   if pstr.Status == "invalid" {
         .          .   1567:       outputErrorMsg(w, http.StatusBadRequest, "カード情報に誤りがあります")
         .          .   1568:       tx.Rollback()
         .          .   1569:       return
         .          .   1570:   }
         .          .   1571:
         .          .   1572:   if pstr.Status == "fail" {
         .          .   1573:       outputErrorMsg(w, http.StatusBadRequest, "カードの残高が足りません")
         .          .   1574:       tx.Rollback()
         .          .   1575:       return
         .          .   1576:   }
         .          .   1577:
         .          .   1578:   if pstr.Status != "ok" {
         .          .   1579:       outputErrorMsg(w, http.StatusBadRequest, "想定外のエラー")
         .          .   1580:       tx.Rollback()
         .          .   1581:       return
         .          .   1582:   }
         .          .   1583:
         .          .   1584:   _, err = tx.Exec("INSERT INTO shippings (transaction_evidence_id, status, item_name, item_id, reserve_id, reserve_time, to_address, to_name, from_address, from_name, img_binary) VALUES (?,?,?,?,?,?,?,?,?,?,?)",
         .          .   1585:       transactionEvidenceID,
         .          .   1586:       ShippingsStatusInitial,
         .          .   1587:       targetItem.Name,
         .          .   1588:       targetItem.ID,
         .          .   1589:       scr.ReserveID,
         .          .   1590:       scr.ReserveTime,
         .          .   1591:       buyer.Address,
         .          .   1592:       buyer.AccountName,
         .          .   1593:       seller.Address,
         .          .   1594:       seller.AccountName,
         .          .   1595:       "",
         .          .   1596:   )
         .          .   1597:   if err != nil {
         .          .   1598:       log.Print(err)
         .          .   1599:
         .          .   1600:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1601:       tx.Rollback()
         .          .   1602:       return
         .          .   1603:   }
         .          .   1604:
         .       10ms   1605:   tx.Commit()
         .          .   1606:
         .          .   1607:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   1608:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidenceID})
         .          .   1609:}
         .          .   1610:
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       30ms (flat, cum)  0.14% of Total
         .          .   1908:       outputErrorMsg(w, errCode, errMsg)
         .          .   1909:       return
         .          .   1910:   }
         .          .   1911:
         .          .   1912:   transactionEvidence := TransactionEvidence{}
         .       10ms   1913:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1914:   if err == sql.ErrNoRows {
         .          .   1915:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1916:       return
         .          .   1917:   }
         .          .   1918:   if err != nil {
         .          .   1919:       log.Print(err)
         .          .   1920:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1921:
         .          .   1922:       return
         .          .   1923:   }
         .          .   1924:
         .          .   1925:   if transactionEvidence.BuyerID != buyer.ID {
         .          .   1926:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1927:       return
         .          .   1928:   }
         .          .   1929:
         .          .   1930:   tx := dbx.MustBegin()
         .          .   1931:   item := Item{}
         .          .   1932:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1933:   if err == sql.ErrNoRows {
         .          .   1934:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1935:       tx.Rollback()
         .          .   1936:       return
         .          .   1937:   }
         .          .   1938:   if err != nil {
         .          .   1939:       log.Print(err)
         .          .   1940:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1941:       tx.Rollback()
         .          .   1942:       return
         .          .   1943:   }
         .          .   1944:
         .          .   1945:   if item.Status != ItemStatusTrading {
         .          .   1946:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1947:       tx.Rollback()
         .          .   1948:       return
         .          .   1949:   }
         .          .   1950:
         .          .   1951:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ? FOR UPDATE", itemID)
         .          .   1952:   if err == sql.ErrNoRows {
         .          .   1953:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1954:       tx.Rollback()
         .          .   1955:       return
         .          .   1956:   }
         .          .   1957:   if err != nil {
         .          .   1958:       log.Print(err)
         .          .   1959:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1960:       tx.Rollback()
         .          .   1961:       return
         .          .   1962:   }
         .          .   1963:
         .          .   1964:   if transactionEvidence.Status != TransactionEvidenceStatusWaitDone {
         .          .   1965:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1966:       tx.Rollback()
         .          .   1967:       return
         .          .   1968:   }
         .          .   1969:
         .          .   1970:   shipping := Shipping{}
         .       10ms   1971:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1972:   if err != nil {
         .          .   1973:       log.Print(err)
         .          .   1974:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1975:       tx.Rollback()
         .          .   1976:       return
         .          .   1977:   }
         .          .   1978:
         .       10ms   1979:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1980:       ReserveID: shipping.ReserveID,
         .          .   1981:   })
         .          .   1982:   if err != nil {
         .          .   1983:       log.Print(err)
         .          .   1984:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0      5.60s (flat, cum) 25.91% of Total
         .          .   2315:   json.NewEncoder(w).Encode(ress)
         .          .   2316:}
         .          .   2317:
         .          .   2318:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2319:   rl := reqLogin{}
         .       10ms   2320:   err := json.NewDecoder(r.Body).Decode(&rl)
         .          .   2321:   if err != nil {
         .          .   2322:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   2323:       return
         .          .   2324:   }
         .          .   2325:
         .          .   2326:   accountName := rl.AccountName
         .          .   2327:   password := rl.Password
         .          .   2328:
         .          .   2329:   if accountName == "" || password == "" {
         .          .   2330:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2331:
         .          .   2332:       return
         .          .   2333:   }
         .          .   2334:
         .          .   2335:   u := User{}
         .      3.84s   2336:   err = dbx.Get(&u, "SELECT * FROM users WHERE account_name = ?", accountName)
         .          .   2337:   if err == sql.ErrNoRows {
         .          .   2338:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2339:       return
         .          .   2340:   }
         .          .   2341:   if err != nil {
         .          .   2342:       log.Print(err)
         .          .   2343:
         .          .   2344:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2345:       return
         .          .   2346:   }
         .          .   2347:   hashedPassword := u.HashedPassword
         .          .   2348:   var rehashedPassword []byte
         .          .   2349:   var rehashed bool
         .      100ms   2350:   if err := dbx.Get(&rehashedPassword, "SELECT hashed_password FROM user_password WHERE user_id = ?", u.ID); err != nil && err != sql.ErrNoRows {
         .          .   2351:       log.Print(err)
         .          .   2352:
         .          .   2353:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2354:       return
         .          .   2355:   } else if err == nil {
         .          .   2356:       rehashed = true
         .          .   2357:       hashedPassword = rehashedPassword
         .          .   2358:   }
         .          .   2359:
         .      1.60s   2360:   err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
         .          .   2361:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2362:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2363:       return
         .          .   2364:   }
         .          .   2365:   if err != nil {
         .          .   2366:       log.Print(err)
         .          .   2367:
         .          .   2368:       outputErrorMsg(w, http.StatusInternalServerError, "crypt error")
         .          .   2369:       return
         .          .   2370:   }
         .          .   2371:
         .          .   2372:   if !rehashed {
         .          .   2373:       rehashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
         .          .   2374:       if err != nil {
         .          .   2375:           log.Print(err)
         .          .   2376:
         .          .   2377:           outputErrorMsg(w, http.StatusInternalServerError, "error")
         .          .   2378:           return
         .          .   2379:       }
         .          .   2380:       if _, err := dbx.Exec(
         .          .   2381:           "INSERT INTO user_password (user_id, hashed_password) VALUES (?, ?) ON CONFLICT (user_id) DO UPDATE SET hashed_password = ?",
         .          .   2382:           u.ID,
         .          .   2383:           rehashedPassword,
         .          .   2384:           rehashedPassword,
         .          .   2385:       ); err != nil {
         .          .   2386:           log.Print(err)
         .          .   2387:
         .          .   2388:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2389:           return
         .          .   2390:       }
         .          .   2391:   }
         .          .   2392:
         .       10ms   2393:   session := getSession(r)
         .          .   2394:
         .          .   2395:   session.Values["user_id"] = u.ID
         .          .   2396:   session.Values["user"] = u
         .          .   2397:   session.Values["csrf_token"] = secureRandomStr(20)
         .       30ms   2398:   if err = session.Save(r, w); err != nil {
         .          .   2399:       log.Print(err)
         .          .   2400:
         .          .   2401:       outputErrorMsg(w, http.StatusInternalServerError, "session error")
         .          .   2402:       return
         .          .   2403:   }
         .          .   2404:
         .          .   2405:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       10ms   2406:   json.NewEncoder(w).Encode(u)
         .          .   2407:}
         .          .   2408:
         .          .   2409:func postRegister(w http.ResponseWriter, r *http.Request) {
         .          .   2410:   rr := reqRegister{}
         .          .   2411:   err := json.NewDecoder(r.Body).Decode(&rr)
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0      110ms (flat, cum)  0.51% of Total
         .          .   2037:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   2038:   json.NewEncoder(w).Encode(resBuy{TransactionEvidenceID: transactionEvidence.ID})
         .          .   2039:}
         .          .   2040:
         .          .   2041:func postSell(w http.ResponseWriter, r *http.Request) {
         .       20ms   2042:   csrfToken := r.FormValue("csrf_token")
         .          .   2043:   name := r.FormValue("name")
         .          .   2044:   description := r.FormValue("description")
         .          .   2045:   priceStr := r.FormValue("price")
         .          .   2046:   categoryIDStr := r.FormValue("category_id")
         .          .   2047:
         .          .   2048:   f, header, err := r.FormFile("image")
         .          .   2049:   if err != nil {
         .          .   2050:       log.Print(err)
         .          .   2051:       outputErrorMsg(w, http.StatusBadRequest, "image error")
         .          .   2052:       return
         .          .   2053:   }
         .          .   2054:   defer f.Close()
         .          .   2055:
         .          .   2056:   if csrfToken != getCSRFToken(r) {
         .          .   2057:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   2058:       return
         .          .   2059:   }
         .          .   2060:
         .          .   2061:   categoryID, err := strconv.Atoi(categoryIDStr)
         .          .   2062:   if err != nil || categoryID < 0 {
         .          .   2063:       outputErrorMsg(w, http.StatusBadRequest, "category id error")
         .          .   2064:       return
         .          .   2065:   }
         .          .   2066:
         .          .   2067:   price, err := strconv.Atoi(priceStr)
         .          .   2068:   if err != nil {
         .          .   2069:       outputErrorMsg(w, http.StatusBadRequest, "price error")
         .          .   2070:       return
         .          .   2071:   }
         .          .   2072:
         .          .   2073:   if name == "" || description == "" || price == 0 || categoryID == 0 {
         .          .   2074:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2075:
         .          .   2076:       return
         .          .   2077:   }
         .          .   2078:
         .          .   2079:   if price < ItemMinPrice || price > ItemMaxPrice {
         .          .   2080:       outputErrorMsg(w, http.StatusBadRequest, ItemPriceErrMsg)
         .          .   2081:
         .          .   2082:       return
         .          .   2083:   }
         .          .   2084:
         .          .   2085:   category, err := getCategoryByID(dbx, categoryID)
         .          .   2086:   if err != nil || category.ParentID == 0 {
         .          .   2087:       log.Print(categoryID, category)
         .          .   2088:       outputErrorMsg(w, http.StatusBadRequest, "Incorrect category ID")
         .          .   2089:       return
         .          .   2090:   }
         .          .   2091:
         .          .   2092:   user, errCode, errMsg := getUser(r)
         .          .   2093:   if errMsg != "" {
         .          .   2094:       outputErrorMsg(w, errCode, errMsg)
         .          .   2095:       return
         .          .   2096:   }
         .          .   2097:
         .       20ms   2098:   img, err := ioutil.ReadAll(f)
         .          .   2099:   if err != nil {
         .          .   2100:       log.Print(err)
         .          .   2101:       outputErrorMsg(w, http.StatusInternalServerError, "image error")
         .          .   2102:       return
         .          .   2103:   }
         .          .   2104:
         .          .   2105:   ext := filepath.Ext(header.Filename)
         .          .   2106:
         .          .   2107:   if !(ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif") {
         .          .   2108:       outputErrorMsg(w, http.StatusBadRequest, "unsupported image format error")
         .          .   2109:       return
         .          .   2110:   }
         .          .   2111:
         .          .   2112:   if ext == ".jpeg" {
         .          .   2113:       ext = ".jpg"
         .          .   2114:   }
         .          .   2115:
         .          .   2116:   imgName := fmt.Sprintf("%s%s", secureRandomStr(16), ext)
         .       10ms   2117:   err = ioutil.WriteFile(fmt.Sprintf("../public/upload/%s", imgName), img, 0644)
         .          .   2118:   if err != nil {
         .          .   2119:       log.Print(err)
         .          .   2120:       outputErrorMsg(w, http.StatusInternalServerError, "Saving image failed")
         .          .   2121:       return
         .          .   2122:   }
         .          .   2123:
         .       50ms   2124:   tx := dbx.MustBegin()
         .          .   2125:
         .          .   2126:   seller := User{}
         .          .   2127:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", user.ID)
         .          .   2128:   if err == sql.ErrNoRows {
         .          .   2129:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .   2130:       tx.Rollback()
         .          .   2131:       return
         .          .   2132:   }
         .          .   2133:   if err != nil {
         .          .   2134:       log.Print(err)
         .          .   2135:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2136:       tx.Rollback()
         .          .   2137:       return
         .          .   2138:   }
         .          .   2139:
         .          .   2140:   var itemID int64
         .          .   2141:   if err := tx.Get(
         .          .   2142:       &itemID,
         .          .   2143:       "INSERT INTO items (seller_id, status, name, price, description,image_name,category_id) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   2144:       seller.ID,
         .          .   2145:       ItemStatusOnSale,
         .          .   2146:       name,
         .          .   2147:       price,
         .          .   2148:       description,
         .          .   2149:       imgName,
         .          .   2150:       category.ID,
         .          .   2151:   ); err != nil {
         .          .   2152:       log.Print(err)
         .          .   2153:
         .          .   2154:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2155:       return
         .          .   2156:   }
         .          .   2157:
         .          .   2158:   // cache
         .          .   2159:   key := fmt.Sprintf(UserSimpleCacheKeyFmt, user.ID)
         .          .   2160:   err = redisClient.Incr(context.Background(), key).Err()
         .          .   2161:   if err != nil {
         .          .   2162:       log.Print(err)
         .          .   2163:
         .          .   2164:       outputErrorMsg(w, http.StatusInternalServerError, "user simple num sell cache update reds error")
         .          .   2165:       return
         .          .   2166:   }
         .          .   2167:
         .          .   2168:   now := time.Now()
         .          .   2169:   _, err = tx.Exec("UPDATE users SET num_sell_items=?, last_bump=? WHERE id=?",
         .          .   2170:       seller.NumSellItems+1,
         .          .   2171:       now,
         .          .   2172:       seller.ID,
         .          .   2173:   )
         .          .   2174:   if err != nil {
         .          .   2175:       log.Print(err)
         .          .   2176:
         .          .   2177:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2178:       return
         .          .   2179:   }
         .       10ms   2180:   tx.Commit()
         .          .   2181:
         .          .   2182:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   2183:   json.NewEncoder(w).Encode(resSell{ID: itemID})
         .          .   2184:}
         .          .   2185:
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
      10ms       30ms (flat, cum)  0.14% of Total
         .          .   1651:   }
         .          .   1652:
         .          .   1653:   tx := dbx.MustBegin()
         .          .   1654:
         .          .   1655:   item := Item{}
         .       10ms   1656:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1657:   if err == sql.ErrNoRows {
         .          .   1658:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1659:       tx.Rollback()
         .          .   1660:       return
         .          .   1661:   }
         .          .   1662:   if err != nil {
         .          .   1663:       log.Print(err)
         .          .   1664:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1665:       tx.Rollback()
         .          .   1666:       return
         .          .   1667:   }
         .          .   1668:
         .          .   1669:   if item.Status != ItemStatusTrading {
         .          .   1670:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1671:       tx.Rollback()
         .          .   1672:       return
         .          .   1673:   }
         .          .   1674:
         .       10ms   1675:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1676:   if err == sql.ErrNoRows {
         .          .   1677:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1678:       tx.Rollback()
         .          .   1679:       return
         .          .   1680:   }
         .          .   1681:   if err != nil {
         .          .   1682:       log.Print(err)
         .          .   1683:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1684:       tx.Rollback()
         .          .   1685:       return
         .          .   1686:   }
         .          .   1687:
         .          .   1688:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1689:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1690:       tx.Rollback()
         .          .   1691:       return
         .          .   1692:   }
         .          .   1693:
         .          .   1694:   shipping := Shipping{}
         .          .   1695:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1696:   if err == sql.ErrNoRows {
         .          .   1697:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1698:       tx.Rollback()
         .          .   1699:       return
         .          .   1700:   }
         .          .   1701:   if err != nil {
         .          .   1702:       log.Print(err)
         .          .   1703:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1704:       tx.Rollback()
         .          .   1705:       return
         .          .   1706:   }
         .          .   1707:
         .          .   1708:   img, err := APIShipmentRequest(getShipmentServiceURL(), &APIShipmentRequestReq{
         .          .   1709:       ReserveID: shipping.ReserveID,
         .          .   1710:   })
         .          .   1711:   if err != nil {
         .          .   1712:       log.Print(err)
         .          .   1713:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1714:       tx.Rollback()
         .          .   1715:
         .          .   1716:       return
         .          .   1717:   }
         .          .   1718:
         .          .   1719:   _, err = tx.Exec("UPDATE shippings SET status = ?, img_binary = ?, updated_at = ? WHERE transaction_evidence_id = ?",
         .          .   1720:       ShippingsStatusWaitPickup,
         .          .   1721:       img,
         .          .   1722:       time.Now(),
         .          .   1723:       transactionEvidence.ID,
         .          .   1724:   )
         .          .   1725:   if err != nil {
         .          .   1726:       log.Print(err)
         .          .   1727:
         .          .   1728:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1729:       tx.Rollback()
         .          .   1730:       return
         .          .   1731:   }
         .          .   1732:
      10ms       10ms   1733:   tx.Commit()
         .          .   1734:
         .          .   1735:   rps := resPostShip{
         .          .   1736:       Path:      fmt.Sprintf("/transactions/%d.png", transactionEvidence.ID),
         .          .   1737:       ReserveID: shipping.ReserveID,
         .          .   1738:   }
ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum) 0.093% of Total
         .          .   1801:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1802:       tx.Rollback()
         .          .   1803:       return
         .          .   1804:   }
         .          .   1805:
         .       20ms   1806:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1807:   if err == sql.ErrNoRows {
         .          .   1808:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1809:       tx.Rollback()
         .          .   1810:       return
         .          .   1811:   }
mackee commented 1 year ago

API並列実行

{"pass":true,"score":13270,"campaign":3,"language":"Go","messages":["GET /new_items/60.json: リクエストに失敗しました(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1797)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3158)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3234)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しま
した (user_id: 340)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3840)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 601)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 804)(タイムアウトしました)","POST /buy: リクエストに失敗しました (item_id: 51923)(タイムアウトしました)","POST /sell: リクエストに失敗しました(タイムアウトしました)","POST /ship: リクエストに失敗しました (item_id: 51908)(タイムアウトしました)"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
705 0 80 0 625 0 POST /buy 0.468 4.284 2162.832 3.068 4.200 0.000 49.000 33.291
417 0 397 0 20 0 GET /users/transactions.json 0.004 6.427 1221.074 2.928 4.972 0.000 6984.000 5426.887
2730 0 2728 0 2 0 GET /new_items/\d+.json 0.004 0.932 252.410 0.092 0.324 0.000 5727.000 5514.135
335 0 335 0 0 0 GET /new_items.json 0.036 1.396 89.090 0.266 0.656 5631.000 6018.000 5782.952
1572 0 1564 0 8 0 POST /login 0.004 0.320 75.592 0.048 0.156 73.000 109.000 96.947
94 0 70 0 24 0 POST /ship_done 0.376 0.916 60.477 0.643 0.824 0.000 83.000 36.415
92 0 75 0 17 0 POST /ship 0.106 0.856 59.554 0.647 0.840 0.000 61.000 54.750
66 0 64 0 2 0 POST /complete 0.053 0.944 50.328 0.763 0.832 0.000 34.000 32.970
7441 0 7441 0 0 0 GET /items/\d+.json 0.000 0.340 31.828 0.004 0.016 1045.000 1978.000 1221.574
574 0 574 0 0 0 GET /users/\d+.json 0.004 0.264 7.560 0.013 0.096 94.000 5304.000 3102.213
114 0 93 0 21 0 POST /sell 0.004 0.280 4.640 0.041 0.236 13.000 106.000 25.404
1 0 1 0 0 0 POST /initialize 2.076 2.076 2.076 2.076 2.076 31.000 31.000 31.000
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.152 1.452 0.025 0.124 51645.000 150881.000 79932.086
1564 0 1564 0 0 0 GET /settings 0.000 0.020 1.048 0.001 0.004 851.000 873.000 860.700
13 0 13 0 0 0 POST /bump 0.004 0.092 0.220 0.017 0.092 90.000 92.000 90.923
87 0 73 0 14 0 GET /transactions/\d+.png 0.000 0.044 0.208 0.002 0.008 33.000 634.000 525.448
10 0 3 0 7 0 POST /items/edit 0.000 0.004 0.012 0.001 0.004 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.004 0.004 0.004 0.004 0.004 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.004 0.004 0.004 0.004 0.004 994.000 994.000 994.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 47294.000 47294.000 47294.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
tetsuzawa commented 1 year ago

https://github.com/tetsuzawa/isucon9-qualify-suburi-20230114/pull/20

{"pass":true,"score":13370,"campaign":3,"language":"Go","messages":[]}

COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
703 0 74 0 629 0 POST /buy 0.077 4.336 2000.022 2.845 4.304 0.000 49.000 33.095
438 0 418 0 20 0 GET /users/transactions.json 0.004 5.616 1218.021 2.781 4.180 0.000 6943.000 5433.466
2576 0 2573 0 3 0 GET /new_items/\d+.json 0.001 0.924 281.754 0.109 0.392 0.000 5687.000 5516.264
333 0 333 0 0 0 GET /new_items.json 0.036 1.220 85.680 0.257 0.704 5624.000 5918.000 5774.342
1571 0 1563 0 8 0 POST /login 0.004 0.220 69.432 0.044 0.148 73.000 109.000 96.834
91 0 70 0 21 0 POST /ship_done 0.804 0.976 59.356 0.652 0.844 29.000 83.000 37.615
85 0 71 0 14 0 POST /ship 0.804 0.888 55.184 0.649 0.828 29.000 61.000 56.388
66 0 63 0 3 0 POST /complete 0.004 0.852 50.139 0.760 0.840 0.000 34.000 32.455
7291 0 7291 0 0 0 GET /items/\d+.json 0.000 0.632 27.156 0.004 0.012 1060.000 1985.000 1223.995
598 0 598 0 0 0 GET /users/\d+.json 0.000 0.208 5.396 0.009 0.044 95.000 5318.000 3018.605
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.260 3.432 0.059 0.172 52606.000 143944.000 88311.000
113 0 92 0 21 0 POST /sell 0.040 0.232 3.424 0.030 0.148 13.000 106.000 25.513
1 0 1 0 0 0 POST /initialize 2.088 2.088 2.088 2.088 2.088 31.000 31.000 31.000
1563 0 1563 0 0 0 GET /settings 0.000 0.008 0.916 0.001 0.004 850.000 872.000 860.554
13 0 13 0 0 0 POST /bump 0.004 0.108 0.296 0.023 0.108 91.000 92.000 91.077
84 0 70 0 14 0 GET /transactions/\d+.png 0.000 0.052 0.232 0.003 0.008 33.000 630.000 521.821
10 0 3 0 7 0 POST /items/edit 0.000 0.008 0.016 0.002 0.008 58.000 93.000 68.400
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.012 0.012 0.012 0.012 0.012 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.008 0.008 0.008 0.008 0.008 774.000 774.000 774.000
1 0 1 0 0 0 GET /reports.json 0.008 0.008 0.008 0.008 0.008 44586.000 44586.000 44586.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.000 0.000 0.000 0.000 0.000 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET / 0.000 0.000 0.000 0.000 0.000 2037.000 2037.000 2037.000
mackee commented 1 year ago

order by status

{"pass":true,"score":14730,"campaign":3,"language":"Go","messages":["GET /new_items/1.json: リクエストに失敗しました(タイムアウトしました)","GET /new_items/10.json: リクエストに失敗しました(タイムアウトしました)","GET /new_items/20.json: リクエストに失敗しました(タイムアウトしました)","GET /new_items/30.json: リクエストに失敗しました(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1047)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1048)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1159)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 125)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 1388)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2005)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2026)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2071)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2285)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2312)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 232)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2381)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2609)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2692)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 2968)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3483)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3495)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3664)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3765)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 3924)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 407)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 579)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 696)(タイムアウトしました)","GET /users/transactions.json リクエストに失敗しました (user_id: 916)(
タイムアウトしました)","POST /complete: リクエストに失敗しました (item_id: 52395)(タイムアウトしました)","POST /sell: リクエストに失敗しました(タイムアウトしました)"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
708 0 85 0 623 0 POST /buy 1.604 4.332 1907.545 2.694 4.284 29.000 49.000 33.367
441 0 415 0 26 0 GET /users/transactions.json 0.004 6.428 1291.093 2.928 4.828 0.000 6876.000 5374.147
3019 0 3014 0 5 0 GET /new_items/\d+.json 0.001 0.756 214.487 0.071 0.276 0.000 5742.000 5513.395
103 0 82 0 21 0 POST /ship_done 0.800 0.904 68.828 0.668 0.828 29.000 83.000 37.194
96 0 82 0 14 0 POST /ship 0.804 0.980 64.371 0.671 0.868 29.000 61.000 56.917
1573 0 1565 0 8 0 POST /login 0.004 0.220 61.872 0.039 0.132 73.000 109.000 96.770
73 0 72 0 1 0 POST /complete 0.004 0.856 56.505 0.774 0.852 0.000 34.000 33.534
8097 0 8097 0 0 0 GET /items/\d+.json 0.000 0.644 37.768 0.005 0.020 1056.000 1999.000 1222.215
582 0 582 0 0 0 GET /users/\d+.json 0.000 0.296 20.708 0.036 0.156 95.000 5313.000 3138.285
390 0 390 0 0 0 GET /new_items.json 0.004 0.216 11.432 0.029 0.152 5603.000 6017.000 5795.644
121 0 100 0 21 0 POST /sell 0.004 0.596 3.928 0.032 0.156 13.000 106.000 24.686
1 0 1 0 0 0 POST /initialize 2.800 2.800 2.800 2.800 2.800 31.000 31.000 31.000
760 0 760 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.176 2.204 0.003 0.004 51400.000 152682.000 80898.203
1567 0 1567 0 0 0 GET /settings 0.000 0.008 1.008 0.001 0.004 851.000 871.000 860.461
96 0 82 0 14 0 GET /transactions/\d+.png 0.000 0.280 0.520 0.005 0.012 33.000 636.000 535.292
13 0 13 0 0 0 POST /bump 0.004 0.204 0.324 0.025 0.204 90.000 92.000 90.769
10 0 3 0 7 0 POST /items/edit 0.000 0.008 0.028 0.003 0.008 58.000 93.000 68.300
2 0 2 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.000 0.008 0.008 0.004 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 49584.000 49584.000 49584.000
2 0 2 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.000 0.000 0.000 0.000 0.000 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
2 0 2 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET / 0.000 0.000 0.000 0.000 0.000 1085.000 1085.000 1085.000
1 0 1 0 0 0 GET /timeline 0.000 0.000 0.000 0.000 0.000 1085.000 1085.000 1085.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css.map 0.000 0.000 0.000 0.000 0.000 1423.000 1423.000 1423.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js.map 0.000 0.000 0.000 0.000 0.000 2169903.000 2169903.000 2169903.000
2 0 2 0 0 0 GET /favicon.png 0.000 0.000 0.000 0.000 0.000 176.000 176.000 176.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js.map 0.000 0.000 0.000 0.000 0.000 292234.000 292234.000 292234.000
tetsuzawa commented 1 year ago

{"pass":true,"score":13530,"campaign":3,"language":"Go","messages":[]}

COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
699 0 76 0 623 0 POST /buy 0.004 4.304 2289.497 3.275 4.268 29.000 49.000 33.382
421 0 399 0 22 0 GET /users/transactions.json 0.004 6.540 1250.928 2.971 4.832 0.000 6951.000 5412.473
2567 0 2562 0 5 0 GET /new_items/\d+.json 0.004 0.792 245.362 0.096 0.324 0.000 5737.000 5509.005
337 0 337 0 0 0 GET /new_items.json 0.032 1.320 75.803 0.225 0.644 5590.000 5952.000 5782.792
1571 0 1563 0 8 0 POST /login 0.052 0.380 73.980 0.047 0.156 73.000 110.000 96.941
94 0 70 0 24 0 POST /ship_done 0.411 0.948 60.807 0.647 0.824 0.000 83.000 36.415
89 0 75 0 14 0 POST /ship 0.804 0.968 58.748 0.660 0.840 29.000 61.000 56.596
65 0 64 0 1 0 POST /complete 0.004 0.884 50.126 0.771 0.832 0.000 34.000 33.477
7004 0 7004 0 0 0 GET /items/\d+.json 0.000 0.572 29.851 0.004 0.016 1060.000 1983.000 1222.961
536 0 536 0 0 0 GET /users/\d+.json 0.012 0.380 7.060 0.013 0.076 96.000 5338.000 3108.998
112 0 91 0 21 0 POST /sell 0.004 0.280 4.192 0.037 0.192 13.000 106.000 25.625
1 0 1 0 0 0 POST /initialize 2.064 2.064 2.064 2.064 2.064 31.000 31.000 31.000
1563 0 1563 0 0 0 GET /settings 0.000 0.016 0.916 0.001 0.004 850.000 875.000 860.694
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.156 0.656 0.011 0.120 51400.000 152466.000 76932.793
87 0 73 0 14 0 GET /transactions/\d+.png 0.000 0.240 0.400 0.005 0.008 33.000 634.000 526.161
13 0 13 0 0 0 POST /bump 0.004 0.112 0.380 0.029 0.112 90.000 91.000 90.923
10 0 3 0 7 0 POST /items/edit 0.000 0.012 0.024 0.002 0.012 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.008 0.008 0.008 0.008 0.008 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /reports.json 0.008 0.008 0.008 0.008 0.008 44679.000 44679.000 44679.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
tetsuzawa commented 1 year ago
$ go tool pprof -list main. -cum -seconds 60 http://localhost:8000/debug/pprof/profile
Fetching profile over HTTP from http://localhost:8000/debug/pprof/profile?seconds=60
Please wait... (1m0s)
Saved profile in /home/isucon/pprof/pprof.isucari.samples.cpu.008.pb.gz
Total: 15.49s
ROUTINE ======================== main.APIShipmentCreate in /home/isucon/isucari/webapp/go/api.go
         0       30ms (flat, cum)  0.19% of Total
         .          .     85:}
         .          .     86:
         .          .     87:func APIShipmentCreate(shipmentURL string, param *APIShipmentCreateReq) (*APIShipmentCreateRes, error) {
         .          .     88:   b, _ := json.Marshal(param)
         .          .     89:
         .       10ms     90:   req, err := http.NewRequest(http.MethodPost, shipmentURL+"/create", bytes.NewBuffer(b))
         .          .     91:   if err != nil {
         .          .     92:       return nil, err
         .          .     93:   }
         .          .     94:
         .          .     95:   req.Header.Set("User-Agent", userAgent)
         .          .     96:   req.Header.Set("Content-Type", "application/json")
         .          .     97:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .     98:
         .       20ms     99:   res, err := http.DefaultClient.Do(req)
         .          .    100:   if err != nil {
         .          .    101:       return nil, err
         .          .    102:   }
         .          .    103:   defer res.Body.Close()
         .          .    104:
ROUTINE ======================== main.APIShipmentStatus in /home/isucon/isucari/webapp/go/api.go
         0       30ms (flat, cum)  0.19% of Total
         .          .    158:
         .          .    159:   req.Header.Set("User-Agent", userAgent)
         .          .    160:   req.Header.Set("Content-Type", "application/json")
         .          .    161:   req.Header.Set("Authorization", IsucariAPIToken)
         .          .    162:
         .       20ms    163:   res, err := http.DefaultClient.Do(req)
         .          .    164:   if err != nil {
         .          .    165:       return nil, err
         .          .    166:   }
         .          .    167:   defer res.Body.Close()
         .          .    168:
         .          .    169:   if res.StatusCode != http.StatusOK {
         .          .    170:       b, err := ioutil.ReadAll(res.Body)
         .          .    171:       if err != nil {
         .          .    172:           return nil, fmt.Errorf("failed to read res.Body and the status code of the response from shipment service was not 200: %v", err)
         .          .    173:       }
         .          .    174:       return nil, fmt.Errorf("status code: %d; body: %s", res.StatusCode, b)
         .          .    175:   }
         .          .    176:
         .          .    177:   ssr := &APIShipmentStatusRes{}
         .       10ms    178:   err = json.NewDecoder(res.Body).Decode(&ssr)
         .          .    179:   if err != nil {
         .          .    180:       return nil, err
         .          .    181:   }
         .          .    182:
         .          .    183:   return ssr, nil
ROUTINE ======================== main.getCSRFToken in /home/isucon/isucari/webapp/go/main.go
         0      130ms (flat, cum)  0.84% of Total
         .          .    422:
         .          .    423:   return session
         .          .    424:}
         .          .    425:
         .          .    426:func getCSRFToken(r *http.Request) string {
         .      130ms    427:   session := getSession(r)
         .          .    428:
         .          .    429:   csrfToken, ok := session.Values["csrf_token"]
         .          .    430:   if !ok {
         .          .    431:       return ""
         .          .    432:   }
ROUTINE ======================== main.getCategoryByID in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .    527://         category.ParentCategoryName = parentCategory.CategoryName
         .          .    528://     }
         .          .    529://     return category, err
         .          .    530:// }
         .          .    531:func getCategoryByID(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .       20ms    532:   return getCategoryByIDOnMemory(q, categoryID)
         .          .    533:}
         .          .    534:
         .          .    535:var categories = []Category{
         .          .    536:   {ID: 1, ParentID: 0, CategoryName: "ソファー", ParentCategoryName: "nan"},
         .          .    537:   {ID: 2, ParentID: 1, CategoryName: "一人掛けソファー", ParentCategoryName: "ソファー"},
ROUTINE ======================== main.getCategoryByIDOnMemory in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .    593:       }
         .          .    594:   }
         .          .    595:}
         .          .    596:
         .          .    597:func getCategoryByIDOnMemory(q sqlx.Queryer, categoryID int) (category Category, err error) {
         .       20ms    598:   return categoryMap[categoryID], nil
         .          .    599:}
         .          .    600:
         .          .    601:func getConfigByName(name string) (string, error) {
         .          .    602:   config := Config{}
         .          .    603:   err := dbx.Get(&config, "SELECT * FROM configs WHERE name = ?", name)
ROUTINE ======================== main.getImageURL in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.52% of Total
         .          .   2565:       Error string `json:"error"`
         .          .   2566:   }{Error: msg})
         .          .   2567:}
         .          .   2568:
         .          .   2569:func getImageURL(imageName string) string {
         .       80ms   2570:   return fmt.Sprintf("/upload/%s", imageName)
         .          .   2571:}
ROUTINE ======================== main.getItem in /home/isucon/isucari/webapp/go/main.go
         0      1.22s (flat, cum)  7.88% of Total
         .          .   1212:   if err != nil || itemID <= 0 {
         .          .   1213:       outputErrorMsg(w, http.StatusBadRequest, "incorrect item id")
         .          .   1214:       return
         .          .   1215:   }
         .          .   1216:
         .      390ms   1217:   user, errCode, errMsg := getUser(r)
         .          .   1218:   if errMsg != "" {
         .          .   1219:       outputErrorMsg(w, errCode, errMsg)
         .          .   1220:       return
         .          .   1221:   }
         .          .   1222:
         .          .   1223:   item := Item{}
         .          .   1224:   // cache read
         .          .   1225:   itemCacheKey := fmt.Sprintf("item_id:%d", itemID)
         .          .   1226:   if it, ok := ItemCache.Get(itemCacheKey); ok {
         .          .   1227:       item = it
         .          .   1228:   } else {
         .      620ms   1229:       err = dbx.Get(&item, "SELECT * FROM items WHERE id = ?", itemID)
         .          .   1230:       if err == sql.ErrNoRows {
         .          .   1231:           outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1232:           return
         .          .   1233:       }
         .          .   1234:       if err != nil {
         .          .   1235:           log.Print(err)
         .          .   1236:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1237:           return
         .          .   1238:       }
         .          .   1239:       ItemCache.Set(itemCacheKey, item)
         .          .   1240:   }
         .          .   1241:
         .          .   1242:   category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1243:   if err != nil {
         .          .   1244:       outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1245:       return
         .          .   1246:   }
         .          .   1247:
         .      140ms   1248:   seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .   1249:   if err != nil {
         .          .   1250:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1251:       return
         .          .   1252:   }
         .          .   1253:
         .          .   1254:   itemDetail := ItemDetail{
         .          .   1255:       ID:       item.ID,
         .          .   1256:       SellerID: item.SellerID,
         .          .   1257:       Seller:   &seller,
         .          .   1258:       // BuyerID
         .          .   1259:       // Buyer
         .          .   1260:       Status:      item.Status,
         .          .   1261:       Name:        item.Name,
         .          .   1262:       Price:       item.Price,
         .          .   1263:       Description: item.Description,
         .       10ms   1264:       ImageURL:    getImageURL(item.ImageName),
         .          .   1265:       CategoryID:  item.CategoryID,
         .          .   1266:       // TransactionEvidenceID
         .          .   1267:       // TransactionEvidenceStatus
         .          .   1268:       // ShippingStatus
         .          .   1269:       Category:  &category,
         .          .   1270:       CreatedAt: item.CreatedAt.Unix(),
         .          .   1271:   }
         .          .   1272:
         .          .   1273:   if (user.ID == item.SellerID || user.ID == item.BuyerID) && item.BuyerID != 0 {
         .          .   1274:       buyer, err := getUserSimpleByID(dbx, item.BuyerID)
         .          .   1275:       if err != nil {
         .          .   1276:           outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1277:           return
         .          .   1278:       }
         .          .   1279:       itemDetail.BuyerID = item.BuyerID
         .          .   1280:       itemDetail.Buyer = &buyer
         .          .   1281:
         .          .   1282:       transactionEvidence := TransactionEvidence{}
         .       10ms   1283:       err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1284:       if err != nil && err != sql.ErrNoRows {
         .          .   1285:           // It's able to ignore ErrNoRows
         .          .   1286:           log.Print(err)
         .          .   1287:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1288:           return
         .          .   1289:       }
         .          .   1290:
         .          .   1291:       if transactionEvidence.ID > 0 {
         .          .   1292:           shipping := Shipping{}
         .          .   1293:           err = dbx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1294:           if err == sql.ErrNoRows {
         .          .   1295:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1296:               return
         .          .   1297:           }
         .          .   1298:           if err != nil {
         .          .   1299:               log.Print(err)
         .          .   1300:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1301:               return
         .          .   1302:           }
         .          .   1303:
         .          .   1304:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1305:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1306:           itemDetail.ShippingStatus = shipping.Status
         .          .   1307:       }
         .          .   1308:   }
         .          .   1309:
         .          .   1310:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       50ms   1311:   json.NewEncoder(w).Encode(itemDetail)
         .          .   1312:}
         .          .   1313:
         .          .   1314:func postItemEdit(w http.ResponseWriter, r *http.Request) {
         .          .   1315:   rie := reqItemEdit{}
         .          .   1316:   err := json.NewDecoder(r.Body).Decode(&rie)
ROUTINE ======================== main.getNewCategoryItems in /home/isucon/isucari/webapp/go/main.go
      10ms      2.98s (flat, cum) 19.24% of Total
         .          .    869:           return
         .          .    870:       }
         .          .    871:   }
         .          .    872:
         .          .    873:   items := []Item{}
         .      1.27s    874:   err = dbx.Select(&items, inQuery, inArgs...)
         .          .    875:
         .          .    876:   if err != nil {
         .          .    877:       log.Print(err)
         .          .    878:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    879:       return
         .          .    880:   }
         .          .    881:
         .          .    882:   itemSimples := []ItemSimple{}
         .          .    883:   for _, item := range items {
         .      1.34s    884:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    885:       if err != nil {
         .          .    886:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    887:           return
         .          .    888:       }
      10ms       30ms    889:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    890:       if err != nil {
         .          .    891:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    892:           return
         .          .    893:       }
         .       30ms    894:       itemSimples = append(itemSimples, ItemSimple{
         .          .    895:           ID:         item.ID,
         .          .    896:           SellerID:   item.SellerID,
         .          .    897:           Seller:     &seller,
         .          .    898:           Status:     item.Status,
         .          .    899:           Name:       item.Name,
         .          .    900:           Price:      item.Price,
         .       60ms    901:           ImageURL:   getImageURL(item.ImageName),
         .          .    902:           CategoryID: item.CategoryID,
         .          .    903:           Category:   &category,
         .          .    904:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    905:       })
         .          .    906:   }
         .          .    907:
         .          .    908:   hasNext := false
         .          .    909:   if len(itemSimples) > ItemsPerPage {
         .          .    910:       hasNext = true
         .          .    911:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    912:   }
         .          .    913:
         .          .    914:   rni := resNewItems{
         .          .    915:       RootCategoryID:   rootCategory.ID,
         .          .    916:       RootCategoryName: rootCategory.CategoryName,
         .          .    917:       Items:            itemSimples,
         .          .    918:       HasNext:          hasNext,
         .          .    919:   }
         .          .    920:
         .       10ms    921:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .      240ms    922:   json.NewEncoder(w).Encode(rni)
         .          .    923:
         .          .    924:}
         .          .    925:
         .          .    926:func getUserItems(w http.ResponseWriter, r *http.Request) {
         .          .    927:   userIDStr := pat.Param(r, "user_id")
ROUTINE ======================== main.getNewItems in /home/isucon/isucari/webapp/go/main.go
         0      320ms (flat, cum)  2.07% of Total
         .          .    726:   }
         .          .    727:
         .          .    728:   items := []Item{}
         .          .    729:   if itemID > 0 && createdAt > 0 {
         .          .    730:       // paging
         .      110ms    731:       err := dbx.Select(&items,
         .          .    732:           "SELECT * FROM items WHERE status IN (?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    733:           ItemStatusOnSale,
         .          .    734:           ItemStatusSoldOut,
         .          .    735:           time.Unix(createdAt, 0),
         .          .    736:           time.Unix(createdAt, 0),
         .          .    737:           itemID,
         .          .    738:           ItemsPerPage+1,
         .          .    739:       )
         .          .    740:       if err != nil {
         .          .    741:           log.Print(err)
         .          .    742:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    743:           return
         .          .    744:       }
         .          .    745:   } else {
         .          .    746:       // 1st page
         .          .    747:       err := dbx.Select(&items,
         .          .    748:           "SELECT * FROM items WHERE status IN (?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    749:           ItemStatusOnSale,
         .          .    750:           ItemStatusSoldOut,
         .          .    751:           ItemsPerPage+1,
         .          .    752:       )
         .          .    753:       if err != nil {
         .          .    754:           log.Print(err)
         .          .    755:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    756:           return
         .          .    757:       }
         .          .    758:   }
         .          .    759:
         .          .    760:   itemSimples := []ItemSimple{}
         .          .    761:   for _, item := range items {
         .      170ms    762:       seller, err := getUserSimpleByID(dbx, item.SellerID)
         .          .    763:       if err != nil {
         .          .    764:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .    765:           return
         .          .    766:       }
         .       10ms    767:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .    768:       if err != nil {
         .          .    769:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .    770:           return
         .          .    771:       }
         .          .    772:       itemSimples = append(itemSimples, ItemSimple{
         .          .    773:           ID:         item.ID,
         .          .    774:           SellerID:   item.SellerID,
         .          .    775:           Seller:     &seller,
         .          .    776:           Status:     item.Status,
         .          .    777:           Name:       item.Name,
         .          .    778:           Price:      item.Price,
         .          .    779:           ImageURL:   getImageURL(item.ImageName),
         .          .    780:           CategoryID: item.CategoryID,
         .          .    781:           Category:   &category,
         .          .    782:           CreatedAt:  item.CreatedAt.Unix(),
         .          .    783:       })
         .          .    784:   }
         .          .    785:
         .          .    786:   hasNext := false
         .          .    787:   if len(itemSimples) > ItemsPerPage {
         .          .    788:       hasNext = true
         .          .    789:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .    790:   }
         .          .    791:
         .          .    792:   rni := resNewItems{
         .          .    793:       Items:   itemSimples,
         .          .    794:       HasNext: hasNext,
         .          .    795:   }
         .          .    796:
         .          .    797:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       30ms    798:   json.NewEncoder(w).Encode(rni)
         .          .    799:}
         .          .    800:
         .          .    801:func getNewCategoryItems(w http.ResponseWriter, r *http.Request) {
         .          .    802:   rootCategoryIDStr := pat.Param(r, "root_category_id")
         .          .    803:   rootCategoryID, err := strconv.Atoi(rootCategoryIDStr)
ROUTINE ======================== main.getQRCode in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .   1421:       outputErrorMsg(w, errCode, errMsg)
         .          .   1422:       return
         .          .   1423:   }
         .          .   1424:
         .          .   1425:   transactionEvidence := TransactionEvidence{}
         .       20ms   1426:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ?", transactionEvidenceID)
         .          .   1427:   if err == sql.ErrNoRows {
         .          .   1428:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1429:       return
         .          .   1430:   }
         .          .   1431:   if err != nil {
ROUTINE ======================== main.getSession in /home/isucon/isucari/webapp/go/main.go
         0      530ms (flat, cum)  3.42% of Total
         .          .    416:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .          .    417:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    418:}
         .          .    419:
         .          .    420:func getSession(r *http.Request) *sessions.Session {
         .      530ms    421:   session, _ := store.Get(r, sessionName)
         .          .    422:
         .          .    423:   return session
         .          .    424:}
         .          .    425:
         .          .    426:func getCSRFToken(r *http.Request) string {
ROUTINE ======================== main.getSettings in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.39% of Total
         .          .   2363:       ItemUpdatedAt: targetItem.UpdatedAt.Unix(),
         .          .   2364:   })
         .          .   2365:}
         .          .   2366:
         .          .   2367:func getSettings(w http.ResponseWriter, r *http.Request) {
         .       40ms   2368:   csrfToken := getCSRFToken(r)
         .          .   2369:
         .          .   2370:   user, _, errMsg := getUser(r)
         .          .   2371:
         .          .   2372:   ress := resSetting{}
         .          .   2373:   ress.CSRFToken = csrfToken
         .          .   2374:   if errMsg == "" {
         .          .   2375:       ress.User = &user
         .          .   2376:   }
         .          .   2377:
         .          .   2378:   ress.PaymentServiceURL = getPaymentServiceURL()
         .          .   2379:   ress.Categories = categories
         .          .   2380:
         .          .   2381:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   2382:   json.NewEncoder(w).Encode(ress)
         .          .   2383:}
         .          .   2384:
         .          .   2385:func postLogin(w http.ResponseWriter, r *http.Request) {
         .          .   2386:   rl := reqLogin{}
         .          .   2387:   err := json.NewDecoder(r.Body).Decode(&rl)
ROUTINE ======================== main.getTransactions in /home/isucon/isucari/webapp/go/main.go
         0      380ms (flat, cum)  2.45% of Total
         .          .   1031:   json.NewEncoder(w).Encode(rui)
         .          .   1032:}
         .          .   1033:
         .          .   1034:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .   1035:
         .       10ms   1036:   user, errCode, errMsg := getUser(r)
         .          .   1037:   if errMsg != "" {
         .          .   1038:       outputErrorMsg(w, errCode, errMsg)
         .          .   1039:       return
         .          .   1040:   }
         .          .   1041:
         .          .   1042:   query := r.URL.Query()
         .          .   1043:   itemIDStr := query.Get("item_id")
         .          .   1044:   var err error
         .          .   1045:   var itemID int64
         .          .   1046:   if itemIDStr != "" {
         .          .   1047:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .   1048:       if err != nil || itemID <= 0 {
         .          .   1049:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .   1050:           return
         .          .   1051:       }
         .          .   1052:   }
         .          .   1053:
         .          .   1054:   createdAtStr := query.Get("created_at")
         .          .   1055:   var createdAt int64
         .          .   1056:   if createdAtStr != "" {
         .          .   1057:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .   1058:       if err != nil || createdAt <= 0 {
         .          .   1059:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .   1060:           return
         .          .   1061:       }
         .          .   1062:   }
         .          .   1063:
         .       70ms   1064:   tx := dbx.MustBegin()
         .          .   1065:   items := []Item{}
         .          .   1066:   if itemID > 0 && createdAt > 0 {
         .          .   1067:       // paging
         .       10ms   1068:       err := tx.Select(&items,
         .          .   1069:           //"SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1070:           //"SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?)  AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1071:           "(SELECT * FROM items WHERE seller_id = ? AND (created_at < ?  OR (created_at <= ? AND id < ?))) UNION (SELECT * FROM items WHERE buyer_id = ? AND (created_at < ?  OR (created_at <= ? AND id < ?))) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1072:           user.ID,
         .          .   1073:           time.Unix(createdAt, 0),
         .          .   1074:           time.Unix(createdAt, 0),
         .          .   1075:           itemID,
         .          .   1076:           user.ID,
         .          .   1077:           time.Unix(createdAt, 0),
         .          .   1078:           time.Unix(createdAt, 0),
         .          .   1079:           itemID,
         .          .   1080:           TransactionsPerPage+1,
         .          .   1081:       )
         .          .   1082:       if err != nil {
         .          .   1083:           log.Print(err)
         .          .   1084:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1085:           tx.Rollback()
         .          .   1086:           return
         .          .   1087:       }
         .          .   1088:   } else {
         .          .   1089:       // 1st page
         .       10ms   1090:       err := tx.Select(&items,
         .          .   1091:           //"SELECT * FROM items WHERE (seller_id = ? OR buyer_id = ?) AND status IN (?,?,?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1092:           "(SELECT * FROM items WHERE seller_id = ?) UNION (SELECT * FROM items WHERE buyer_id = ?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .   1093:           user.ID,
         .          .   1094:           user.ID,
         .          .   1095:           TransactionsPerPage+1,
         .          .   1096:       )
         .          .   1097:       if err != nil {
         .          .   1098:           log.Print(err)
         .          .   1099:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1100:           tx.Rollback()
         .          .   1101:           return
         .          .   1102:       }
         .          .   1103:   }
         .          .   1104:
         .          .   1105:   itemDetails := []ItemDetail{}
         .          .   1106:   for _, item := range items {
         .       60ms   1107:       seller, err := getUserSimpleByID(tx, item.SellerID)
         .          .   1108:       if err != nil {
         .          .   1109:           outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1110:           tx.Rollback()
         .          .   1111:           return
         .          .   1112:       }
         .          .   1113:       category, err := getCategoryByID(tx, item.CategoryID)
         .          .   1114:       if err != nil {
         .          .   1115:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1116:           tx.Rollback()
         .          .   1117:           return
         .          .   1118:       }
         .          .   1119:
         .          .   1120:       itemDetail := ItemDetail{
         .          .   1121:           ID:       item.ID,
         .          .   1122:           SellerID: item.SellerID,
         .          .   1123:           Seller:   &seller,
         .          .   1124:           // BuyerID
         .          .   1125:           // Buyer
         .          .   1126:           Status:      item.Status,
         .          .   1127:           Name:        item.Name,
         .          .   1128:           Price:       item.Price,
         .          .   1129:           Description: item.Description,
         .       10ms   1130:           ImageURL:    getImageURL(item.ImageName),
         .          .   1131:           CategoryID:  item.CategoryID,
         .          .   1132:           // TransactionEvidenceID
         .          .   1133:           // TransactionEvidenceStatus
         .          .   1134:           // ShippingStatus
         .          .   1135:           Category:  &category,
         .          .   1136:           CreatedAt: item.CreatedAt.Unix(),
         .          .   1137:       }
         .          .   1138:
         .          .   1139:       if item.BuyerID != 0 {
         .       40ms   1140:           buyer, err := getUserSimpleByID(tx, item.BuyerID)
         .          .   1141:           if err != nil {
         .          .   1142:               outputErrorMsg(w, http.StatusNotFound, "buyer not found")
         .          .   1143:               tx.Rollback()
         .          .   1144:               return
         .          .   1145:           }
         .          .   1146:           itemDetail.BuyerID = item.BuyerID
         .          .   1147:           itemDetail.Buyer = &buyer
         .          .   1148:       }
         .          .   1149:
         .          .   1150:       transactionEvidence := TransactionEvidence{}
         .      100ms   1151:       err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", item.ID)
         .          .   1152:       if err != nil && err != sql.ErrNoRows {
         .          .   1153:           // It's able to ignore ErrNoRows
         .          .   1154:           log.Print(err)
         .          .   1155:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1156:           tx.Rollback()
         .          .   1157:           return
         .          .   1158:       }
         .          .   1159:
         .          .   1160:       if transactionEvidence.ID > 0 {
         .          .   1161:           shipping := Shipping{}
         .       20ms   1162:           err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ?", transactionEvidence.ID)
         .          .   1163:           if err == sql.ErrNoRows {
         .          .   1164:               outputErrorMsg(w, http.StatusNotFound, "shipping not found")
         .          .   1165:               tx.Rollback()
         .          .   1166:               return
         .          .   1167:           }
         .          .   1168:           if err != nil {
         .          .   1169:               log.Print(err)
         .          .   1170:               outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1171:               tx.Rollback()
         .          .   1172:               return
         .          .   1173:           }
         .       20ms   1174:           ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1175:               ReserveID: shipping.ReserveID,
         .          .   1176:           })
         .          .   1177:           if err != nil {
         .          .   1178:               log.Print(err)
         .          .   1179:               outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1180:               tx.Rollback()
         .          .   1181:               return
         .          .   1182:           }
         .          .   1183:
         .          .   1184:           itemDetail.TransactionEvidenceID = transactionEvidence.ID
         .          .   1185:           itemDetail.TransactionEvidenceStatus = transactionEvidence.Status
         .          .   1186:           itemDetail.ShippingStatus = ssr.Status
         .          .   1187:       }
         .          .   1188:
         .       10ms   1189:       itemDetails = append(itemDetails, itemDetail)
         .          .   1190:   }
         .          .   1191:   tx.Commit()
         .          .   1192:
         .          .   1193:   hasNext := false
         .          .   1194:   if len(itemDetails) > TransactionsPerPage {
         .          .   1195:       hasNext = true
         .          .   1196:       itemDetails = itemDetails[0:TransactionsPerPage]
         .          .   1197:   }
         .          .   1198:
         .          .   1199:   rts := resTransactions{
         .          .   1200:       Items:   itemDetails,
         .          .   1201:       HasNext: hasNext,
         .          .   1202:   }
         .          .   1203:
         .          .   1204:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1205:   json.NewEncoder(w).Encode(rts)
         .          .   1206:
         .          .   1207:}
         .          .   1208:
         .          .   1209:func getItem(w http.ResponseWriter, r *http.Request) {
         .          .   1210:   itemIDStr := pat.Param(r, "item_id")
ROUTINE ======================== main.getUser in /home/isucon/isucari/webapp/go/main.go
         0      400ms (flat, cum)  2.58% of Total
         .          .    433:
         .          .    434:   return csrfToken.(string)
         .          .    435:}
         .          .    436:
         .          .    437:func getUser(r *http.Request) (user User, errCode int, errMsg string) {
         .      400ms    438:   session := getSession(r)
         .          .    439:   userSess, ok := session.Values["user"]
         .          .    440:   //userID, ok := session.Values["user_id"]
         .          .    441:   if !ok {
         .          .    442:       return user, http.StatusNotFound, "no session"
         .          .    443:   }
ROUTINE ======================== main.getUserItems in /home/isucon/isucari/webapp/go/main.go
      10ms      190ms (flat, cum)  1.23% of Total
         .          .    929:   if err != nil || userID <= 0 {
         .          .    930:       outputErrorMsg(w, http.StatusBadRequest, "incorrect user id")
         .          .    931:       return
         .          .    932:   }
         .          .    933:
         .       10ms    934:   userSimple, err := getUserSimpleByID(dbx, userID)
         .          .    935:   if err != nil {
         .          .    936:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .    937:       return
         .          .    938:   }
         .          .    939:
         .          .    940:   query := r.URL.Query()
         .          .    941:   itemIDStr := query.Get("item_id")
         .          .    942:   var itemID int64
         .          .    943:   if itemIDStr != "" {
         .          .    944:       itemID, err = strconv.ParseInt(itemIDStr, 10, 64)
         .          .    945:       if err != nil || itemID <= 0 {
         .          .    946:           outputErrorMsg(w, http.StatusBadRequest, "item_id param error")
         .          .    947:           return
         .          .    948:       }
         .          .    949:   }
         .          .    950:
         .          .    951:   createdAtStr := query.Get("created_at")
         .          .    952:   var createdAt int64
         .          .    953:   if createdAtStr != "" {
         .          .    954:       createdAt, err = strconv.ParseInt(createdAtStr, 10, 64)
         .          .    955:       if err != nil || createdAt <= 0 {
         .          .    956:           outputErrorMsg(w, http.StatusBadRequest, "created_at param error")
         .          .    957:           return
         .          .    958:       }
         .          .    959:   }
         .          .    960:
         .          .    961:   items := []Item{}
         .          .    962:   if itemID > 0 && createdAt > 0 {
         .          .    963:       // paging
         .       40ms    964:       err := dbx.Select(&items,
         .          .    965:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) AND (created_at < ?  OR (created_at <= ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    966:           userSimple.ID,
         .          .    967:           ItemStatusOnSale,
         .          .    968:           ItemStatusTrading,
         .          .    969:           ItemStatusSoldOut,
         .          .    970:           time.Unix(createdAt, 0),
         .          .    971:           time.Unix(createdAt, 0),
         .          .    972:           itemID,
         .          .    973:           ItemsPerPage+1,
         .          .    974:       )
         .          .    975:       if err != nil {
         .          .    976:           log.Print(err)
         .          .    977:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    978:           return
         .          .    979:       }
         .          .    980:   } else {
         .          .    981:       // 1st page
         .       90ms    982:       err := dbx.Select(&items,
         .          .    983:           "SELECT * FROM items WHERE seller_id = ? AND status IN (?,?,?) ORDER BY created_at DESC, id DESC LIMIT ?",
         .          .    984:           userSimple.ID,
         .          .    985:           ItemStatusOnSale,
         .          .    986:           ItemStatusTrading,
         .          .    987:           ItemStatusSoldOut,
         .          .    988:           ItemsPerPage+1,
         .          .    989:       )
         .          .    990:       if err != nil {
         .          .    991:           log.Print(err)
         .          .    992:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .    993:           return
         .          .    994:       }
         .          .    995:   }
         .          .    996:
         .          .    997:   itemSimples := []ItemSimple{}
      10ms       10ms    998:   for _, item := range items {
         .          .    999:       category, err := getCategoryByID(dbx, item.CategoryID)
         .          .   1000:       if err != nil {
         .          .   1001:           outputErrorMsg(w, http.StatusNotFound, "category not found")
         .          .   1002:           return
         .          .   1003:       }
         .       20ms   1004:       itemSimples = append(itemSimples, ItemSimple{
         .          .   1005:           ID:         item.ID,
         .          .   1006:           SellerID:   item.SellerID,
         .          .   1007:           Seller:     &userSimple,
         .          .   1008:           Status:     item.Status,
         .          .   1009:           Name:       item.Name,
         .          .   1010:           Price:      item.Price,
         .          .   1011:           ImageURL:   getImageURL(item.ImageName),
         .          .   1012:           CategoryID: item.CategoryID,
         .          .   1013:           Category:   &category,
         .          .   1014:           CreatedAt:  item.CreatedAt.Unix(),
         .          .   1015:       })
         .          .   1016:   }
         .          .   1017:
         .          .   1018:   hasNext := false
         .          .   1019:   if len(itemSimples) > ItemsPerPage {
         .          .   1020:       hasNext = true
         .          .   1021:       itemSimples = itemSimples[0:ItemsPerPage]
         .          .   1022:   }
         .          .   1023:
         .          .   1024:   rui := resUserItems{
         .          .   1025:       User:    &userSimple,
         .          .   1026:       Items:   itemSimples,
         .          .   1027:       HasNext: hasNext,
         .          .   1028:   }
         .          .   1029:
         .          .   1030:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .       20ms   1031:   json.NewEncoder(w).Encode(rui)
         .          .   1032:}
         .          .   1033:
         .          .   1034:func getTransactions(w http.ResponseWriter, r *http.Request) {
         .          .   1035:
         .          .   1036:   user, errCode, errMsg := getUser(r)
ROUTINE ======================== main.getUserSimpleByID in /home/isucon/isucari/webapp/go/main.go
         0      1.73s (flat, cum) 11.17% of Total
         .          .    483:   }
         .          .    484:   return nil
         .          .    485:}
         .          .    486:
         .          .    487:func getUserSimpleByID(q sqlx.Queryer, userID int64) (userSimple UserSimple, err error) {
         .       20ms    488:   if us, ok := UserSimpleCache[userID]; ok {
         .      1.71s    489:       numsellStr, err := redisClient.Get(context.Background(), fmt.Sprintf(UserSimpleCacheKeyFmt, userID)).Result()
         .          .    490:       if err != nil {
         .          .    491:           return us, err
         .          .    492:       }
         .          .    493:       numsell, err := strconv.Atoi(numsellStr)
         .          .    494:       if err != nil {
ROUTINE ======================== main.main in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.065% of Total
         .          .    412:   mux.HandleFunc(pat.Get("/transactions/:transaction_id"), getIndex)
         .          .    413:   mux.HandleFunc(pat.Get("/users/:user_id"), getIndex)
         .          .    414:   mux.HandleFunc(pat.Get("/users/setting"), getIndex)
         .          .    415:   // Assets
         .          .    416:   mux.Handle(pat.Get("/*"), http.FileServer(http.Dir("../public")))
         .       10ms    417:   log.Fatal(http.ListenAndServe(":8000", mux))
         .          .    418:}
         .          .    419:
         .          .    420:func getSession(r *http.Request) *sessions.Session {
         .          .    421:   session, _ := store.Get(r, sessionName)
         .          .    422:
ROUTINE ======================== main.postBuy in /home/isucon/isucari/webapp/go/main.go
         0      4.39s (flat, cum) 28.34% of Total
         .          .   1463:   w.Header().Set("Content-Type", "image/png")
         .          .   1464:   w.Write(shipping.ImgBinary)
         .          .   1465:}
         .          .   1466:
         .          .   1467:func postBuy(w http.ResponseWriter, r *http.Request) {
         .       10ms   1468:   rb := reqBuy{}
         .          .   1469:
         .          .   1470:   err := json.NewDecoder(r.Body).Decode(&rb)
         .          .   1471:   if err != nil {
         .          .   1472:       outputErrorMsg(w, http.StatusBadRequest, "json decode error")
         .          .   1473:       return
         .          .   1474:   }
         .          .   1475:
         .       60ms   1476:   if rb.CSRFToken != getCSRFToken(r) {
         .          .   1477:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1478:
         .          .   1479:       return
         .          .   1480:   }
         .          .   1481:
         .          .   1482:   buyer, errCode, errMsg := getUser(r)
         .          .   1483:   if errMsg != "" {
         .          .   1484:       outputErrorMsg(w, errCode, errMsg)
         .          .   1485:       return
         .          .   1486:   }
         .          .   1487:
         .          .   1488:   var targetItem Item
         .          .   1489:   // cache read
         .          .   1490:   itemCacheKey := fmt.Sprintf("item_id:%d", rb.ItemID)
         .          .   1491:   if it, ok := ItemCache.Get(itemCacheKey); ok {
         .          .   1492:       targetItem = it
         .          .   1493:   } else {
         .      300ms   1494:       err = dbx.Get(&targetItem, "SELECT * FROM items WHERE id = ?", rb.ItemID)
         .          .   1495:       ItemCache.Set(itemCacheKey, targetItem)
         .          .   1496:
         .          .   1497:       if err == sql.ErrNoRows {
         .          .   1498:           outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1499:           return
         .          .   1500:       }
         .          .   1501:       if err != nil {
         .          .   1502:           log.Print(err)
         .          .   1503:
         .          .   1504:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1505:           return
         .          .   1506:       }
         .          .   1507:   }
         .          .   1508:   if targetItem.Status != ItemStatusOnSale {
         .          .   1509:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .          .   1510:       return
         .          .   1511:   }
         .          .   1512:
         .          .   1513:   seller := User{}
         .      1.95s   1514:   err = dbx.Get(&seller, "SELECT * FROM users WHERE id = ?", targetItem.SellerID)
         .          .   1515:   if err == sql.ErrNoRows {
         .          .   1516:       outputErrorMsg(w, http.StatusNotFound, "seller not found")
         .          .   1517:       return
         .          .   1518:   }
         .          .   1519:   if err != nil {
         .          .   1520:       log.Print(err)
         .          .   1521:
         .          .   1522:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1523:       return
         .          .   1524:   }
         .          .   1525:
         .       30ms   1526:   scr, err := APIShipmentCreate(getShipmentServiceURL(), &APIShipmentCreateReq{
         .          .   1527:       ToAddress:   buyer.Address,
         .          .   1528:       ToName:      buyer.AccountName,
         .          .   1529:       FromAddress: seller.Address,
         .          .   1530:       FromName:    seller.AccountName,
         .          .   1531:   })
         .          .   1532:   if err != nil {
         .          .   1533:       log.Print(err)
         .          .   1534:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
         .          .   1535:
         .          .   1536:       return
         .          .   1537:   }
         .          .   1538:
         .      2.01s   1539:   tx := dbx.MustBegin()
         .          .   1540:
         .          .   1541:   // get exclusive-lock
         .       20ms   1542:   err = tx.Get(&targetItem, "SELECT * FROM items WHERE id = ? FOR UPDATE", targetItem.ID)
         .          .   1543:   if err == sql.ErrNoRows {
         .          .   1544:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1545:       tx.Rollback()
         .          .   1546:       return
         .          .   1547:   }
         .          .   1548:   if err != nil {
         .          .   1549:       log.Print(err)
         .          .   1550:
         .          .   1551:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1552:       tx.Rollback()
         .          .   1553:       return
         .          .   1554:   }
         .          .   1555:
         .          .   1556:   if targetItem.Status != ItemStatusOnSale {
         .          .   1557:       outputErrorMsg(w, http.StatusForbidden, "item is not for sale")
         .       10ms   1558:       tx.Rollback()
         .          .   1559:       return
         .          .   1560:   }
         .          .   1561:
         .          .   1562:   if targetItem.SellerID == buyer.ID {
         .          .   1563:       outputErrorMsg(w, http.StatusForbidden, "自分の商品は買えません")
ROUTINE ======================== main.postComplete in /home/isucon/isucari/webapp/go/main.go
         0       60ms (flat, cum)  0.39% of Total
         .          .   1958:   }
         .          .   1959:
         .          .   1960:   csrfToken := reqpc.CSRFToken
         .          .   1961:   itemID := reqpc.ItemID
         .          .   1962:
         .       20ms   1963:   if csrfToken != getCSRFToken(r) {
         .          .   1964:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1965:
         .          .   1966:       return
         .          .   1967:   }
         .          .   1968:
         .          .   1969:   buyer, errCode, errMsg := getUser(r)
         .          .   1970:   if errMsg != "" {
         .          .   1971:       outputErrorMsg(w, errCode, errMsg)
         .          .   1972:       return
         .          .   1973:   }
         .          .   1974:
         .          .   1975:   transactionEvidence := TransactionEvidence{}
         .       20ms   1976:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1977:   if err == sql.ErrNoRows {
         .          .   1978:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1979:       return
         .          .   1980:   }
         .          .   1981:   if err != nil {
         .          .   1982:       log.Print(err)
         .          .   1983:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1984:
         .          .   1985:       return
         .          .   1986:   }
         .          .   1987:
         .          .   1988:   if transactionEvidence.BuyerID != buyer.ID {
         .          .   1989:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1990:       return
         .          .   1991:   }
         .          .   1992:
         .          .   1993:   tx := dbx.MustBegin()
         .          .   1994:   item := Item{}
         .       20ms   1995:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1996:   if err == sql.ErrNoRows {
         .          .   1997:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1998:       tx.Rollback()
         .          .   1999:       return
         .          .   2000:   }
ROUTINE ======================== main.postLogin in /home/isucon/isucari/webapp/go/main.go
         0      3.94s (flat, cum) 25.44% of Total
         .          .   2397:       outputErrorMsg(w, http.StatusBadRequest, "all parameters are required")
         .          .   2398:
         .          .   2399:       return
         .          .   2400:   }
         .          .   2401:
         .       10ms   2402:   u := User{}
         .      2.79s   2403:   err = dbx.Get(&u, "SELECT * FROM users WHERE account_name = ?", accountName)
         .          .   2404:   if err == sql.ErrNoRows {
         .          .   2405:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2406:       return
         .          .   2407:   }
         .          .   2408:   if err != nil {
         .          .   2409:       log.Print(err)
         .          .   2410:
         .          .   2411:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2412:       return
         .          .   2413:   }
         .          .   2414:   hashedPassword := u.HashedPassword
         .       10ms   2415:   var rehashedPassword []byte
         .          .   2416:   var rehashed bool
         .       50ms   2417:   if err := dbx.Get(&rehashedPassword, "SELECT hashed_password FROM user_password WHERE user_id = ?", u.ID); err != nil && err != sql.ErrNoRows {
         .          .   2418:       log.Print(err)
         .          .   2419:
         .          .   2420:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2421:       return
         .          .   2422:   } else if err == nil {
         .          .   2423:       rehashed = true
         .          .   2424:       hashedPassword = rehashedPassword
         .          .   2425:   }
         .          .   2426:
         .      1.03s   2427:   err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
         .          .   2428:   if err == bcrypt.ErrMismatchedHashAndPassword {
         .          .   2429:       outputErrorMsg(w, http.StatusUnauthorized, "アカウント名かパスワードが間違えています")
         .          .   2430:       return
         .          .   2431:   }
         .          .   2432:   if err != nil {
         .          .   2433:       log.Print(err)
         .          .   2434:
         .          .   2435:       outputErrorMsg(w, http.StatusInternalServerError, "crypt error")
         .          .   2436:       return
         .          .   2437:   }
         .          .   2438:
         .          .   2439:   if !rehashed {
         .          .   2440:       rehashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
         .          .   2441:       if err != nil {
         .          .   2442:           log.Print(err)
         .          .   2443:
         .          .   2444:           outputErrorMsg(w, http.StatusInternalServerError, "error")
         .          .   2445:           return
         .          .   2446:       }
         .          .   2447:       if _, err := dbx.Exec(
         .          .   2448:           "INSERT INTO user_password (user_id, hashed_password) VALUES (?, ?) ON CONFLICT (user_id) DO UPDATE SET hashed_password = ?",
         .          .   2449:           u.ID,
         .          .   2450:           rehashedPassword,
         .          .   2451:           rehashedPassword,
         .          .   2452:       ); err != nil {
         .          .   2453:           log.Print(err)
         .          .   2454:
         .          .   2455:           outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2456:           return
         .          .   2457:       }
         .          .   2458:   }
         .          .   2459:
         .          .   2460:   session := getSession(r)
         .          .   2461:
         .          .   2462:   session.Values["user_id"] = u.ID
         .          .   2463:   session.Values["user"] = u
         .       10ms   2464:   session.Values["csrf_token"] = secureRandomStr(20)
         .       30ms   2465:   if err = session.Save(r, w); err != nil {
         .          .   2466:       log.Print(err)
         .          .   2467:
         .          .   2468:       outputErrorMsg(w, http.StatusInternalServerError, "session error")
         .          .   2469:       return
         .          .   2470:   }
         .          .   2471:
         .       10ms   2472:   w.Header().Set("Content-Type", "application/json;charset=utf-8")
         .          .   2473:   json.NewEncoder(w).Encode(u)
         .          .   2474:}
         .          .   2475:
         .          .   2476:func postRegister(w http.ResponseWriter, r *http.Request) {
         .          .   2477:   rr := reqRegister{}
ROUTINE ======================== main.postSell in /home/isucon/isucari/webapp/go/main.go
         0       80ms (flat, cum)  0.52% of Total
         .          .   2184:       log.Print(err)
         .          .   2185:       outputErrorMsg(w, http.StatusInternalServerError, "Saving image failed")
         .          .   2186:       return
         .          .   2187:   }
         .          .   2188:
         .       70ms   2189:   tx := dbx.MustBegin()
         .          .   2190:
         .          .   2191:   seller := User{}
         .          .   2192:   err = tx.Get(&seller, "SELECT * FROM users WHERE id = ? FOR UPDATE", user.ID)
         .          .   2193:   if err == sql.ErrNoRows {
         .          .   2194:       outputErrorMsg(w, http.StatusNotFound, "user not found")
         .          .   2195:       tx.Rollback()
         .          .   2196:       return
         .          .   2197:   }
         .          .   2198:   if err != nil {
         .          .   2199:       log.Print(err)
         .          .   2200:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   2201:       tx.Rollback()
         .          .   2202:       return
         .          .   2203:   }
         .          .   2204:
         .          .   2205:   var itemID int64
         .       10ms   2206:   if err := tx.Get(
         .          .   2207:       &itemID,
         .          .   2208:       "INSERT INTO items (seller_id, status, name, price, description,image_name,category_id) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id",
         .          .   2209:       seller.ID,
         .          .   2210:       ItemStatusOnSale,
         .          .   2211:       name,
ROUTINE ======================== main.postShip in /home/isucon/isucari/webapp/go/main.go
         0       20ms (flat, cum)  0.13% of Total
         .          .   1714:   }
         .          .   1715:
         .          .   1716:   tx := dbx.MustBegin()
         .          .   1717:
         .          .   1718:   item := Item{}
         .       20ms   1719:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1720:   if err == sql.ErrNoRows {
         .          .   1721:       outputErrorMsg(w, http.StatusNotFound, "item not found")
         .          .   1722:       tx.Rollback()
         .          .   1723:       return
         .          .   1724:   }
ROUTINE ======================== main.postShipDone in /home/isucon/isucari/webapp/go/main.go
         0       30ms (flat, cum)  0.19% of Total
         .          .   1812:   }
         .          .   1813:
         .          .   1814:   csrfToken := reqpsd.CSRFToken
         .          .   1815:   itemID := reqpsd.ItemID
         .          .   1816:
         .       10ms   1817:   if csrfToken != getCSRFToken(r) {
         .          .   1818:       outputErrorMsg(w, http.StatusUnprocessableEntity, "csrf token error")
         .          .   1819:
         .          .   1820:       return
         .          .   1821:   }
         .          .   1822:
         .          .   1823:   seller, errCode, errMsg := getUser(r)
         .          .   1824:   if errMsg != "" {
         .          .   1825:       outputErrorMsg(w, errCode, errMsg)
         .          .   1826:       return
         .          .   1827:   }
         .          .   1828:
         .          .   1829:   transactionEvidence := TransactionEvidence{}
         .          .   1830:   err = dbx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE item_id = ?", itemID)
         .          .   1831:   if err == sql.ErrNoRows {
         .          .   1832:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidence not found")
         .          .   1833:       return
         .          .   1834:   }
         .          .   1835:   if err != nil {
         .          .   1836:       log.Print(err)
         .          .   1837:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1838:
         .          .   1839:       return
         .          .   1840:   }
         .          .   1841:
         .          .   1842:   if transactionEvidence.SellerID != seller.ID {
         .          .   1843:       outputErrorMsg(w, http.StatusForbidden, "権限がありません")
         .          .   1844:       return
         .          .   1845:   }
         .          .   1846:
         .          .   1847:   tx := dbx.MustBegin()
         .          .   1848:
         .          .   1849:   item := Item{}
         .          .   1850:   err = tx.Get(&item, "SELECT * FROM items WHERE id = ? FOR UPDATE", itemID)
         .          .   1851:   if err == sql.ErrNoRows {
         .          .   1852:       outputErrorMsg(w, http.StatusNotFound, "items not found")
         .          .   1853:       tx.Rollback()
         .          .   1854:       return
         .          .   1855:   }
         .          .   1856:   if err != nil {
         .          .   1857:       log.Print(err)
         .          .   1858:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1859:       tx.Rollback()
         .          .   1860:       return
         .          .   1861:   }
         .          .   1862:
         .          .   1863:   if item.Status != ItemStatusTrading {
         .          .   1864:       outputErrorMsg(w, http.StatusForbidden, "商品が取引中ではありません")
         .          .   1865:       tx.Rollback()
         .          .   1866:       return
         .          .   1867:   }
         .          .   1868:
         .          .   1869:   err = tx.Get(&transactionEvidence, "SELECT * FROM transaction_evidences WHERE id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1870:   if err == sql.ErrNoRows {
         .          .   1871:       outputErrorMsg(w, http.StatusNotFound, "transaction_evidences not found")
         .          .   1872:       tx.Rollback()
         .          .   1873:       return
         .          .   1874:   }
         .          .   1875:   if err != nil {
         .          .   1876:       log.Print(err)
         .          .   1877:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1878:       tx.Rollback()
         .          .   1879:       return
         .          .   1880:   }
         .          .   1881:
         .          .   1882:   if transactionEvidence.Status != TransactionEvidenceStatusWaitShipping {
         .          .   1883:       outputErrorMsg(w, http.StatusForbidden, "準備ができていません")
         .          .   1884:       tx.Rollback()
         .          .   1885:       return
         .          .   1886:   }
         .          .   1887:
         .          .   1888:   shipping := Shipping{}
         .       10ms   1889:   err = tx.Get(&shipping, "SELECT * FROM shippings WHERE transaction_evidence_id = ? FOR UPDATE", transactionEvidence.ID)
         .          .   1890:   if err == sql.ErrNoRows {
         .          .   1891:       outputErrorMsg(w, http.StatusNotFound, "shippings not found")
         .          .   1892:       tx.Rollback()
         .          .   1893:       return
         .          .   1894:   }
         .          .   1895:   if err != nil {
         .          .   1896:       log.Print(err)
         .          .   1897:       outputErrorMsg(w, http.StatusInternalServerError, "db error")
         .          .   1898:       tx.Rollback()
         .          .   1899:       return
         .          .   1900:   }
         .          .   1901:
         .       10ms   1902:   ssr, err := APIShipmentStatus(getShipmentServiceURL(), &APIShipmentStatusReq{
         .          .   1903:       ReserveID: shipping.ReserveID,
         .          .   1904:   })
         .          .   1905:   if err != nil {
         .          .   1906:       log.Print(err)
         .          .   1907:       outputErrorMsg(w, http.StatusInternalServerError, "failed to request to shipment service")
ROUTINE ======================== main.secureRandomStr in /home/isucon/isucari/webapp/go/main.go
         0       10ms (flat, cum) 0.065% of Total
         .          .   2248:   json.NewEncoder(w).Encode(resSell{ID: itemID})
         .          .   2249:}
         .          .   2250:
         .          .   2251:func secureRandomStr(b int) string {
         .          .   2252:   k := make([]byte, b)
         .       10ms   2253:   if _, err := crand.Read(k); err != nil {
         .          .   2254:       panic(err)
         .          .   2255:   }
         .          .   2256:   return fmt.Sprintf("%x", k)
         .          .   2257:}
         .          .   2258:
mackee commented 1 year ago

status ASC

{"pass":true,"score":15310,"campaign":3,"language":"Go","messages":[]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
712 0 88 0 624 0 POST /buy 1.203 4.332 1727.444 2.426 4.276 0.000 49.000 33.346
463 0 443 0 20 0 GET /users/transactions.json 0.125 5.640 1301.301 2.811 4.820 0.000 6963.000 5494.117
3139 0 3133 0 6 0 GET /new_items/\d+.json 0.004 0.772 201.479 0.064 0.236 0.000 5728.000 5512.836
105 0 83 0 22 0 POST /ship_done 0.374 0.904 70.062 0.667 0.844 0.000 83.000 36.810
102 0 86 0 16 0 POST /ship 0.172 1.040 68.191 0.669 0.848 0.000 61.000 55.961
1571 0 1563 0 8 0 POST /login 0.004 0.240 65.291 0.042 0.124 73.000 110.000 96.974
78 0 77 0 1 0 POST /complete 0.004 0.824 59.972 0.769 0.812 0.000 34.000 33.564
8230 0 8230 0 0 0 GET /items/\d+.json 0.000 0.372 39.227 0.005 0.020 1060.000 2010.000 1223.393
342 0 342 0 0 0 GET /new_items.json 0.004 0.216 12.624 0.037 0.168 5623.000 6010.000 5792.789
589 0 589 0 0 0 GET /users/\d+.json 0.004 0.200 7.364 0.013 0.088 97.000 5320.000 3161.762
122 0 101 0 21 0 POST /sell 0.008 0.240 3.212 0.026 0.140 13.000 106.000 24.590
1 0 1 0 0 0 POST /initialize 2.052 2.052 2.052 2.052 2.052 31.000 31.000 31.000
1563 0 1563 0 0 0 GET /settings 0.000 0.012 1.032 0.001 0.004 850.000 876.000 860.702
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.192 1.016 0.018 0.108 52192.000 151874.000 75943.534
98 0 84 0 14 0 GET /transactions/\d+.png 0.004 0.084 0.380 0.004 0.020 33.000 636.000 534.582
13 0 13 0 0 0 POST /bump 0.004 0.108 0.292 0.022 0.108 90.000 92.000 91.000
10 0 3 0 7 0 POST /items/edit 0.000 0.160 0.192 0.019 0.160 58.000 93.000 68.300
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.008 0.008 0.008 0.008 0.008 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /reports.json 0.004 0.004 0.004 0.004 0.004 51449.000 51449.000 51449.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000
mackee commented 1 year ago

transacation.jsonでAPIShipment見ない

{"pass":true,"score":36260,"campaign":3,"language":"Go","messages":["購入後の商品を購入者が見ているのにtransaction_evidence_statusが返っていません (item_id: 53161)","購入後の商品を購入者が見ているのにtransaction_evidence_statusが返っていませ
ん (item_id: 53388)","購入後の商品を購入者が見ているのにtransaction_evidence_statusが返っていません (item_id: 53411)","購入後の商品を購入者が見ているのにtransaction_evidence_statusが返っていません (item_id: 53418)"]}
COUNT 1XX 2XX 3XX 4XX 5XX METHOD URI MIN MAX SUM AVG P95 MIN(BODY) MAX(BODY) AVG(BODY)
904 0 271 0 633 0 POST /buy 0.004 4.280 2148.615 2.377 4.228 0.000 49.000 33.217
3523 0 3522 0 1 0 GET /new_items/\d+.json 0.005 1.064 484.921 0.138 0.320 0.000 5739.000 5521.904
281 0 258 0 23 0 POST /ship 0.110 1.196 214.400 0.763 0.876 0.000 61.000 57.651
279 0 253 0 26 0 POST /ship_done 0.043 1.196 211.168 0.757 0.864 0.000 83.000 34.570
253 0 247 0 6 0 POST /complete 0.004 1.380 203.497 0.804 0.888 0.000 34.000 33.194
18513 0 18513 0 0 0 GET /items/\d+.json 0.000 0.812 134.680 0.007 0.052 1060.000 1978.000 1257.187
1571 0 1563 0 8 0 POST /login 0.000 0.240 81.684 0.052 0.140 73.000 110.000 96.901
2765 0 2765 0 0 0 GET /users/transactions.json 0.000 0.752 73.268 0.026 0.100 2628.000 8678.000 5972.218
1673 0 1673 0 0 0 GET /new_items.json 0.004 0.592 49.244 0.029 0.120 5606.000 6016.000 5781.352
2318 0 2318 0 0 0 GET /users/\d+.json 0.004 0.772 27.760 0.012 0.068 94.000 5310.000 2725.610
303 0 282 0 21 0 POST /sell 0.004 0.252 9.864 0.033 0.120 13.000 106.000 17.667
1 0 1 0 0 0 POST /initialize 2.088 2.088 2.088 2.088 2.088 31.000 31.000 31.000
272 0 258 0 14 0 GET /transactions/\d+.png 0.000 0.116 1.864 0.007 0.052 33.000 636.000 589.507
1563 0 1563 0 0 0 GET /settings 0.000 0.016 1.236 0.001 0.004 849.000 875.000 860.678
58 0 58 0 0 0 GET /upload/[0-9a-zA-Z]+.jpg 0.000 0.128 1.184 0.020 0.116 51615.000 141436.000 80344.259
13 0 13 0 0 0 POST /bump 0.004 0.108 0.380 0.029 0.108 90.000 91.000 90.769
14 0 7 0 7 0 POST /items/edit 0.000 0.112 0.292 0.021 0.112 58.000 93.000 75.357
1 0 1 0 0 0 GET /static/js/2.ff6e1067.chunk.js 0.008 0.008 0.008 0.008 0.008 149001.000 149001.000 149001.000
1 0 1 0 0 0 GET /reports.json 0.008 0.008 0.008 0.008 0.008 156125.000 156125.000 156125.000
1 0 1 0 0 0 GET /static/js/main.babc3d4d.chunk.js 0.004 0.004 0.004 0.004 0.004 17072.000 17072.000 17072.000
1 0 1 0 0 0 GET /static/js/runtime~main.a8a9905a.js 0.000 0.000 0.000 0.000 0.000 774.000 774.000 774.000
1 0 1 0 0 0 GET /static/css/main.19393e92.chunk.css 0.000 0.000 0.000 0.000 0.000 994.000 994.000 994.000