Move to fasthttp for improved performance

This commit is contained in:
Morgan Bazalgette
2017-02-02 13:40:28 +01:00
parent ace2fded7e
commit 85e6dc7e5e
26 changed files with 448 additions and 380 deletions

View File

@@ -4,32 +4,32 @@ import (
"strconv"
"strings"
"zxq.co/ripple/rippleapi/common"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/thehowl/go-osuapi"
"github.com/valyala/fasthttp"
"zxq.co/ripple/rippleapi/common"
)
// GetBeatmap retrieves general beatmap information.
func GetBeatmap(c *gin.Context, db *sqlx.DB) {
func GetBeatmap(c *fasthttp.RequestCtx, db *sqlx.DB) {
var whereClauses []string
var params []interface{}
limit := strconv.Itoa(common.InString(1, c.Query("limit"), 500, 500))
limit := strconv.Itoa(common.InString(1, query(c, "limit"), 500, 500))
// since value is not stored, silently ignore
if c.Query("s") != "" {
if query(c, "s") != "" {
whereClauses = append(whereClauses, "beatmaps.beatmapset_id = ?")
params = append(params, c.Query("s"))
params = append(params, query(c, "s"))
}
if c.Query("b") != "" {
if query(c, "b") != "" {
whereClauses = append(whereClauses, "beatmaps.beatmap_id = ?")
params = append(params, c.Query("b"))
params = append(params, query(c, "b"))
// b is unique, so change limit to 1
limit = "1"
}
// creator is not stored, silently ignore u and type
if c.Query("m") != "" {
m := genmode(c.Query("m"))
if query(c, "m") != "" {
m := genmode(query(c, "m"))
if m == "std" {
// Since STD beatmaps are converted, all of the diffs must be != 0
for _, i := range modes {
@@ -37,14 +37,14 @@ func GetBeatmap(c *gin.Context, db *sqlx.DB) {
}
} else {
whereClauses = append(whereClauses, "beatmaps.difficulty_"+m+" != 0")
if c.Query("a") == "1" {
if query(c, "a") == "1" {
whereClauses = append(whereClauses, "beatmaps.difficulty_std = 0")
}
}
}
if c.Query("h") != "" {
if query(c, "h") != "" {
whereClauses = append(whereClauses, "beatmaps.beatmap_md5 = ?")
params = append(params, c.Query("h"))
params = append(params, query(c, "h"))
}
where := strings.Join(whereClauses, " AND ")
@@ -61,8 +61,8 @@ func GetBeatmap(c *gin.Context, db *sqlx.DB) {
FROM beatmaps `+where+" ORDER BY id DESC LIMIT "+limit,
params...)
if err != nil {
c.Error(err)
c.JSON(200, defaultResponse)
common.Err(c, err)
json(c, 200, defaultResponse)
return
}
@@ -82,7 +82,7 @@ FROM beatmaps `+where+" ORDER BY id DESC LIMIT "+limit,
&rawLastUpdate,
)
if err != nil {
c.Error(err)
common.Err(c, err)
continue
}
bm.TotalLength = bm.HitLength
@@ -103,7 +103,7 @@ FROM beatmaps `+where+" ORDER BY id DESC LIMIT "+limit,
bms = append(bms, bm)
}
c.JSON(200, bms)
json(c, 200, bms)
}
var rippleToOsuRankedStatus = map[int]osuapi.ApprovedStatus{

View File

@@ -2,12 +2,12 @@ package peppy
import (
"database/sql"
_json "encoding/json"
"strconv"
"zxq.co/ripple/rippleapi/common"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/valyala/fasthttp"
"zxq.co/ripple/rippleapi/common"
)
var modes = []string{"std", "taiko", "ctb", "mania"}
@@ -30,25 +30,25 @@ func rankable(m string) bool {
return x == 0 || x == 3
}
func genUser(c *gin.Context, db *sqlx.DB) (string, string) {
func genUser(c *fasthttp.RequestCtx, db *sqlx.DB) (string, string) {
var whereClause string
var p string
// used in second case of switch
s, err := strconv.Atoi(c.Query("u"))
s, err := strconv.Atoi(query(c, "u"))
switch {
// We know for sure that it's an username.
case c.Query("type") == "string":
case query(c, "type") == "string":
whereClause = "users.username_safe = ?"
p = common.SafeUsername(c.Query("u"))
p = common.SafeUsername(query(c, "u"))
// It could be an user ID, so we look for an user with that username first.
case err == nil:
err = db.QueryRow("SELECT id FROM users WHERE id = ? LIMIT 1", s).Scan(&p)
if err == sql.ErrNoRows {
// If no user with that userID were found, assume username.
whereClause = "users.username_safe = ?"
p = common.SafeUsername(c.Query("u"))
p = common.SafeUsername(query(c, "u"))
} else {
// An user with that userID was found. Thus it's an userID.
whereClause = "users.id = ?"
@@ -56,7 +56,20 @@ func genUser(c *gin.Context, db *sqlx.DB) (string, string) {
// u contains letters, so it's an username.
default:
whereClause = "users.username_safe = ?"
p = common.SafeUsername(c.Query("u"))
p = common.SafeUsername(query(c, "u"))
}
return whereClause, p
}
func query(c *fasthttp.RequestCtx, s string) string {
return string(c.QueryArgs().Peek(s))
}
func json(c *fasthttp.RequestCtx, code int, data interface{}) {
c.SetStatusCode(code)
d, err := _json.Marshal(data)
if err != nil {
panic(err)
}
c.Write(d)
}

View File

@@ -2,11 +2,11 @@
package peppy
import (
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/valyala/fasthttp"
)
// GetMatch retrieves general match information.
func GetMatch(c *gin.Context, db *sqlx.DB) {
c.JSON(200, defaultResponse)
func GetMatch(c *fasthttp.RequestCtx, db *sqlx.DB) {
json(c, 200, defaultResponse)
}

View File

@@ -7,43 +7,43 @@ import (
"zxq.co/ripple/rippleapi/common"
"zxq.co/x/getrank"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/valyala/fasthttp"
"gopkg.in/thehowl/go-osuapi.v1"
"zxq.co/x/getrank"
)
// GetScores retrieve information about the top 100 scores of a specified beatmap.
func GetScores(c *gin.Context, db *sqlx.DB) {
if c.Query("b") == "" {
c.JSON(200, defaultResponse)
func GetScores(c *fasthttp.RequestCtx, db *sqlx.DB) {
if query(c, "b") == "" {
json(c, 200, defaultResponse)
return
}
var beatmapMD5 string
err := db.Get(&beatmapMD5, "SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = ? LIMIT 1", c.Query("b"))
err := db.Get(&beatmapMD5, "SELECT beatmap_md5 FROM beatmaps WHERE beatmap_id = ? LIMIT 1", query(c, "b"))
switch {
case err == sql.ErrNoRows:
c.JSON(200, defaultResponse)
json(c, 200, defaultResponse)
return
case err != nil:
c.Error(err)
c.JSON(200, defaultResponse)
common.Err(c, err)
json(c, 200, defaultResponse)
return
}
var sb = "scores.score"
if rankable(c.Query("m")) {
if rankable(query(c, "m")) {
sb = "scores.pp"
}
var (
extraWhere string
extraParams []interface{}
)
if c.Query("u") != "" {
if query(c, "u") != "" {
w, p := genUser(c, db)
extraWhere = "AND " + w
extraParams = append(extraParams, p)
}
mods := common.Int(c.Query("mods"))
mods := common.Int(query(c, "mods"))
rows, err := db.Query(`
SELECT
scores.id, scores.score, users.username, scores.300_count, scores.100_count,
@@ -58,11 +58,11 @@ WHERE scores.completed = '3'
AND scores.play_mode = ?
AND scores.mods & ? = ?
`+extraWhere+`
ORDER BY `+sb+` DESC LIMIT `+strconv.Itoa(common.InString(1, c.Query("limit"), 100, 50)),
append([]interface{}{beatmapMD5, genmodei(c.Query("m")), mods, mods}, extraParams...)...)
ORDER BY `+sb+` DESC LIMIT `+strconv.Itoa(common.InString(1, query(c, "limit"), 100, 50)),
append([]interface{}{beatmapMD5, genmodei(query(c, "m")), mods, mods}, extraParams...)...)
if err != nil {
c.Error(err)
c.JSON(200, defaultResponse)
common.Err(c, err)
json(c, 200, defaultResponse)
return
}
var results []osuapi.GSScore
@@ -82,17 +82,17 @@ ORDER BY `+sb+` DESC LIMIT `+strconv.Itoa(common.InString(1, c.Query("limit"), 1
)
if err != nil {
if err != sql.ErrNoRows {
c.Error(err)
common.Err(c, err)
}
continue
}
s.FullCombo = osuapi.OsuBool(fullcombo)
s.Mods = osuapi.Mods(mods)
s.Date = osuapi.MySQLDate(date)
s.Rank = strings.ToUpper(getrank.GetRank(osuapi.Mode(genmodei(c.Query("m"))), s.Mods,
s.Rank = strings.ToUpper(getrank.GetRank(osuapi.Mode(genmodei(query(c, "m"))), s.Mods,
accuracy, s.Count300, s.Count100, s.Count50, s.CountMiss))
results = append(results, s)
}
c.JSON(200, results)
json(c, 200, results)
return
}

View File

@@ -5,23 +5,24 @@ import (
"database/sql"
"fmt"
"zxq.co/ripple/ocl"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"github.com/thehowl/go-osuapi"
"github.com/valyala/fasthttp"
"zxq.co/ripple/ocl"
"zxq.co/ripple/rippleapi/common"
)
// GetUser retrieves general user information.
func GetUser(c *gin.Context, db *sqlx.DB) {
if c.Query("u") == "" {
c.JSON(200, defaultResponse)
func GetUser(c *fasthttp.RequestCtx, db *sqlx.DB) {
if query(c, "u") == "" {
json(c, 200, defaultResponse)
return
}
var user osuapi.User
whereClause, p := genUser(c, db)
whereClause = "WHERE " + whereClause
mode := genmode(c.Query("m"))
mode := genmode(query(c, "m"))
var lbpos *int
err := db.QueryRow(fmt.Sprintf(
@@ -43,9 +44,9 @@ func GetUser(c *gin.Context, db *sqlx.DB) {
&user.Country,
)
if err != nil {
c.JSON(200, defaultResponse)
json(c, 200, defaultResponse)
if err != sql.ErrNoRows {
c.Error(err)
common.Err(c, err)
}
return
}
@@ -54,5 +55,5 @@ func GetUser(c *gin.Context, db *sqlx.DB) {
}
user.Level = ocl.GetLevelPrecise(user.TotalScore)
c.JSON(200, []osuapi.User{user})
json(c, 200, []osuapi.User{user})
}

View File

@@ -4,32 +4,32 @@ import (
"fmt"
"strings"
"github.com/jmoiron/sqlx"
"github.com/valyala/fasthttp"
"gopkg.in/thehowl/go-osuapi.v1"
"zxq.co/ripple/rippleapi/common"
"zxq.co/x/getrank"
"github.com/gin-gonic/gin"
"github.com/jmoiron/sqlx"
"gopkg.in/thehowl/go-osuapi.v1"
)
// GetUserRecent retrieves an user's recent scores.
func GetUserRecent(c *gin.Context, db *sqlx.DB) {
getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, c.Query("limit"), 50, 10))
func GetUserRecent(c *fasthttp.RequestCtx, db *sqlx.DB) {
getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, query(c, "limit"), 50, 10))
}
// GetUserBest retrieves an user's best scores.
func GetUserBest(c *gin.Context, db *sqlx.DB) {
func GetUserBest(c *fasthttp.RequestCtx, db *sqlx.DB) {
var sb string
if rankable(c.Query("m")) {
if rankable(query(c, "m")) {
sb = "scores.pp"
} else {
sb = "scores.score"
}
getUserX(c, db, "AND completed = '3' ORDER BY "+sb+" DESC", common.InString(1, c.Query("limit"), 100, 10))
getUserX(c, db, "AND completed = '3' ORDER BY "+sb+" DESC", common.InString(1, query(c, "limit"), 100, 10))
}
func getUserX(c *gin.Context, db *sqlx.DB, orderBy string, limit int) {
func getUserX(c *fasthttp.RequestCtx, db *sqlx.DB, orderBy string, limit int) {
whereClause, p := genUser(c, db)
query := fmt.Sprintf(
sqlQuery := fmt.Sprintf(
`SELECT
beatmaps.beatmap_id, scores.score, scores.max_combo,
scores.300_count, scores.100_count, scores.50_count,
@@ -44,11 +44,11 @@ func getUserX(c *gin.Context, db *sqlx.DB, orderBy string, limit int) {
LIMIT %d`, whereClause, orderBy, limit,
)
scores := make([]osuapi.GUSScore, 0, limit)
m := genmodei(c.Query("m"))
rows, err := db.Query(query, p, m)
m := genmodei(query(c, "m"))
rows, err := db.Query(sqlQuery, p, m)
if err != nil {
c.JSON(200, defaultResponse)
c.Error(err)
json(c, 200, defaultResponse)
common.Err(c, err)
return
}
for rows.Next() {
@@ -68,8 +68,8 @@ func getUserX(c *gin.Context, db *sqlx.DB, orderBy string, limit int) {
&curscore.PP, &acc,
)
if err != nil {
c.JSON(200, defaultResponse)
c.Error(err)
json(c, 200, defaultResponse)
common.Err(c, err)
return
}
if bid == nil {
@@ -91,5 +91,5 @@ func getUserX(c *gin.Context, db *sqlx.DB, orderBy string, limit int) {
))
scores = append(scores, curscore)
}
c.JSON(200, scores)
json(c, 200, scores)
}