Open tetsuzawa opened 1 year ago
初期スコア
2023/01/14 01:55:07 main.go:212: 2510 0
{"pass":true,"score":2510,"campaign":0,"language":"Go","messages":[]}
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 |
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)
.
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: }
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 |
pgx-replace + category cache
{"pass":true,"score":3510,"campaign":0,"language":"Go","messages":[]}
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 |
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 |
{"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")
category cache
{"pass":true,"score":11370,"campaign":2,"language":"Go","messages":[]}
{"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 {
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 |
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 |
{"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 |
$ 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: }
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 |
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 |
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 |
{"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 |
$ 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:
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 |
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 |
ベンチマーク実行方法