rankdb

package module
v1.0.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2022 License: BSD-3-Clause Imports: 23 Imported by: 0

README

RankDB

Build Status

RankDB is a fast and scalable ranking system optimized for realtime ranking of various metrics.

The system will provide ranking of elements based on a score. The ranking of the elements can be retrieved either by element IDs or retrieving ordered segment lists.

Features

  • Fast updates with resulting ranking.
  • Fast bulk ingest of new scores.
  • Scalable to many millions of entries in a single ranking list.
  • Stable sorting of items in list.
  • Provide lookup by secondary index or rank with single read.
  • Provides rank comparisons between a subset of all elements.
  • Many independent lists per server instance.
  • Crosslist requests based on custom metadata.
  • Experimental JWT authentication.

Installing

All modes require a configuration file to be set up.

A good basis for cofiguration can be found in conf/conf.stub.toml. You can copy this to conf/conf.toml and adjust it to your needs.

From binaries

  • Extract archive
  • Go to the folder containing the files
  • Copy the configuration as described above
  • Execute rankdb

You can now open the documentation/sample UI on http://127.0.0.1:8080/doc

Go

RankDB uses modules. If you install RankDB inside GOPATH, be sure to use GO111MODULE=on. This will ensure that dependencies are correctly fetched.

go get -u github.com/Vivino/rankdb/cmd/rankdb
  • Go to the folder containing the files.
  • Copy the configuration as described above.
  • Execute rankdb.

You can now open the documentation/sample UI on http://127.0.0.1:8080/doc

Docker

A Dockerfile is included. You can download the docker image from docker hub:

// TODO, something like:

docker pull vivino/rankdb
docker run -e "GOGC=25" -p 8080:8080 -v /mnt/data:/data -v /mnt/conf:/conf vivino/rankdb

By default the server will start on port 8080, which you can of course remap to a local port.

The following paths can/should be set:

  • /conf should contain a conf.toml file with your configuration.
  • /data should point to where you want the local DB to be stored if you use one.
  • /jwtkeys can be used to add jwt keys. Set JwtKeyPath = "/jwtkeys" in your config.

Sample Data

You can try out a test data set you can download here.

It will add some rather large tables to your database and you can test queries, etc. on that.

Glossary

  • Element: An entry in the ranked list.
  • Element ID: An ID of an element. An element ID can only be present in the list once.
  • Score: The value that determines the placement of an element in the ranked list.
  • Segment: A segment defines a part of the complete sorted list. A segment will only contain elements that are within the minimum/maximum values of the Segment. Segments cannot overlap.
  • Index: An index of elements sorted by their ID. The index points to which segment contains each object.
  • Element Index: Ordered list containing all segments pre-sorted by Element ID.
  • List: Complete structure containing Rank and Element Index. Updates and reads go through this.
  • Metadata: Custom JSON that can be attached to elements and lists.

Assumptions/Limitations

To make RankDB efficient, some limitations must be fulfilled:

  • Your element IDs must be representable as 64 bit unsigned integers.
  • The element scores must be representable as a sortable value; 64 bit score and 32 bit tiebreaker.

We provide a convenient reversible converter from 64 bit floats to 64 bit unsigned integers. See the sortfloat package.

With these limitations, it should be possible to use the RankDB server with your data.

Memory Usage

For optimal performance RankDB tries to keep frequently updated data in memory, so configuring the server to match your RAM amount available is important.

Use element metadata sparingly. Only use it for data you will actually need or consider keeping it separately. While it may be convenient to include a lot of information, it will always be loaded alongside each element.

Keep segment split/merge size reasonable. If you have much metadata you might consider setting this lower than the default values.

Use the load_index only on selected lists. The segments (but not elements) of these lists are loaded on startup and always kept in memory.

The CacheEntries in the configuration specifies how many segment elements to keep in memory. This can significantly speed up reading/updating often accessed data. Each entry in the cache contain all the elements of a segment. If a segment not present in the cache is not found, it will be loaded from storage.

The LazySaver will keep data to be saved in memory for a an amount of time. This can significantly reduce the number of writes to storage for frequently updated segments.

Two limits are set on this. When the number of stored item reaches FlushAtItems, it will begin to flush the oldest items to stay below this number. When the number of items reaches LimitItems, writes will begin to block and writes will not complete until the server is below this number. This will affect performance of write endpoints.

Tweak the GOGC environment variable. It is reasonable to reduce the default (100) to something between 25 and 50.

It is possible to limit the number of concurrent updates with MaxUpdates. This will be shared across all endpoints that updates and will block updates from queuing up in memory. Read operations are not affected by this, but limiting the number of concurrent updates will help ensure that read operations can always complete in reasonable time.

Keep bulk updates at a reasonable size. While bulk operations are significantly faster than single operations, they can potentially use a lot of memory. For a single bulk update operation all affected segments and indexes will be loaded so all updates can be applied at once. So for really big updates this could become a problem for the server.

Multiserver setup

RankDB does not support multiple servers accessing the same data.

It is of course possible to set up multiple servers by doing manual sharding of lists and keeping specific lists on separate servers.

Consistency

Do not use RankDB as the primary storage source.

To keep up performance some data is kept in memory and not stored at once. While a lot of measures have been put in place to prevent data loss, an unexpected server shutdown is likely to cause data inconsistencies.

Design your system so you are able to repopulate data. While there is functionality to repair inconsistent lists, the repaired list is likely to be missing fully updated information.

While an element is being updated it might return inconsistent results until the update has completed. Update functions will allow to return updated data, so use that if this can cause problems.

API

Defined in api/design using goa DSL. Generated swagger definitions can be found in api/swagger.

To view API documentation:

  • Go to api folder.
  • First time go get -u ./....
  • Execute go build && api to start server.
  • Navigate to http://localhost:8080/doc

Not all properties are shown in the UI.

To view documentation using Chrome Plugin:

Basic API usage

Create a list

Use POST /lists with payload:

{
  "id": "highscore-list",
  "load_index": false,
  "merge_size": 500,
  "metadata": {
    "country": "dk",
    "game": "2"
  },
  "populate": [
    {
      "id": 101,
      "payload": {
        "country": "dk",
        "name": "Tom Kristensen"
      },
      "score": 500,
      "tie_breaker": 1000
    },
    {
      "id": 102,
      "payload": {
        "country": "uk",
        "name": "Anthony Davidson"
      },
      "score": 200,
      "tie_breaker": 2000
    }
  ],
  "set": "storage-set",
  "split_size": 2000
}

This will create a list called "highscore-list". See documentation with the running server

{
  "avg_segment_elements": 2,
  "bottom_element": {
    "from_bottom": 0,
    "from_top": 1,
    "id": 102,
    "list_id": "highscore-list",
    "payload": {
      "country": "uk",
      "name": "Anthony Davidson"
    },
    "score": 200,
    "tie_breaker": 2000,
    "updated_at": "2019-07-22T13:00:47Z"
  },
  "cache_hits": 0,
  "cache_misses": 0,
  "cache_percent": 0,
  "elements": 2,
  "id": "highscore-list",
  "load_index": false,
  "merge_size": 500,
  "metadata": {
    "country": "dk",
    "game": "2"
  },
  "segments": 1,
  "set": "storage-set",
  "split_size": 2000,
  "top_element": {
    "from_bottom": 1,
    "from_top": 0,
    "id": 101,
    "list_id": "highscore-list",
    "payload": {
      "country": "dk",
      "name": "Tom Kristensen"
    },
    "score": 500,
    "tie_breaker": 1000,
    "updated_at": "2019-07-22T13:00:47Z"
  }
}

To get an element in the list, use GET /lists/highscore-list/elements/101?range=5. This will return the elements and up to 5 neighbors in each direction.

{
  "from_bottom": 1,
  "from_top": 0,
  "id": 101,
  "list_id": "highscore-list",
  "neighbors": {
    "below": [
      {
        "from_bottom": 0,
        "from_top": 1,
        "id": 102,
        "list_id": "highscore-list",
        "payload": {
          "country": "uk",
          "name": "Anthony Davidson"
        },
        "score": 200,
        "tie_breaker": 2000,
        "updated_at": "2019-07-22T13:00:47Z"
      }
    ]
  },
  "payload": {
    "country": "dk",
    "name": "Tom Kristensen"
  },
  "score": 500,
  "tie_breaker": 1000,
  "updated_at": "2019-07-22T13:00:47Z"
}

There are functions to add, update, delete elements and multiple way of querying and get ranks.

There are operations that work across multiple lists. With these queries you can specify which lists to operate on, or you can give list metadata to match.

See the documentation for more details, or the section below which describes the technical details more.

Storage

An atomic key/value blob store must be provided. The storage layer should be able to replace data blobs atomically.

The storage layer is a relatively easy to implement and must simply be able to store blobs of bytes.

Provided storage:
  • Badger: Provides local file-based storage.
  • BoltDB: Provides local file-based storage.
  • Aerospike: Provides storage on Aerospike cluster.
  • DynamoDB: Provides storage on DynamoDB. (experimental, not exposed)
  • Memstore/NullStore: Provides storage for tests, etc.
Storage helpers:
  • LazySaver: Allows in-memory write caching, significantly reducing redundant writes.
  • MaxSize: Allows splitting of elements if storage has an upper size limit on writes.
  • Retry: Retries read/writes if underlying storage returns errors.
  • Test: Allows to insert hooks for checking read, write and delete operations. Available only for development.

List Management

Each ranking list operates independently from each other. However, for convenience there are functions that allow operations to be applied to several lists at once.

Lists are defined uniquely by a string ID, but each list can also have meta-data defined as string -> string key value pairs.
It is possible to specify operations that will execute on specific meta-data values.

The server keeps track of available lists. Lists must be fast loading, or available on demand.

Lists can be created with optional content, or they can be cloned from other lists.

Single List Structure

Elements

Each element in the ranking list contains the following information:

type Element struct {
	Score      uint64
	ElementID  uint64
	Payload    []byte
	TieBreaker uint32
	Updated    time.Time
}

Score is the score that determines the ranking in the list. In case of multiple similar scores, a TieBreaker can be supplied as a secondary sorting option to provide consistent sorting. Float64 values can be converted to sortable score, and can be reversed if sorted values are floating point values.

The ElementID provides an ID for the element. Each Element ID can only be present in a ranking list once.

An optional Payload can be set for an element, which will be returned along with results. The payload is returned untouched, and can optionally be updated along other updates.

Segments

The entire ranking list will be split into (sorted) list segments. Two values "Split Size" and "Merge Size" are specified. When a segment size is greater than "Split size" it is split into two segments. When two adjacent segments have less than "Merged Size" elements combined they are joined.

Segment sizes should be...

  • Small enough to quickly load/search all elements on a single update.
  • Big enough to provide a significant speedup when doing search/aggregate calculations.

Suggested sizes could be in the range of 1000-2000 elements per segment.

A segment index will be created, so it will know the range of score values that is represented by each range. Segments are stored as sorted, non-overlapping elements for fast traversal, and is stored as a single blob for quick reload.

The server will keep the segment index in memory. Each segment index will contain this information about the range it represents:

type Segment struct {
	ID             uint64
	Min, Max       uint64
	MinTie, MaxTie uint32
	Elements       int
	Updated        time.Time
}

ID is a unique ID identifying the range. Min/Max describes the minimum and maximum value in the segment, along with tiebreakers. Elements represents the number of elements in the segment.

The first segment created will contain the range from 0 to math.MaxUint64, and when ranges split, the center values will determine the range of the two segments.

This structure allows for a fast linear search to identify the exact range needed to provide a specific rank, either by accumulating Elements (get rank X) or by checking min/max (is value inside range).

The structure will be used for representing both the Rank Index and the Element Index. Updates will affect both indexes, but most operations will only require at most 2 ranges to be touched.

Rank Index

rank-index dot

Element Index

user-index dot

Operation Complexity

This describes expected complexity in terms of IO per operation. This is excluding any LRU/lazy write cache, which may void the need for certain read/writes, so should be considered worst case scenarios.

  • EI = Element Index
  • RI = Rank Index
  • $$ = Segment Size
Operation Parameter Read Ops Write Ops Notes
Get element rank Element ID 1 EI, 1RI -
Get element at percentile Percentage 1RI -
Get element+surrounding at rank Element ID, radius 1 EI, 1RI - +1 RI if radius goes into another segment.
Get element at rank Rank 1 RI -
Get elements in rank range From/To Rank 1 + FLOOR((To-From)/$$) RI -
Elements with score Score 1 RI (+1 if score crosses into other segments) -
Update Element Score Element ID, Score 1 EI, 2 RI 2 RI (old+new), 1 EI If element remains in same segment only 1 RI op and no EI.
Delete Element Score Element ID 1 EI, 1 RI 1 RI, 1 EI
Bulk Update Score Element ID, Score 1 EI, 2 RI 2 RI (old+new), 1 EI Segments affected by multiple elements will only need to be read/write once.
Bulk Create Table Element ID, Score - 2*len(elems) / $$
Automatic Split - 1 RI, $$ EI 2 RI, $$ EI Only unique EI segments will be written. One Segment is retained, so no EI update.
Automatic Merge - 2 RI, $$ EI 1 RI, $$ / 4 EI Only unique EI segments will be written. One Segment is retained, so no EI update.
Get elements below/above percentile Percentage users/$$ RI -

The segment lists for rank and elements are kept in memory, but dumped at a regular interval or when split/merges occur. Both of these indexes can be recreated by reading through the complete segment lists in case of corruption.

Only automatic Split/Join are complex operations, but the segment size keeps the operation impact to a fairly low one.

Segment splitting/joining is done by an asynchronous job

Concurrency

The database is capable of concurrent operations. However be aware that results of concurrent operations on a list can yield unpredictable results. If two updates are sent to the database, it is undefined which will be applied first.

Security

The RankDB API has optional jwt security feature for restricting access to the database. If your use case is a service behind a firewall, you are free to skip securing your server more than you would otherwise so.

If you choose to keep security disabled, you can disregard the API security requirements. Security is enabled by setting JwtKeyPath config value.

The API has basic access control based on scopes. Currently api:read, api:update, api:delete and api:manage are available as scopes. In the API documentation it is stated what is required for each endpoint. A jwt token can contain several scopes, so "api:read api:update" will allow access to read and update API endpoints.

For read, update+delete you can add restrictions to only allow direct access to either specific lists or elements. This is done by adding custom fields to the jwt token claims. "only_elements": "2,3,6" will only allow reads/updates/deletes on the specified elements. "only_lists": "list1,list2,list5" will only allow access to the specified lists. "api:manager" does not enforce these restrictions.

Note that some calls returns "neighbors" of the current ranked element. These elements are not checked for read access.

For testing, the /jwt endpoint can be used to generate tokens, you can however use your favorite jwt library.

Only RSA Signatures (RSnnn) are supported. This allows you to generate custom keys from other servers, and this server will only need the public key to validate the request.

Go Client

An autogenerated client for Go is provided in github.com/Vivino/rankdb/api/client. You can choose to use this instead of regular HTTP calls.

The client provides structs with predefined data types. The client provides marshal/unmarshal functionality of request data.

An example of setting up the client with retry functionality can be seen in docs/client_example.go

With that setup a sample request will look like this:

func PrintPercentile(list string, pct, neighbors int) error {
	// Create a client
	c := Client("127:0.0.1:8080")
    
	// Perform the request
	resp, err = c.GetPercentileLists(ctx, client.GetPercentileListsPath(list), strconv.Itoa(pct), &neighbors)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return DecodeError(resp)
	}

	// Decode result
	res, err := c.DecodeRankdbElementFull(resp)
	if err != nil {
		return err
	}
	fmt.Println(res)
	return nil
}

Note that minor version upgrades may change client signatures. So upgrading from v1.0.x to v1.1.0 may include changes to client signatures.

Backup

The server supports the following modes of backup.

  1. Backup to local file on the server.
  2. Backup to another RankDB server.
  3. Upload to S3 compatible storage.
  4. Download to calling machine.

Backup to local file

This will start an async job that will save the backup to a local path on the server.

The destination path can get sent and it is possible to filter which lists to back up.

$curl -X PUT "http://127.0.0.1:8080/xlist/backup" \
-H "Content-Type: application/json" \
-d "{ \"destination\": { \"path\": \"/tmp/backup.bin\", \"type\": \"file\" }, \"lists\": {}}"

Example response:

{
  "callback_url": "/backup/c551KHYLi1UxjlbWQ4",
  "id": "c551KHYLi1UxjlbWQ4"
}

It is possible to query for the progress:

curl -X GET "http://127.0.0.1:8080/backup/c551KHYLi1UxjlbWQ4"

Which for example can return:

{
  "cancelled": false,
  "done": true,
  "finished": "2019-05-31T14:44:50.316372113Z",
  "lists": 0,
  "size": 34,
  "started": "2019-05-31T14:44:50.315633403Z",
  "storage": "*file.File",
  "uri": "/backup/c551KHYLi1UxjlbWQ4"
}

A backup job can be cancelled:

curl -X DELETE "http://127.0.0.1:8080/backup/c551KHYLi1UxjlbWQ4"

Backup to another server.

This will transfer contents of a server to another server.

curl -X PUT "http://127.0.0.1:8080/xlist/backup" \
-H "Content-Type: application/json" -d \
"{ \"destination\": { \"path\": \"10.0.0.1:8080\", \"type\": \"server\" }, \"lists\": {}}"

Will transfer all lists from 127.0.0.1:8080 to 10.0.0.1:8080.

Status of the transfer can be queried in the same manner as above.

The receiving server should have a ReadTimeout/WriteTimeout to be able to process the entire set.

Backup to S3

This will backup all content directly to S3.

curl -X PUT "http://127.0.0.1:8080/xlist/backup" \ 
-H "Content-Type: application/json" -d \
"{ \"destination\": { \"path\": \"s3://backup-bucket/path/file-backup.bin\", \"type\": \"s3\" }, \"lists\": {}}"

This will start the backup job directly to s3. To specify the destination, use the following syntax: s3://{bucket}/{path+file}. The same syntax can be used for restoring.

The credentials should be configured in the [AWS] section of the configuration:

[AWS]
Enabled = false

# Specify the region to use.
Region = ""

# URL to object storage service.
# Leave blank to use standard AWS endpoint.
S3Endpoint = ""

# Access keys can be specified here, or be picked up from environment:
# * Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY
# * Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY
# If running on an EC2 instance credentials will also be attempted to be picked up there.
# These config values take priority.
AccessKey = ""
SecretKey = ""

Download to calling machine

This will return the backup data to the caller.

$ curl -J -L -O -X PUT "http://127.0.0.1:8080/xlist/backup"
 -H "Content-Type: application/json" \
 -d "{ \"destination\": { \"path\":\"\", \"type\": \"download\" }, \"lists\": { }}"

This will save the backup to the current directory with the ID of the backup. Alternatively curl -D -o backup.bin -X PUT .... can be used to save to a specific file. The ID of the backup will be returned.

Recompressing

The data is lightly compressed zstd stream. It can be further re-compressed using the zstd commandline:

zstd -c -d LaLh1KyaeUCu0WCaQ4.bin | zstd -T0 -19 -o 06-04-18-ranks-backup.zst

This command will recompress to level 19. Typically this will result in a further 1.5x reduction of data size. The recompressed stream can be used for restoring instead of the original.

This can of course also be done as part of the curl download:

curl -X PUT "http://127.0.0.1:8080/xlist/backup" \
-H "accept: application/octet-stream" -H "Content-Type: application/json" \
-d "{ \"destination\": { \"type\": \"download\" }, \"lists\": { \"match_metadata\": { \"country\": \"dk\" } }}" | zstd -d -c - | zstd -T0 -19 -o 06-04-18-ranks-backup.zst

Reduce -19 if this is too slow.

Restoring data

Restoring data is done by sending the binary data to the server:

$curl -i -T backup.bin -X POST "http://127.0.0.1:8080/xlist/restore"
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Content-Type: text/plain
Date: Fri, 31 May 2019 15:14:31 GMT
Content-Length: 0
{
  "errors": null,
  "restored": 2068,
  "skipped": 0
}

This will send the backup data in backup.bin to the server and restore lists. Lists are replaced on the server.

Note that the ReadTimeout in config must be set for a realistic value, otherwise no response is returned and restore may be interrupted.

# ReadTimeout is the maximum duration for reading the entire request, including the body.
ReadTimeout = "60m"

# WriteTimeout is the maximum duration before timing out writes of the response.
# It is reset whenever a new request's header is read.
WriteTimeout = "60m"

The backup will however keep running even if the connection is broken. Check status in the logs.

Restoring from S3

AWS must be configured as described in "Backup to S3".

To specify s3 as a source, use the src parameter with the s3://{bucket}/{path+file} syntax described above.

Example:

curl -X POST "http://127.0.0.1:8080/xlist/restore?src=s3%3A%2F%2Fbackup-bucket%2Fpath%2Ffile-backup.bin"

This will restore from the backup-bucket bucket and the file at path/file-backup.bin.

License

RankDB is licensed under BSD 3-Clause Revised License. See LICENSE file.

Contributing

You can contribute to the project by sending in a Pull Request.

Be sure to include tests for your pull requests. If you fix a bug, please add a regression test and make sure that new features have tests covering their functionality.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// ErrNotImplemented is returned if the functionality is not yet implemented.
	ErrNotImplemented = errors.New("not implemented")

	// ErrNotFound is returned if the requested item could not be found.
	ErrNotFound = errors.New("not found")

	// ErrVersionMismatch is returned when unable to decode a content because of version mismatch.
	ErrVersionMismatch = errors.New("version mismatch")

	// ErrOffsetOutOfBounds is returned if requested offset is outside list bounds.
	ErrOffsetOutOfBounds = errors.New("offset out of list bounds")

	// ErrEmptySet is returned if a set was empty.
	ErrEmptySet = errors.New("set cannot be empty")
)
View Source
var WithListOption = ListOption(nil)

WithListOption provides an element to create list parameters.

Functions

func MsgpGetVersion

func MsgpGetVersion(in []byte) (b []byte, v uint8)

MsgpGetVersion reads the version and returns it from a byte slice.

func NewSegmentElements

func NewSegmentElements(parent *Segments, e Elements) *lockedSegment

NewSegmentElements creates a segment with a number of elements. If elements are provided min/max will be populated with values from slice. Elements are not saved to segment.

func RandString

func RandString(numRnd ...int) string

RandString returns a random string. x Random bytes + 8 bytes derived from current time. The length is x + 8 characters (same in bytes). The returned string has values 0-9,a-z,A-Z. The number of random bytes is prefixed to help avoid collisions and make strings sort with random distribution. Default number of random bytes is 8, giving 62^8 = 2e14 combinations for the given OS time resolution. Note that creation time is leaked.

Types

type Bucket

type Bucket chan struct{}

A Bucket contains a number of tokens. A bucket can safely be concurrently accessed. A user is free to access the channel directly.

func NewBucket

func NewBucket(tokens int) Bucket

NewBucket creates a bucket with a given number of tokens.

func (Bucket) Get

func (b Bucket) Get() Token

Get will return a token. The order in which tokens are handed out is random.

type Cache

type Cache interface {
	Add(key, value interface{})
	Get(key interface{}) (interface{}, bool)
	Contains(key interface{}) bool
	Remove(key interface{})
}

Cache provides a caching interface.

type Element

type Element struct {
	Score      uint64
	ID         ElementID
	Payload    []byte
	TieBreaker uint32
	Updated    uint32
}

Element contains information about a single element in a list.

func (Element) Above

func (e Element) Above(b Element) bool

Above returns true if e should be ranked above b. If score is equal, the tiebreaker is used, descending. If tiebreaker is equal, last update time wins. Final tiebreaker is element ID, where lowest ID gets first.

func (Element) AboveThis

func (e Element) AboveThis() (score uint64, tie uint32)

AboveThis will return the score of the element just above this.

func (Element) AsIndex

func (e Element) AsIndex(s SegmentID) IndexElement

AsIndex returns the index element of this element in the given collection.

func (*Element) DecodeMsg

func (z *Element) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*Element) EncodeMsg

func (z *Element) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*Element) MarshalMsg

func (z *Element) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Element) Msgsize

func (z *Element) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (Element) PrevMax

func (e Element) PrevMax() (score uint64, tie uint32)

PrevMax will return the maximum just prior to the current element.

func (Element) String

func (e Element) String() string

String returns a readable string representation of the element.

func (*Element) UnmarshalMsg

func (z *Element) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type ElementID

type ElementID uint64

ElementID is the ID of an element in a list.

func (*ElementID) DecodeMsg

func (z *ElementID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (ElementID) EncodeMsg

func (z ElementID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (ElementID) MarshalMsg

func (z ElementID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (ElementID) Msgsize

func (z ElementID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*ElementID) UnmarshalMsg

func (z *ElementID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type ElementIDs

type ElementIDs []ElementID

ElementIds is a slice of collection ids.

func (ElementIDs) AsScore

func (e ElementIDs) AsScore() []uint64

AsScore returns the element ids as slice of uint64.

func (*ElementIDs) DecodeMsg

func (z *ElementIDs) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*ElementIDs) Deduplicate

func (e *ElementIDs) Deduplicate()

Deduplicate and sort the element ids.

func (ElementIDs) EncodeMsg

func (z ElementIDs) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (ElementIDs) Map

func (e ElementIDs) Map() map[ElementID]struct{}

Map returns the element ids as a map.

func (ElementIDs) MarshalMsg

func (z ElementIDs) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (ElementIDs) Msgsize

func (z ElementIDs) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (ElementIDs) NotIn

func (e ElementIDs) NotIn(b ElementIDs) ElementIDs

NotIn returns the elements not in b.

func (ElementIDs) Overlap

func (e ElementIDs) Overlap(b ElementIDs) ElementIDs

Overlap returns the overlap between the Element IDs.

func (*ElementIDs) Sort

func (e *ElementIDs) Sort()

Sort the element ids.

func (*ElementIDs) UnmarshalMsg

func (z *ElementIDs) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type Elements

type Elements []Element

Elements is a collection of elements. When this type is used elements can be assumed to be sorted.

func NewElements

func NewElements(e []Element) Elements

NewElements converts an (unsorted) slice of elements into a sorted slice of elements. Duplicate elements are removed.

func (*Elements) Add

func (l *Elements) Add(e Element) (*Rank, error)

Add element if it does not exist. If element exists, it is updated.

func (Elements) Clone

func (l Elements) Clone(payloads bool) Elements

Clone all element in list. Payloads are optionally cloned.

func (*Elements) DecodeMsg

func (z *Elements) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*Elements) Deduplicate

func (l *Elements) Deduplicate() (changed bool)

Deduplicate will remove entries with duplicate Element IDs. If duplicates are found, the element with latest update time is kept. If update time is equal, the one with the highest score is kept. The element list is always re-sorted.

func (*Elements) Delete

func (l *Elements) Delete(id ElementID) error

Delete element from list. Returns ErrNotFound if element could not be found.

func (Elements) ElementIDs

func (l Elements) ElementIDs(id SegmentID) IndexElements

ElementIDs returns element ids as ranked elements, where score is the element id and payload is the segment to which they belong.

func (Elements) EncodeMsg

func (z Elements) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (Elements) Find

func (l Elements) Find(id ElementID) (*Element, error)

Find element in list. Returns ErrNotFound if not found.

func (Elements) FindIdx

func (l Elements) FindIdx(id ElementID) (int, error)

FindIdx returns index of element.

func (Elements) FindScoreIdx

func (l Elements) FindScoreIdx(score uint64) (int, error)

FindScoreIdx returns index of first element that matches score.

func (Elements) FirstElementsWithScore

func (e Elements) FirstElementsWithScore(scores []uint64) Elements

firstElementsWithScore returns the first element that has each of the supplied scores. If no element exist with the supplied score, the element below is returned. Scores must be sorted descending.

func (*Elements) HasDuplicates

func (l *Elements) HasDuplicates() error

HasDuplicates returns true if elements contains duplicates.

func (Elements) IDSorter

func (l Elements) IDSorter() func(i, j int) bool

IDSorter returns a sorting function that sorts by ID.

func (Elements) IDs

func (l Elements) IDs() ElementIDs

IDSorter returns a sorting function that sorts by ID.

func (*Elements) Insert

func (l *Elements) Insert(e Element) int

Insert element in list. Returns index of inserted item.

func (Elements) MarshalMsg

func (z Elements) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Elements) Merge

func (l *Elements) Merge(ins Elements, sliced bool)

Merge other elements into this list. Provided elements must be sorted. Provide information on whether l is shared with other slices. Use true if in doubt. Does not deduplicate on IDs, use MergeDeduplicate for this (approximately 1 order of magnitude slower).

func (*Elements) MergeDeduplicate

func (l *Elements) MergeDeduplicate(ins Elements)

MergeDeduplicate will merge other elements into this list. IDs are checked for duplicates and inserted elements overwrite existing. ins is used, so content is overwritten. Each list must be de-duplicated. It is not a strict requirement that lists are sorted.

func (Elements) MinMax

func (l Elements) MinMax() (min, max uint64, minTie, maxTie uint32)

MinMax returns the minimum and maximum values of the elements. If no elements are provided the entire range is returned.

func (Elements) Msgsize

func (z Elements) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (Elements) Ranked

func (l Elements) Ranked(topOffset, total int) RankedElements

Ranked converts elements to ranked elements. The offset from the top of the list of the first element and total number of elements in the list must be provided.

func (*Elements) Sort

func (l *Elements) Sort() (changed bool)

Sort the elements. Returns whether a change was made.

func (Elements) Sorter

func (l Elements) Sorter() func(i, j int) bool

Sorter returns a sorter that will sort the elements by score, descending. If score is equal, the tiebreaker is used, descending. If tiebreaker is equal, earliest update time wins. Final tiebreaker is element ID, where lowest ID gets first.

func (Elements) SplitSize

func (l Elements) SplitSize(inEach int) []Elements

SplitSize will split the elements into a slice of elements.

func (Elements) String

func (l Elements) String() string

String returns a readable string representation of the elements.

func (*Elements) UnmarshalMsg

func (z *Elements) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*Elements) Update

func (l *Elements) Update(e Element) (*Rank, error)

Update will delete the previous element in the list with the same ID and insert the new element. Returns ErrNotFound if element could not be found.

func (*Elements) UpdateTime

func (l *Elements) UpdateTime(t time.Time)

UpdateTime will update time of all elements to the provided time.

type IndexElement

type IndexElement struct {
	Element
}

IndexElement is an element that is used as an index.

func (*IndexElement) DecodeMsg

func (z *IndexElement) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*IndexElement) EncodeMsg

func (z *IndexElement) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*IndexElement) MarshalMsg

func (z *IndexElement) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*IndexElement) Msgsize

func (z *IndexElement) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*IndexElement) UnmarshalMsg

func (z *IndexElement) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type IndexElements

type IndexElements struct {
	Elements
}

IndexElements contains elements that are used to index other elements.

func (*IndexElements) DecodeMsg

func (z *IndexElements) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*IndexElements) EncodeMsg

func (z *IndexElements) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*IndexElements) MarshalMsg

func (z *IndexElements) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*IndexElements) Msgsize

func (z *IndexElements) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (IndexElements) SegmentSorter

func (e IndexElements) SegmentSorter() func(i, j int) bool

SegmentSorter returns a sorted that orders by segment first and score secondly.

func (*IndexElements) UnmarshalMsg

func (z *IndexElements) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type IndexError

type IndexError error

IndexError is returned when error is related to index and can be fixed by a re-index.

type List

type List struct {
	sync.RWMutex `msg:"-" json:"-"`
	ID           ListID
	Set          string
	Metadata     map[string]string
	SplitSize    int
	MergeSize    int
	LoadIndex    bool
	Scores       SegmentsID

	Index SegmentsID
	// contains filtered or unexported fields
}

List contains the segments representing a list as well as the index to look up segments by object ID.

func NewList

func NewList(ctx context.Context, id ListID, set string, bs blobstore.Store, opts ...ListOption) (*List, error)

NewList creates a new list. A list ID and storage set must be provided. Use WithListOption to access additional options.

func RestoreList

func RestoreList(ctx context.Context, bs blobstore.Store, r *ReaderMsgp, c Cache, newID *ListID) (*List, error)

RestoreList will restore a list. If "newID" is provided, the list is saved with its new new and segments are given new ids. Otherwise the exact same list is restored.

func (*List) Backup

func (l *List) Backup(ctx context.Context, bs blobstore.Store, w *WriterMsgp) error

Backup serializes all content of a lists and writes it to the provided writer.

func (*List) DecodeMsg

func (z *List) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*List) DeleteAll

func (l *List) DeleteAll(ctx context.Context, bs blobstore.Store) error

DeleteAll will delete all stored content of a list.

func (*List) DeleteElements

func (l *List) DeleteElements(ctx context.Context, bs blobstore.Store, ids []ElementID) error

deleteElements will delete elements with the supplied ids. If no elements are not found, no error is returned.

func (*List) EncodeMsg

func (z *List) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*List) ForceSplit

func (l *List) ForceSplit(ctx context.Context, bs blobstore.Store) error

ForceSplit will force list splitting/Merging.

func (*List) GetElements

func (l *List) GetElements(ctx context.Context, bs blobstore.Store, ids []ElementID, radius int) (RankedElements, error)

GetElements will look up the provided elements and return them as ranked. The returned elements are sorted by rank. Elements that are not found are not returned.

func (*List) GetPercentile

func (l *List) GetPercentile(ctx context.Context, bs blobstore.Store, percentile float64, radius int) (*RankedElement, error)

GetPercentile returns the element at a given percentile. The percentile must be normalized to 0->1.

func (*List) GetRankBottom

func (l *List) GetRankBottom(ctx context.Context, bs blobstore.Store, offset, elements int) (Elements, error)

GetRankBottom returns a number of elements at a specific offset from the top of the list. Elements are returned in descending order. First element returned is (list length) - offset - 1. Requesting offset 0 will return the bottom element. If offset is outside list range ErrOffsetOutOfBounds is returned.

func (*List) GetRankScoreAsc

func (l *List) GetRankScoreAsc(ctx context.Context, bs blobstore.Store, score uint64, n int) (Elements, error)

GetRankScoreAsc is not implemented.

func (*List) GetRankScoreDesc

func (l *List) GetRankScoreDesc(ctx context.Context, bs blobstore.Store, score uint64, n int) (RankedElements, error)

GetRankScoreDesc returns ranked elements starting with first elements at a specific score. If the exact score isn't found, the list will start with the following next entry below the score. If no scores are at or below the supplied score, an empty array is returned. Results are returned in descending order.

func (*List) GetRankTop

func (l *List) GetRankTop(ctx context.Context, bs blobstore.Store, offset, elements int) (Elements, error)

GetRankTop returns a number of elements at a specific offset from the top of the list. Elements are returned in descending order. First element returned is rank offset+1. Requesting offset 0 will start with top ranked element. Requesting offset equal to or grater than list length will return ErrOffsetOutOfBounds.

func (*List) Insert

func (l *List) Insert(ctx context.Context, bs blobstore.Store, e Elements) error

Insert elements into list. Elements are not checked if they exist and will result in duplicates if they do. Provided elements are not required to be sorted.

func (*List) Len

func (l *List) Len(ctx context.Context, bs blobstore.Store) (int, error)

Len returns the number of elements in the list.

func (*List) MarshalMsg

func (z *List) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*List) Msgsize

func (z *List) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*List) Populate

func (l *List) Populate(ctx context.Context, bs blobstore.Store, e Elements) error

Populate will replace content of list with the supplied elements. Elements are assumed to be de-duplicated.

func (*List) Reindex

func (l *List) Reindex(ctx context.Context, bs blobstore.Store) error

Reindex will re-create the list element index.

func (*List) ReleaseSegments

func (l *List) ReleaseSegments(ctx context.Context)

ReleaseSegments will release all segments from list including all elements.

func (*List) Repair

func (l *List) Repair(ctx context.Context, bs blobstore.Store, clearIfErr bool) error

Repair will repair the list. So far this is fairly brutally done by loading all segments and recreating scores and indexes.

func (*List) Stats

func (l *List) Stats(ctx context.Context, bs blobstore.Store, elements bool) (*ListStats, error)

Stats returns general stats about the list. If elements is true, top and bottom elements will be loaded.

func (*List) String

func (l *List) String() string

func (*List) UnmarshalMsg

func (z *List) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*List) UpdateElements

func (l *List) UpdateElements(ctx context.Context, bs blobstore.Store, elems Elements, radius int, results bool) (RankedElements, error)

UpdateElements will update or add elements to the list. Elements should be deduplicated when provided. Concurrent calls to UpdateElements with the same elements can result in duplicate entries in the list. If results are not requested, the returned slice will always be empty.

func (*List) Verify

func (l *List) Verify(ctx context.Context, bs blobstore.Store) error

Verify a list without loading elements.

func (*List) VerifyElements

func (l *List) VerifyElements(ctx context.Context, bs blobstore.Store) error

VerifyElements verifies elements in list.

func (*List) VerifyUnlocked

func (l *List) VerifyUnlocked(ctx context.Context, timeout time.Duration) error

VerifyUnlocked validates that all elements of the list can be locked. This is only really usable for tests, where list context is controlled.

type ListID

type ListID string

ListID is the ID of a list.

func ListToListID

func ListToListID(lists ...*List) []ListID

ListToListID returns the IDs of the supplied lists. Elements that are nil are ignored.

func (*ListID) DecodeMsg

func (z *ListID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (ListID) EncodeMsg

func (z ListID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (ListID) MarshalMsg

func (z ListID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (ListID) Msgsize

func (z ListID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*ListID) UnmarshalMsg

func (z *ListID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type ListIDs

type ListIDs []ListID

ListIDs is a slice of list IDs.

func (*ListIDs) DecodeMsg

func (z *ListIDs) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*ListIDs) Deduplicate

func (ids *ListIDs) Deduplicate() (changed bool)

Deduplicate list ids. The id list will be sorted as a side effect.

func (ListIDs) EncodeMsg

func (z ListIDs) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (ListIDs) MarshalMsg

func (z ListIDs) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (ListIDs) Msgsize

func (z ListIDs) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (ListIDs) Sort

func (ids ListIDs) Sort()

Sort ids.

func (*ListIDs) UnmarshalMsg

func (z *ListIDs) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type ListOption

type ListOption func(*listOptions) error

ListOption can be used to specify options when creating a list. Use either directly or use WithListOption.

func (ListOption) Cache

func (l ListOption) Cache(cache Cache) ListOption

Cache will set cache of the list.

func (ListOption) Clone

func (l ListOption) Clone(lst *List) ListOption

Clone will populate the list with the content of another list.

func (ListOption) LoadIndex

func (l ListOption) LoadIndex(b bool) ListOption

LoadIndex will signify that lists indexes should be loaded on startup. If not, they are loaded on demand, and reclaimed by the server based on global policy. By default indexes are loaded.

func (ListOption) MergeSplitSize

func (l ListOption) MergeSplitSize(merge, split int) ListOption

Provide custom split/merge sizes for list. Merge must be < split.

func (ListOption) Metadata

func (l ListOption) Metadata(m map[string]string) ListOption

Metadata will set metadata.

func (ListOption) Populate

func (l ListOption) Populate(e []Element) ListOption

Populate will populate the list with supplied elements.

type ListStats

type ListStats struct {
	Elements    int
	Segments    int
	Top         *RankedElement
	Bottom      *RankedElement
	CacheHits   uint64
	CacheMisses uint64
	CachePct    float64
}

ListStats provides overall stats for a list

func (*ListStats) DecodeMsg

func (z *ListStats) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*ListStats) EncodeMsg

func (z *ListStats) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*ListStats) MarshalMsg

func (z *ListStats) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*ListStats) Msgsize

func (z *ListStats) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*ListStats) UnmarshalMsg

func (z *ListStats) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type Lists

type Lists struct {
	// contains filtered or unexported fields
}

Lists contains an id indexed

func (*Lists) Add

func (l *Lists) Add(lists ...*List)

Add or overwrite list.

func (*Lists) All

func (l *Lists) All() ListIDs

All returns all list ids, unsorted.

func (Lists) ByID

func (l Lists) ByID(id ListID) (*List, bool)

ByID returns a list by ID. This is an O(1) operation.

func (Lists) ByIDs

func (l Lists) ByIDs(ids ...ListID) []*List

ByIDs returns lists by ID. This is an O(n) operation. Lists that are not found are returned as nil.

func (*Lists) Delete

func (l *Lists) Delete(id ListID)

Delete a list. Does not delete underlying data.

func (*Lists) Exists

func (l *Lists) Exists(ids ...ListID) []ListID

Exists returns the ids that exist.

func (*Lists) Load

func (l *Lists) Load(ctx context.Context, bs blobstore.Store, b []byte) error

Load the lists. Overwrites all existing lists.

func (*Lists) MatchAll

func (l *Lists) MatchAll(q map[string]string, sets []string) []ListID

MatchAll will return all lists where all metadata fields match the provided. A numbers of sets can also be provided. All lists from sets are included. If q and sets both have 0 entries nothing is returned. Results are returned in random order.

func (*Lists) Prune

func (l *Lists) Prune(ctx context.Context) error

Prune will release all in-memory elements and for lists that has LoadIndex set to false.

func (Lists) Save

func (l Lists) Save(ctx context.Context, w io.Writer) error

Save the lists.

func (*Lists) SortedIDsAfter

func (l *Lists) SortedIDsAfter(from ListID, n int) ([]*List, PageInfo)

SortedIDsAfter returns a number of lists sorted by ID. The first entry returned will be the one following the provided ID. Provide an empty ID to get from the first list. If n <= 0 an empty list and page info will be returned.

func (*Lists) SortedIDsBefore

func (l *Lists) SortedIDsBefore(to ListID, n int) ([]*List, PageInfo)

SortedIDsBefore returns a number of lists sorted by ID. The last entry in the list will be the one preceding the provided ID. Provide an empty ID to get from the first list. If n <= 0 an empty list and page info will be returned.

type Manager

type Manager struct {
	Storage     blobstore.Store
	Backup      blobstore.WithSet
	Set         string
	Lists       Lists
	BackupEvery int
	// contains filtered or unexported fields
}

func NewManager

func NewManager(store blobstore.Store, set string) (*Manager, error)

NewManager will create a new manager that manages the lists of the server.

func (*Manager) DeleteList

func (m *Manager) DeleteList(ctx context.Context, id ListID) error

DeleteList will delete a list and all data associated.

func (*Manager) LoadLists

func (m *Manager) LoadLists(ctx context.Context, cache Cache) error

LoadLists will load lists from storage and prepare them for queries.

func (*Manager) NewLists

func (m *Manager) NewLists(cache Cache) error

NewLists will initialize an empty lists set and attach the cache.

func (*Manager) SaveLists

func (m *Manager) SaveLists(ctx context.Context, backup bool) error

SaveLists explicitly saves all lists.

func (*Manager) StartIntervalSaver

func (m *Manager) StartIntervalSaver(ctx context.Context, every time.Duration, shutdown chan chan struct{})

StartIntervalSaver will start a saver that will save the lists at fixed intervals. If the interval is 0 the lists are never saved. The lists are saved on shutdown. The provided channel is closed when save has finished.

func (*Manager) StartListPruner

func (m *Manager) StartListPruner(ctx context.Context, every time.Duration, shutdown chan chan struct{})

StartListPruner will start an async list pruner that will unload elements and segments at regular intervals. If the interval provided is 0 the lists are never pruned.

func (*Manager) StartListSplitter

func (m *Manager) StartListSplitter(ctx context.Context, store blobstore.Store, shutdown chan chan struct{})

StartListSplitter will start an async list splitter that will split lists that requests so.

type PageInfo

type PageInfo struct {
	Before, After int
}

PageInfo contains information about the number of lists/elements before and after the ones in the list.

type Rank

type Rank struct {
	FromTop    int
	FromBottom int
	Element    Element
}

Rank contains the element rank without neighbors.

type RankedElement

type RankedElement struct {
	Element
	// FromTop indicates how many items are above this in the list.
	FromTop int
	// FromBottom indicates how many items are below this in the list.
	FromBottom int

	// Above contains elements above the current element.
	// Ends with element just above current.
	Above []Element `json:",omitempty"`

	// Below contains elements below the current element.
	// Starts with element just below current.
	Below []Element `json:",omitempty"`

	// Before contains the element placement before update.
	Before *RankedElement `json:"before,omitempty"`
}

RankedElement is a ranked element.

func (*RankedElement) CalculateFromBottom

func (r *RankedElement) CalculateFromBottom(total int)

CalculateFromBottom recalculates the FromBottom with the supplied list total.

func (RankedElement) String

func (r RankedElement) String() string

String returns a human readble representation of the ranked element.

type RankedElements

type RankedElements []RankedElement

RankedElements is a collection od ranked elements.

func (RankedElements) CalculateFromBottom

func (r RankedElements) CalculateFromBottom(total int)

CalculateFromBottom recalculates the FromBottom with the supplied list total.

func (RankedElements) Elements

func (r RankedElements) Elements() Elements

Elements returns the underlying elements in the same order as the ranked elements.

func (RankedElements) IDs

func (r RankedElements) IDs() ElementIDs

ElementIDs returns the element ids.

func (RankedElements) Offset

func (r RankedElements) Offset(offset int)

Offset will add a specific offset to FromTop and subtract it from FromBottom.

func (RankedElements) Sort

func (r RankedElements) Sort()

Sort will re-sort the elements.

type ReaderMsgp

type ReaderMsgp struct {
	// contains filtered or unexported fields
}

ReaderMsgp

func NewReaderMsgp

func NewReaderMsgp(b []byte) *ReaderMsgp

func NewReaderMsgpReader

func NewReaderMsgpReader(r io.Reader) *ReaderMsgp

func (*ReaderMsgp) Close

func (w *ReaderMsgp) Close()

Call Close to signify you are done with serialization. This will recycle the Reader. This may only be called once, otherwise races will occur.

func (ReaderMsgp) GetVersion

func (w ReaderMsgp) GetVersion() (v uint8)

func (ReaderMsgp) Reader

func (w ReaderMsgp) Reader() *msgp.Reader

type ScoreError

type ScoreError error

ScoreError is returned when error is related to scores.

type Segment

type Segment struct {
	ID             SegmentID // ID of this segment
	Min, Max       uint64    // Min/Max score of the elements in the segment.
	MinTie, MaxTie uint32    // Min/Max Tiebreaker of the elements in the segment.
	N              int
	Updated        int64
	Parent         SegmentsID
	// contains filtered or unexported fields
}

A Segment describes a part of the list. All elements in the segment are guaranteed to be within Min/Max values (inclusive). A segment way describe a larger range than the elements themselves represent, it is up to splitting functions to determine this.

func MaxSegment

func MaxSegment() *Segment

MaxSegment contains the highest score and tiebreaker.

func NewSegment

func NewSegment(parent *Segments) *Segment

NewSegment creates a new, empty segment.

func (*Segment) DecodeMsg

func (z *Segment) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*Segment) EncodeMsg

func (z *Segment) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*Segment) Filter

func (s *Segment) Filter(e Elements) Elements

Filter returns the slice of elements that falls within the current segment.

func (*Segment) FilterIdx

func (s *Segment) FilterIdx(e Elements) (start, end int)

FilterIdx returns the indexes of the start and end of the slice of elements that fall within the range of the segment.

func (*Segment) FilterScoresIdx

func (s *Segment) FilterScoresIdx(scores []uint64) (start, end int)

FilterScoresIdx returns the indexes of the start and end of the slice of elements that fall within the range of the segment. The supplied scores must be sorted descending.

func (*Segment) MarshalMsg

func (z *Segment) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Segment) Msgsize

func (z *Segment) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*Segment) StorageKey

func (s *Segment) StorageKey() string

StorageKey returns the storage key for a segment.

func (*Segment) String

func (s *Segment) String() string

String returns a human readable representation of the segment.

func (*Segment) UnmarshalMsg

func (z *Segment) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type SegmentID

type SegmentID uint32

SegmentID is the ID of a segment. It does not correspond to the placement in the list, but is used to reference it.

func (*SegmentID) DecodeMsg

func (z *SegmentID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (SegmentID) EncodeMsg

func (z SegmentID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (SegmentID) MarshalMsg

func (z SegmentID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (SegmentID) Msgsize

func (z SegmentID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*SegmentID) UnmarshalMsg

func (z *SegmentID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type Segments

type Segments struct {
	ID           SegmentsID
	Segments     []Segment    // Individual segments
	SegmentsLock sync.RWMutex `msg:"-" json:"-"` // Global lock for all stats on all segments (above)
	NextID       SegmentID    // Next unused ID
	IsIndex      bool         // These segments represents an index.
	// contains filtered or unexported fields
}

Segments contains all segments of a list and contains and index to quickly look up the index of a segment.

func NewSegments

func NewSegments(preAlloc int, withIdx bool) *Segments

NewSegments creates new segments and preallocates space for segments. Optionally it will preallocate internal index as well.

func NewSegmentsElements

func NewSegmentsElements(ctx context.Context, bs blobstore.WithSet, e []Elements, idx *IndexElements) (*Segments, error)

NewSegmentsElements creates segments from slices of elements. Each member of the slice will result in one segment. The Segment range will cover the entire possible space, so first segment will always be from MaxElement, etc. If idx is supplied it will be populated with index elements. If idx is not supplied it is assumed that this is index segments.

func (*Segments) AlignIndex

func (s *Segments) AlignIndex()

AlignIndex will align segments to be used for indexes. This ensure that segment splits are on score boundaries so changes in tiebreaker does not affect segment assignment. This should only be used on segment creation and not on segments with content.

func (*Segments) Append

func (s *Segments) Append(ls *lockedSegment)

Append a segment to the end of segments. The locked segment is updated with new index.

func (*Segments) DecodeMsg

func (z *Segments) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*Segments) Delete

func (s *Segments) Delete(ctx context.Context, store blobstore.WithSet) error

Delete will delete the segments representation and elements.

func (*Segments) DeleteElements

func (s *Segments) DeleteElements(ctx context.Context, bs blobstore.WithSet, e Elements, withIdx bool) (IndexElements, error)

deleteElements deletes specific elements in segments. Optionally returns index elements of deleted items. Provided elements must be sorted.

func (*Segments) DeleteElementsIdx

func (s *Segments) DeleteElementsIdx(ctx context.Context, bs blobstore.WithSet, ids IndexElements, withIdx bool) (IndexElements, error)

deleteElements deletes specific elements in segments places at supplied index. Optionally returns index elements of deleted items. Read lock must be held for segments.

func (*Segments) ElementIndexAll

func (s *Segments) ElementIndexAll(ctx context.Context, bs blobstore.WithSet) (IndexElements, error)

ElementIndexAll will return index for all elements in the list.

func (*Segments) Elements

func (s *Segments) Elements() int

Elements returns the number of elements in all segments. Caller must hold at least read lock for segment.

func (*Segments) EncodeMsg

func (z *Segments) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*Segments) FindElements

func (s *Segments) FindElements(ctx context.Context, bs blobstore.WithSet, ids IndexElements, radius int) ([]RankedElement, error)

FindElements returns elements with the supplied indices. Not found errors are logged, but not fatal. Caller must hold s.scores read lock.

func (*Segments) Insert

func (s *Segments) Insert(ctx context.Context, bs blobstore.WithSet, in Elements, withIdx bool) (IndexElements, error)

Insert sorted elements into segments. Returns indexes of elements if withIdx is true. Read lock must be held

func (*Segments) MarshalMsg

func (z *Segments) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*Segments) Msgsize

func (z *Segments) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*Segments) Save

func (s *Segments) Save(ctx context.Context, store blobstore.WithSet) error

Save segments to disk.

func (*Segments) String

func (s *Segments) String() string

String returns a human readable representation of the segments.

func (*Segments) UnmarshalMsg

func (z *Segments) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*Segments) Verify

func (s *Segments) Verify(ctx context.Context, bs blobstore.WithSet) error

Verify segments without validating elements.

func (*Segments) VerifyElements

func (s *Segments) VerifyElements(ctx context.Context, bs blobstore.WithSet, IDs *map[ElementID]SegmentID) error

VerifyElements verifies elements in segments. Supply a non-nil "IDs". It will be populated with the Element IDs found. The supplied map is overwritten. Segments must be read locked by callers.

type SegmentsID

type SegmentsID string

SegmentsID is the ID of a collection of segments.

func (*SegmentsID) DecodeMsg

func (z *SegmentsID) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (SegmentsID) EncodeMsg

func (z SegmentsID) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (SegmentsID) Load

func (s SegmentsID) Load(ctx context.Context, store blobstore.WithSet, cache Cache) (*Segments, error)

Load will load the segments (but no elements) of the specified ID.

func (SegmentsID) MarshalMsg

func (z SegmentsID) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (SegmentsID) Msgsize

func (z SegmentsID) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*SegmentsID) UnmarshalMsg

func (z *SegmentsID) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (SegmentsID) Unset

func (s SegmentsID) Unset() bool

SegmentsID returns whether the segment ID is non-zero.

type Token

type Token struct {
	// contains filtered or unexported fields
}

A Token from a bucket. Can be put buck using the Release function.

func (Token) Release

func (t Token) Release()

Release a token and put it back in the bucket.

type WriterMsgp

type WriterMsgp struct {
	// contains filtered or unexported fields
}

func NewWriterMsg

func NewWriterMsg() *WriterMsgp

func (WriterMsgp) Buffer

func (w WriterMsgp) Buffer() *bytes.Buffer

Buffer returns the buffer containing the encoded content. The encoder is flushed to buffer.

func (*WriterMsgp) Close

func (w *WriterMsgp) Close()

Call Close to signify you are done with serialization and you no longer need the data kept in the buffer. This will recycle the Writer. This may only be called once, otherwise races will occur.

func (WriterMsgp) Flush

func (w WriterMsgp) Flush()

Flush the messagepack writer.

func (WriterMsgp) ReplaceWriter

func (w WriterMsgp) ReplaceWriter(writer io.Writer)

ReplaceWriter replaces the writer

func (WriterMsgp) SetVersion

func (w WriterMsgp) SetVersion(v uint8) error

SetVersion will write a version number.

func (WriterMsgp) Writer

func (w WriterMsgp) Writer() *msgp.Writer

Writer returns the msgpack writer.

Directories

Path Synopsis
api
app
design
Package design contains the API design used for generating goa models and controllers.
Package design contains the API design used for generating goa models and controllers.
s3
bstest
nolint Package bstest supplies helpers to test blobstores.
nolint Package bstest supplies helpers to test blobstores.
cmd
log
testlogger
Package testlogger provides a logging adapter for tests and benchmarks.
Package testlogger provides a logging adapter for tests and benchmarks.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL