コード例 #1
0
ファイル: app.go プロジェクト: catatsuy/isucon5_qualifier
func render(w http.ResponseWriter, r *http.Request, status int, file string, data interface{}) {
	fmap := template.FuncMap{
		"getUser": func(id int) *User {
			return getUser(id)
		},
		"getCurrentUser": func() *User {
			return getCurrentUser(w, r)
		},
		"isFriend": func(id int) bool {
			return isFriend(w, r, id)
		},
		"prefectures": func() []string {
			return prefs
		},
		"substring": func(s string, l int) string {
			if len(s) > l {
				return s[:l]
			}
			return s
		},
		"split": strings.Split,
		"getEntry": func(id int) Entry {
			row := db.QueryRow(`SELECT entries.id, entries.user_id, private, entries.title, created_at FROM entries WHERE id=?`, id)
			var entryID, userID, private int
			var subject string
			var createdAt time.Time
			checkErr(row.Scan(&entryID, &userID, &private, &subject, &createdAt))
			return Entry{id, userID, private == 1, subject, "", createdAt}
		},
		"numComments": func(id int) int {
			return numComments(id)
		},
		"watch": func(name string) string {
			stopwatch.Watch(name)
			return ""
		},
	}
	tpl := template.Must(template.New(file).Funcs(fmap).ParseFiles(getTemplatePath(file), getTemplatePath("header.html")))
	w.WriteHeader(status)
	checkErr(tpl.Execute(w, data))
}
コード例 #2
0
ファイル: app.go プロジェクト: catatsuy/isucon5_qualifier
func ListEntries(w http.ResponseWriter, r *http.Request) {
	stopwatch.Reset("GET /diary/entries/{account_name}")
	defer stopwatch.Watch("finish GET /diary/entries/{account_name}")
	if !authenticated(w, r) {
		return
	}

	account := mux.Vars(r)["account_name"]
	owner := getUserFromAccount(w, account)
	var query string
	if permitted(w, r, owner.ID) {
		query = `SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? ORDER BY created_at DESC LIMIT 20`
	} else {
		query = `SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? AND private=0 ORDER BY created_at DESC LIMIT 20`
	}
	rows, err := db.Query(query, owner.ID)
	if err != sql.ErrNoRows {
		checkErr(err)
	}
	entries := make([]Entry, 0, 20)
	for rows.Next() {
		var id, userID, private int
		var body string
		var createdAt time.Time
		checkErr(rows.Scan(&id, &userID, &private, &body, &createdAt))
		entry := Entry{id, userID, private == 1, strings.SplitN(body, "\n", 2)[0], strings.SplitN(body, "\n", 2)[1], createdAt}
		entries = append(entries, entry)
	}
	rows.Close()

	markFootprint(w, r, owner.ID)

	w.WriteHeader(http.StatusOK)

	checkErr(EntriesMyTmpl(w, struct {
		Owner   *User
		Entries []Entry
		Myself  bool
	}{owner, entries, getCurrentUser(w, r).ID == owner.ID}))
}
コード例 #3
0
ファイル: app.go プロジェクト: catatsuy/isucon5_qualifier
func GetIndex(w http.ResponseWriter, r *http.Request) {
	stopwatch.Reset("GET /")
	defer stopwatch.Watch("finish GET /")
	if !authenticated(w, r) {
		return
	}

	stopwatch.Watch("After authorize")
	user := getCurrentUser(w, r)

	stopwatch.Watch("After getCurrentUser")

	prof := Profile{}
	row := db.QueryRow(`SELECT * FROM profiles WHERE user_id = ?`, user.ID)
	err := row.Scan(&prof.UserID, &prof.FirstName, &prof.LastName, &prof.Sex, &prof.Birthday, &prof.Pref, &prof.UpdatedAt)
	if err != sql.ErrNoRows {
		checkErr(err)
	}

	stopwatch.Watch("After Profile")

	rows, err := db.Query(`SELECT id, user_id, private, body, created_at FROM entries WHERE user_id = ? ORDER BY created_at LIMIT 5`, user.ID)
	if err != sql.ErrNoRows {
		checkErr(err)
	}

	entries := make([]Entry, 0, 5)
	for rows.Next() {
		var id, userID, private int
		var body string
		var createdAt time.Time
		checkErr(rows.Scan(&id, &userID, &private, &body, &createdAt))
		entries = append(entries, Entry{id, userID, private == 1, strings.SplitN(body, "\n", 2)[0], strings.SplitN(body, "\n", 2)[1], createdAt})
	}
	rows.Close()

	stopwatch.Watch("After Entry")

	rows, err = db.Query(`SELECT c.id AS id, c.entry_id AS entry_id, c.user_id AS user_id, c.summary, c.created_at AS created_at
FROM comments c
JOIN entries e ON c.entry_id = e.id
WHERE e.user_id = ?
ORDER BY c.created_at DESC
LIMIT 10`, user.ID)
	if err != sql.ErrNoRows {
		checkErr(err)
	}
	commentsForMe := make([]Comment, 0, 10)
	for rows.Next() {
		c := Comment{}
		checkErr(rows.Scan(&c.ID, &c.EntryID, &c.UserID, &c.Comment, &c.CreatedAt))
		commentsForMe = append(commentsForMe, c)
	}
	rows.Close()

	stopwatch.Watch("After Comment")

	relations := friendsForMe[user.ID]
	friendsMap := make(map[int]time.Time)
	friendIds := make([]string, 0)
	for _, r := range relations {
		friendID := r.anotherID
		if _, ok := friendsMap[friendID]; !ok {
			friendsMap[friendID] = r.createdAt
			friendIds = append(friendIds, strconv.Itoa(friendID))
		}
	}
	friends := make([]Friend, 0, len(friendsMap))
	for key, val := range friendsMap {
		friends = append(friends, Friend{key, val})
	}
	rows.Close()

	stopwatch.Watch("After Relation")

	entryIndex := ""
	if len(friendIds) > 20 {
		entryIndex = "created_at"
	} else {
		entryIndex = "user_id"
	}
	// fmt.Println(entryIndex)
	rows, err = db.Query(fmt.Sprintf(`SELECT id, user_id, private, title, created_at FROM entries FORCE INDEX(%s) WHERE user_id IN (%s) ORDER BY created_at DESC LIMIT 10`, entryIndex, strings.Join(friendIds, ",")))
	if err != sql.ErrNoRows {
		checkErr(err)
	}
	entriesOfFriends := make([]Entry, 0, 10)
	for rows.Next() {
		var id, userID, private int
		var subject string
		var createdAt time.Time
		checkErr(rows.Scan(&id, &userID, &private, &subject, &createdAt))
		if !isFriend2(friendsMap, userID) {
			continue
		}
		entriesOfFriends = append(entriesOfFriends, Entry{id, userID, private == 1, subject, "", createdAt})
		if len(entriesOfFriends) >= 10 {
			break
		}
	}
	rows.Close()

	stopwatch.Watch("After entries")

	// コメントを1000件取得し、privateであれば自分と相手がフレンドかどうかをチェックする
	commentIndex := ""
	if len(friendIds) > 20 {
		commentIndex = "created_at"
	} else {
		commentIndex = "user_id"
	}
	query := fmt.Sprintf(`SELECT c.id, c.entry_id, c.user_id, c.summary, c.created_at, e.id, e.user_id, e.private, e.title, e.created_at FROM comments c FORCE INDEX (%s) JOIN entries e ON c.entry_id = e.id WHERE c.user_id IN (%s) ORDER BY c.created_at DESC LIMIT 10`, commentIndex, strings.Join(friendIds, ","))
	// fmt.Println(query)
	rows, err = db.Query(query)
	if err != sql.ErrNoRows {
		checkErr(err)
	}
	commentsOfFriends := make([]CommentWithEntry, 0, 10)
	for rows.Next() {
		c := CommentWithEntry{}
		var id, userID, private int
		var subject string
		var createdAt time.Time
		checkErr(rows.Scan(&c.ID, &c.EntryID, &c.UserID, &c.Comment, &c.CreatedAt, &id, &userID, &private, &subject, &createdAt))
		if !isFriend2(friendsMap, c.UserID) {
			continue
		}
		entry := Entry{id, userID, private == 1, subject, "", createdAt}
		c.Entry = entry
		if entry.Private {
			if !permitted(w, r, entry.UserID) {
				continue
			}
		}
		commentsOfFriends = append(commentsOfFriends, c)
		if len(commentsOfFriends) >= 10 {
			break
		}
	}
	rows.Close()

	stopwatch.Watch("After Friend Comments")

	footprints, err := getFootprintsFromRedis(user.ID, 10)
	if err != nil {
		checkErr(err)
	}

	stopwatch.Watch("After Get Friend Comments")

	w.WriteHeader(http.StatusOK)

	checkErr(MyTmpl(w, struct {
		User              User
		Profile           Profile
		Entries           []Entry
		CommentsForMe     []Comment
		EntriesOfFriends  []Entry
		CommentsOfFriends []CommentWithEntry
		Friends           []Friend
		Footprints        []Footprint
	}{
		*user, prof, entries, commentsForMe, entriesOfFriends, commentsOfFriends, friends, footprints,
	}))
	stopwatch.Watch("After after render")
}
コード例 #4
0
ファイル: app.go プロジェクト: catatsuy/isucon5_final
func GetData(w http.ResponseWriter, r *http.Request) {
	stopwatch.Watch("GetData")
	user := getCurrentUser(w, r)
	if user == nil {
		w.WriteHeader(http.StatusForbidden)
		return
	}

	row := db.QueryRow(`SELECT arg FROM subscriptions WHERE user_id=$1`, user.ID)
	var argJson string
	checkErr(row.Scan(&argJson))
	var arg Arg
	checkErr(json.Unmarshal([]byte(argJson), &arg))

	data := make([]Data, 0, len(arg))
	var services []string
	for service, _ := range arg {
		services = append(services, `'`+service+`'`)
	}
	if len(services) == 0 {
		w.Header().Set("Content-Type", "application/json")
		body, err := json.Marshal(data)
		checkErr(err)
		w.Write(body)
		return
	}
	query := fmt.Sprintf(`SELECT meth, token_type, token_key, uri, service FROM endpoints WHERE service IN (%s)`, strings.Join(services, ","))

	rows, err := db.Query(query)
	defer rows.Close()
	checkErr(err)

	for rows.Next() {
		var method string
		var tokenType *string
		var tokenKey *string
		var uriTemplate *string
		var serv *string
		checkErr(rows.Scan(&method, &tokenType, &tokenKey, &uriTemplate, &serv))
		service := *serv

		conf, _ := arg[service]

		headers := make(map[string]string)
		params := conf.Params
		if params == nil {
			params = make(map[string]string)
		}

		if tokenType != nil && tokenKey != nil {
			switch *tokenType {
			case "header":
				headers[*tokenKey] = conf.Token
				break
			case "param":
				params[*tokenKey] = conf.Token
				break
			}
		}

		ks := make([]interface{}, len(conf.Keys))
		ks2 := make([]string, len(conf.Keys))
		for i, s := range conf.Keys {
			ks[i] = s
			ks2[i] = s
		}
		uri := fmt.Sprintf(*uriTemplate, ks...)

		stopwatch.Watch(service + " start")
		lc := logcache(time.Now())

		if service == "ken" {
			key := ks2[0]
			cache, ok := kenCache[key]
			if ok {
				data = append(data, cache)
				lc("hit", user.ID, service, key)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				kenCache[key] = d
				data = append(data, d)
				lc("miss", user.ID, service, key)
			}
		} else if service == "ken2" {
			q, _ := params["zipcode"]
			cache, ok := ken2Cache[q]
			if ok {
				data = append(data, cache)
				lc("hit", user.ID, service, q)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				ken2Cache[q] = d
				data = append(data, d)
				lc("miss", user.ID, service, q)
			}
		} else if service == "surname" {
			q, _ := params["q"]
			cache, ok := surnameCache[q]
			if ok {
				data = append(data, cache)
				lc("hit", user.ID, service, q)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				surnameCache[q] = d
				data = append(data, d)
				lc("miss", user.ID, service, q)
			}
		} else if service == "givenname" {
			q, _ := params["q"]
			cache, ok := givennameCache[q]
			if ok {
				data = append(data, cache)
				lc("hit", user.ID, service, q)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				givennameCache[q] = d
				data = append(data, d)
				lc("miss", user.ID, service, q)
			}
		} else if service == "tenki" {
			q, _ := params["zipcode"]
			cache, found := tenkiCache.Get(q)
			if found {
				data = append(data, cache.(Data))
				lc("hit", user.ID, service, q)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				tenkiCache.Set(q, d, 2*time.Second)
				data = append(data, d)
				lc("miss", user.ID, service, q)
			}
		} else if service == "perfectsec_attacked" {
			key := fmt.Sprintf("pa_%s", headers["X-PERFECT-SECURITY-TOKEN"])
			cache, found := exCache.Get(key)
			if found {
				data = append(data, cache.(Data))
				lc("hit", user.ID, service, key)
			} else {
				d := Data{service, fetchApi(method, uri, headers, params)}
				exCache.Set(key, d, 10*time.Second)
				data = append(data, d)
				lc("miss", user.ID, service, key)
			}
		} else {
			data = append(data, Data{service, fetchApi(method, uri, headers, params)})
			lc("uncached", user.ID, service, "-")
		}
		stopwatch.Watch(service + " finish")
	}

	w.Header().Set("Content-Type", "application/json")
	body, err := json.Marshal(data)
	checkErr(err)
	w.Write(body)
}