blob: c41afc735bc6b2e768b167e885b41c34fc9045e9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package store
import (
"context"
"sort"
"strconv"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/core/resources/model"
"github.com/apache/dubbo-kubernetes/pkg/core/resources/registry"
)
// The Pagination Store is handling only the pagination functionality in the List.
// This is an in-memory operation and offloads this from the persistent stores (k8s, postgres etc.)
// Two reasons why this is needed:
// * There is no filtering + pagination on the native K8S database
// * On Postgres, we keep the object in a column as a string. We would have to use JSON column type and convert it to native SQL queries.
//
// The in-memory filtering has been tested with 10,000 Dataplanes and proved to be fast enough, although not that efficient.
func NewPaginationStore(delegate ResourceStore) ResourceStore {
return &paginationStore{
delegate: delegate,
}
}
type paginationStore struct {
delegate ResourceStore
}
func (p *paginationStore) Create(ctx context.Context, resource model.Resource, optionsFunc ...CreateOptionsFunc) error {
return p.delegate.Create(ctx, resource, optionsFunc...)
}
func (p *paginationStore) Update(ctx context.Context, resource model.Resource, optionsFunc ...UpdateOptionsFunc) error {
return p.delegate.Update(ctx, resource, optionsFunc...)
}
func (p *paginationStore) Delete(ctx context.Context, resource model.Resource, optionsFunc ...DeleteOptionsFunc) error {
return p.delegate.Delete(ctx, resource, optionsFunc...)
}
func (p *paginationStore) Get(ctx context.Context, resource model.Resource, optionsFunc ...GetOptionsFunc) error {
return p.delegate.Get(ctx, resource, optionsFunc...)
}
func (p *paginationStore) List(ctx context.Context, list model.ResourceList, optionsFunc ...ListOptionsFunc) error {
opts := NewListOptions(optionsFunc...)
// At least one of the following options is required to trigger the paginationStore to do work.
// Otherwise, it delegates the request and returns early.
if opts.FilterFunc == nil && opts.PageSize == 0 && opts.PageOffset == "" && !opts.Ordered && len(opts.ResourceKeys) == 0 {
return p.delegate.List(ctx, list, optionsFunc...)
}
fullList, err := registry.Global().NewList(list.GetItemType())
if err != nil {
return err
}
err = p.delegate.List(ctx, fullList, optionsFunc...)
if err != nil {
return err
}
filteredList, err := registry.Global().NewList(list.GetItemType())
if err != nil {
return err
}
for _, item := range fullList.GetItems() {
_, exists := opts.ResourceKeys[model.MetaToResourceKey(item.GetMeta())]
if len(opts.ResourceKeys) > 0 && !exists {
continue
}
if !opts.Filter(item) {
continue
}
_ = filteredList.AddItem(item)
}
filteredItems := filteredList.GetItems()
lenFilteredItems := len(filteredItems)
sort.Sort(model.ByMeta(filteredItems))
offset := 0
pageSize := lenFilteredItems
paginationEnabled := opts.PageSize != 0
if paginationEnabled {
pageSize = opts.PageSize
if opts.PageOffset != "" {
o, err := strconv.Atoi(opts.PageOffset)
if err != nil {
return ErrorInvalidOffset
}
offset = o
}
}
for i := offset; i < offset+pageSize && i < lenFilteredItems; i++ {
_ = list.AddItem(filteredItems[i])
}
if paginationEnabled {
nextOffset := ""
if offset+pageSize < lenFilteredItems { // set new offset only if we did not reach the end of the collection
nextOffset = strconv.Itoa(offset + opts.PageSize)
}
list.GetPagination().SetNextOffset(nextOffset)
}
list.GetPagination().SetTotal(uint32(lenFilteredItems))
return nil
}
var _ ResourceStore = &paginationStore{}