diff --git a/backend/dbtools/cache_handler.go b/backend/dbtools/cache_handler.go index a4731ea2..e1074935 100644 --- a/backend/dbtools/cache_handler.go +++ b/backend/dbtools/cache_handler.go @@ -1,54 +1,68 @@ package dbtools import ( - "backend/enum" "backend/structs" + "sync" +) + +type QueryState int + +const ( + None QueryState = iota + Working + Completed ) type QueryCache struct { - Store []Query + HashStore sync.Map // [structs.LeaderboardPayload]Query } type Query struct { - Filter structs.LeaderboardPayload DataPositions []int + Status QueryState } // AddQuery - Adds a query to the cache. func (q *QueryCache) AddQuery(query structs.LeaderboardPayload, dataPositions []int) { - q.Store = append(q.Store, Query{Filter: query, DataPositions: dataPositions}) + query.Start, query.Stop = 0, 0 + q.HashStore.Store(query, Query{dataPositions, Completed}) } -// CheckQuery - Checks if the query has been run before, if so, return the data positions. -// A true value indicates that the query has been run before. -// A false value indicates that the query has not been run before. -// And a false value with a non-nil slice indicates that a similar query has been run before and the data positions are returned that should be used to filer from. -// Hoping that the last case makes things a bit faster to reduce having to run multiple startup caching queries. -func (q *QueryCache) CheckQuery(query structs.LeaderboardPayload) (bool, []int) { - for _, cacheQuery := range q.Store { - if cacheQuery.Filter == query { - return true, cacheQuery.DataPositions - } - // checks for exact match - if cacheQuery.Filter.SortBy == query.SortBy && cacheQuery.Filter.Federation == query.Federation && cacheQuery.Filter.WeightClass == query.WeightClass && cacheQuery.Filter.Year == query.Year { - return true, cacheQuery.DataPositions +func (q *QueryCache) InitQuery(query structs.LeaderboardPayload) { + q.HashStore.Store(query, Query{ + DataPositions: nil, + Status: Working, + }) +} + +func (q *QueryCache) QueryStatus(query structs.LeaderboardPayload) QueryState { + query.Start, query.Stop = 0, 0 + queryStuff, ok := q.HashStore.Load(query) + if !ok { + return None + } else { + query, ok := queryStuff.(Query) + if !ok { + panic("how the fuck did you fuck this up?") } + return query.Status } - // if we get here, we haven't found a match, so we'll do some partial matching - for _, cacheQuery := range q.Store { - // all years for the same total/sinclair, federation and weight class - if cacheQuery.Filter.SortBy == query.SortBy && cacheQuery.Filter.Federation == query.Federation && cacheQuery.Filter.WeightClass == query.WeightClass && cacheQuery.Filter.Year == enum.AllYearsStr { - return false, cacheQuery.DataPositions - } - // all years for the same total/sinclair, federation, and all gendered weight classes - if cacheQuery.Filter.SortBy == query.SortBy && cacheQuery.Filter.Federation == query.Federation && cacheQuery.Filter.Year == enum.AllYearsStr { - if query.WeightClass[0] == 'M' && cacheQuery.Filter.WeightClass == "MALL" { - return false, cacheQuery.DataPositions - } - if query.WeightClass[0] == 'F' && cacheQuery.Filter.WeightClass == "FALL" { - return false, cacheQuery.DataPositions - } +} + +// CheckQuery - Checks if the query has been run before, if so, return the query state and data positions if they exist +func (q *QueryCache) CheckQuery(query structs.LeaderboardPayload) (state QueryState, positions []int) { + query.Start, query.Stop = 0, 0 + loadedData, ok := q.HashStore.Load(query) + if ok { + storedQuery, ok := loadedData.(Query) + if ok && (storedQuery.Status == Completed) || (storedQuery.Status == Working) { + state = storedQuery.Status + positions = storedQuery.DataPositions + return } } - return false, nil + + state = None + positions = []int{} + return state, positions } diff --git a/backend/dbtools/cache_handler_test.go b/backend/dbtools/cache_handler_test.go deleted file mode 100644 index 27fb62ea..00000000 --- a/backend/dbtools/cache_handler_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package dbtools - -import ( - "backend/structs" - "testing" -) - -func TestQueryCache_AddQuery(t *testing.T) { - type fields struct { - Store []Query - } - type args struct { - query structs.LeaderboardPayload - dataPositions []int - } - tests := []struct { - name string - fields fields - args args - }{ - {"AddQuery", fields{Store: []Query{}}, args{query: structs.LeaderboardPayload{}, dataPositions: []int{1}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - q := &QueryCache{ - Store: tt.fields.Store, - } - q.AddQuery(tt.args.query, tt.args.dataPositions) - }) - } -} - -func TestQueryCache_CheckQuery(t *testing.T) { - type fields struct { - Store []Query - } - type args struct { - query structs.LeaderboardPayload - } - tests := []struct { - name string - fields fields - args args - want bool - want1 []int - }{ - {"CheckQuery", fields{Store: []Query{{Filter: structs.LeaderboardPayload{SortBy: "total", Federation: "IPF", WeightClass: "93", StartDate: "2018-01-01", EndDate: "2018-01-01"}, DataPositions: []int{1}}}}, args{query: structs.LeaderboardPayload{SortBy: "total", Federation: "IPF", WeightClass: "93", StartDate: "2018-01-01", EndDate: "2018-01-01"}}, true, []int{1}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - q := &QueryCache{ - Store: tt.fields.Store, - } - got, got1 := q.CheckQuery(tt.args.query) - if got != tt.want { - t.Errorf("QueryCache.CheckQuery() got = %v, want %v", got, tt.want) - } - if len(got1) != len(tt.want1) { - t.Errorf("QueryCache.CheckQuery() got1 = %v, want %v", got1, tt.want1) - } - }) - } -} diff --git a/backend/dbtools/sortby.go b/backend/dbtools/sortby.go index ac11af2c..e2ee2315 100644 --- a/backend/dbtools/sortby.go +++ b/backend/dbtools/sortby.go @@ -10,15 +10,27 @@ import ( // FilterLifts - Returns a slice of structs relating to the selected filter selection func FilterLifts(bigData []structs.Entry, filterQuery structs.LeaderboardPayload, weightCat structs.WeightClass, cache *QueryCache) (filteredData structs.LeaderboardResponse) { - exists, positions := cache.CheckQuery(filterQuery) - - if exists { + queryState, positions := cache.CheckQuery(filterQuery) + + switch queryState { + case None: + cache.InitQuery(filterQuery) + case Working: + state := cache.QueryStatus(filterQuery) + for state == Working { + time.Sleep(100 * time.Millisecond) + state = cache.QueryStatus(filterQuery) + if state == Completed { + filteredData.Data, filteredData.Size = fetchLifts(&bigData, positions, &filterQuery) + return + } + } + case Completed: filteredData.Data, filteredData.Size = fetchLifts(&bigData, positions, &filterQuery) return - } - - if !exists && positions != nil { - bigData = fetchLiftsAll(&bigData, positions) + default: + // if you hit this, fuck you + panic("Invalid query state") } var names []string @@ -50,6 +62,27 @@ func FilterLifts(bigData []structs.Entry, filterQuery structs.LeaderboardPayload } func PreCacheFilter(bigData []structs.Entry, filterQuery structs.LeaderboardPayload, weightCat structs.WeightClass, cache *QueryCache) { + queryState, _ := cache.CheckQuery(filterQuery) + + switch queryState { + case None: + cache.InitQuery(filterQuery) + case Working: + state := cache.QueryStatus(filterQuery) + for state == Working { + time.Sleep(100 * time.Millisecond) + state = cache.QueryStatus(filterQuery) + if state == Completed { + return + } + } + case Completed: + return + default: + // if you hit this, fuck you + panic("Invalid query state") + } + var names []string var liftPtr *structs.Entry var liftPositions []int @@ -84,13 +117,6 @@ func fetchLifts(bigData *[]structs.Entry, pos []int, query *structs.LeaderboardP return } -func fetchLiftsAll(bigData *[]structs.Entry, pos []int) (lifts []structs.Entry) { - for _, p := range pos { - lifts = append(lifts, (*bigData)[p]) - } - return -} - // SortSinclair Descending order by entry sinclair func SortSinclair(sliceStructs []structs.Entry) { sort.Slice(sliceStructs, func(i, j int) bool { diff --git a/backend/endpoints.go b/backend/endpoints.go index 006a98e0..1ef14872 100644 --- a/backend/endpoints.go +++ b/backend/endpoints.go @@ -175,7 +175,7 @@ func LifterHistory(c *gin.Context) { // @Success 200 {object} structs.LeaderboardResponse // @Router /leaderboard [post] func Leaderboard(c *gin.Context) { - sortby, exists := c.GetQuery("sortby") + sortby, exists := c.GetQuery("sortBy") if !exists { sortby = "total" }