Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
511 lines
12 KiB
Go
511 lines
12 KiB
Go
package redis3
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/seaweedfs/seaweedfs/weed/util/skiplist"
|
|
)
|
|
|
|
type ItemList struct {
|
|
skipList *skiplist.SkipList
|
|
batchSize int
|
|
client redis.UniversalClient
|
|
prefix string
|
|
}
|
|
|
|
/*
|
|
Be reluctant to create new nodes. Try to fit into either previous node or next node.
|
|
Prefer to add to previous node.
|
|
|
|
There are multiple cases after finding the name for greater or equal node
|
|
1. found and node.Key == name
|
|
The node contains a batch with leading key the same as the name
|
|
nothing to do
|
|
2. no such node found or node.Key > name
|
|
|
|
if no such node found
|
|
prevNode = list.LargestNode
|
|
|
|
// case 2.1
|
|
if previousNode contains name
|
|
nothing to do
|
|
|
|
// prefer to add to previous node
|
|
if prevNode != nil {
|
|
// case 2.2
|
|
if prevNode has capacity
|
|
prevNode.add name, and save
|
|
return
|
|
// case 2.3
|
|
split prevNode by name
|
|
}
|
|
|
|
// case 2.4
|
|
// merge into next node. Avoid too many nodes if adding data in reverse order.
|
|
if nextNode is not nil and nextNode has capacity
|
|
delete nextNode.Key
|
|
nextNode.Key = name
|
|
nextNode.batch.add name
|
|
insert nodeNode.Key
|
|
return
|
|
|
|
// case 2.5
|
|
if prevNode is nil
|
|
insert new node with key = name, value = batch{name}
|
|
return
|
|
|
|
*/
|
|
|
|
func (nl *ItemList) canAddMember(node *skiplist.SkipListElementReference, name string) (alreadyContains bool, nodeSize int, err error) {
|
|
ctx := context.Background()
|
|
pipe := nl.client.TxPipeline()
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
countOperation := pipe.ZLexCount(ctx, key, "-", "+")
|
|
scoreOperationt := pipe.ZScore(ctx, key, name)
|
|
if _, err = pipe.Exec(ctx); err != nil && err != redis.Nil {
|
|
return false, 0, err
|
|
}
|
|
if err == redis.Nil {
|
|
err = nil
|
|
}
|
|
alreadyContains = scoreOperationt.Err() == nil
|
|
nodeSize = int(countOperation.Val())
|
|
return
|
|
}
|
|
|
|
func (nl *ItemList) WriteName(name string) error {
|
|
|
|
lookupKey := []byte(name)
|
|
prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// case 1: the name already exists as one leading key in the batch
|
|
if found && bytes.Compare(nextNode.Key, lookupKey) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var prevNodeReference *skiplist.SkipListElementReference
|
|
if !found {
|
|
prevNodeReference = nl.skipList.GetLargestNodeReference()
|
|
}
|
|
|
|
if nextNode != nil && prevNode == nil {
|
|
prevNodeReference = nextNode.Prev
|
|
}
|
|
|
|
if prevNodeReference != nil {
|
|
alreadyContains, nodeSize, err := nl.canAddMember(prevNodeReference, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if alreadyContains {
|
|
// case 2.1
|
|
return nil
|
|
}
|
|
|
|
// case 2.2
|
|
if nodeSize < nl.batchSize {
|
|
return nl.NodeAddMember(prevNodeReference, name)
|
|
}
|
|
|
|
// case 2.3
|
|
x := nl.NodeInnerPosition(prevNodeReference, name)
|
|
y := nodeSize - x
|
|
addToX := x <= y
|
|
// add to a new node
|
|
if x == 0 || y == 0 {
|
|
if err := nl.ItemAdd(lookupKey, 0, name); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if addToX {
|
|
// collect names before name, add them to X
|
|
namesToX, err := nl.NodeRangeBeforeExclusive(prevNodeReference, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// delete skiplist reference to old node
|
|
if _, err := nl.skipList.DeleteByKey(prevNodeReference.Key); err != nil {
|
|
return err
|
|
}
|
|
// add namesToY and name to a new X
|
|
namesToX = append(namesToX, name)
|
|
if err := nl.ItemAdd([]byte(namesToX[0]), 0, namesToX...); err != nil {
|
|
return err
|
|
}
|
|
// remove names less than name from current Y
|
|
if err := nl.NodeDeleteBeforeExclusive(prevNodeReference, name); err != nil {
|
|
return err
|
|
}
|
|
|
|
// point skip list to current Y
|
|
if err := nl.ItemAdd(lookupKey, prevNodeReference.ElementPointer); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
} else {
|
|
// collect names after name, add them to Y
|
|
namesToY, err := nl.NodeRangeAfterExclusive(prevNodeReference, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// add namesToY and name to a new Y
|
|
namesToY = append(namesToY, name)
|
|
if err := nl.ItemAdd(lookupKey, 0, namesToY...); err != nil {
|
|
return err
|
|
}
|
|
// remove names after name from current X
|
|
if err := nl.NodeDeleteAfterExclusive(prevNodeReference, name); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
}
|
|
|
|
// case 2.4
|
|
if nextNode != nil {
|
|
nodeSize := nl.NodeSize(nextNode.Reference())
|
|
if nodeSize < nl.batchSize {
|
|
if id, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil {
|
|
return err
|
|
} else {
|
|
if err := nl.ItemAdd(lookupKey, id, name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// case 2.5
|
|
// now prevNode is nil
|
|
return nl.ItemAdd(lookupKey, 0, name)
|
|
}
|
|
|
|
/*
|
|
// case 1: exists in nextNode
|
|
|
|
if nextNode != nil && nextNode.Key == name {
|
|
remove from nextNode, update nextNode
|
|
// TODO: merge with prevNode if possible?
|
|
return
|
|
}
|
|
|
|
if nextNode is nil
|
|
|
|
prevNode = list.Largestnode
|
|
|
|
if prevNode == nil and nextNode.Prev != nil
|
|
|
|
prevNode = load(nextNode.Prev)
|
|
|
|
// case 2: does not exist
|
|
// case 2.1
|
|
|
|
if prevNode == nil {
|
|
return
|
|
}
|
|
|
|
// case 2.2
|
|
|
|
if prevNameBatch does not contain name {
|
|
return
|
|
}
|
|
|
|
// case 3
|
|
delete from prevNameBatch
|
|
if prevNameBatch + nextNode < capacityList
|
|
|
|
// case 3.1
|
|
merge
|
|
|
|
else
|
|
|
|
// case 3.2
|
|
update prevNode
|
|
*/
|
|
func (nl *ItemList) DeleteName(name string) error {
|
|
lookupKey := []byte(name)
|
|
prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// case 1
|
|
if found && bytes.Compare(nextNode.Key, lookupKey) == 0 {
|
|
if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil {
|
|
return err
|
|
}
|
|
if err := nl.NodeDeleteMember(nextNode.Reference(), name); err != nil {
|
|
return err
|
|
}
|
|
minName := nl.NodeMin(nextNode.Reference())
|
|
if minName == "" {
|
|
return nl.NodeDelete(nextNode.Reference())
|
|
}
|
|
return nl.ItemAdd([]byte(minName), nextNode.Id)
|
|
}
|
|
|
|
if !found {
|
|
prevNode, err = nl.skipList.GetLargestNode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if nextNode != nil && prevNode == nil {
|
|
prevNode, err = nl.skipList.LoadElement(nextNode.Prev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// case 2
|
|
if prevNode == nil {
|
|
// case 2.1
|
|
return nil
|
|
}
|
|
if !nl.NodeContainsItem(prevNode.Reference(), name) {
|
|
return nil
|
|
}
|
|
|
|
// case 3
|
|
if err := nl.NodeDeleteMember(prevNode.Reference(), name); err != nil {
|
|
return err
|
|
}
|
|
prevSize := nl.NodeSize(prevNode.Reference())
|
|
if prevSize == 0 {
|
|
if _, err := nl.skipList.DeleteByKey(prevNode.Key); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
nextSize := nl.NodeSize(nextNode.Reference())
|
|
if nextSize > 0 && prevSize+nextSize < nl.batchSize {
|
|
// case 3.1 merge nextNode and prevNode
|
|
if _, err := nl.skipList.DeleteByKey(nextNode.Key); err != nil {
|
|
return err
|
|
}
|
|
nextNames, err := nl.NodeRangeBeforeExclusive(nextNode.Reference(), "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := nl.NodeAddMember(prevNode.Reference(), nextNames...); err != nil {
|
|
return err
|
|
}
|
|
return nl.NodeDelete(nextNode.Reference())
|
|
} else {
|
|
// case 3.2 update prevNode
|
|
// no action to take
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (nl *ItemList) ListNames(startFrom string, visitNamesFn func(name string) bool) error {
|
|
lookupKey := []byte(startFrom)
|
|
prevNode, nextNode, found, err := nl.skipList.FindGreaterOrEqual(lookupKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if found && bytes.Compare(nextNode.Key, lookupKey) == 0 {
|
|
prevNode = nil
|
|
}
|
|
if !found {
|
|
prevNode, err = nl.skipList.GetLargestNode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if prevNode != nil {
|
|
if !nl.NodeScanInclusiveAfter(prevNode.Reference(), startFrom, visitNamesFn) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
for nextNode != nil {
|
|
if !nl.NodeScanInclusiveAfter(nextNode.Reference(), startFrom, visitNamesFn) {
|
|
return nil
|
|
}
|
|
nextNode, err = nl.skipList.LoadElement(nextNode.Next[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (nl *ItemList) IsEmpty() bool {
|
|
return nl.skipList.StartLevels[0] == nil
|
|
}
|
|
|
|
func (nl *ItemList) RemoteAllListElement() error {
|
|
|
|
t := nl.skipList
|
|
|
|
nodeRef := t.StartLevels[0]
|
|
for nodeRef != nil {
|
|
node, err := t.LoadElement(nodeRef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
if err := t.DeleteElement(node); err != nil {
|
|
return err
|
|
}
|
|
if err := nl.NodeDelete(node.Reference()); err != nil {
|
|
return err
|
|
}
|
|
nodeRef = node.Next[0]
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
func (nl *ItemList) NodeContainsItem(node *skiplist.SkipListElementReference, item string) bool {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
_, err := nl.client.ZScore(context.Background(), key, item).Result()
|
|
if err == redis.Nil {
|
|
return false
|
|
}
|
|
if err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (nl *ItemList) NodeSize(node *skiplist.SkipListElementReference) int {
|
|
if node == nil {
|
|
return 0
|
|
}
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
return int(nl.client.ZLexCount(context.Background(), key, "-", "+").Val())
|
|
}
|
|
|
|
func (nl *ItemList) NodeAddMember(node *skiplist.SkipListElementReference, names ...string) error {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
var members []redis.Z
|
|
for _, name := range names {
|
|
members = append(members, redis.Z{
|
|
Score: 0,
|
|
Member: name,
|
|
})
|
|
}
|
|
return nl.client.ZAddNX(context.Background(), key, members...).Err()
|
|
}
|
|
func (nl *ItemList) NodeDeleteMember(node *skiplist.SkipListElementReference, name string) error {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
return nl.client.ZRem(context.Background(), key, name).Err()
|
|
}
|
|
|
|
func (nl *ItemList) NodeDelete(node *skiplist.SkipListElementReference) error {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
return nl.client.Del(context.Background(), key).Err()
|
|
}
|
|
|
|
func (nl *ItemList) NodeInnerPosition(node *skiplist.SkipListElementReference, name string) int {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
return int(nl.client.ZLexCount(context.Background(), key, "-", "("+name).Val())
|
|
}
|
|
|
|
func (nl *ItemList) NodeMin(node *skiplist.SkipListElementReference) string {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
slice := nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{
|
|
Min: "-",
|
|
Max: "+",
|
|
Offset: 0,
|
|
Count: 1,
|
|
}).Val()
|
|
if len(slice) > 0 {
|
|
s := slice[0]
|
|
return s
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (nl *ItemList) NodeScanInclusiveAfter(node *skiplist.SkipListElementReference, startFrom string, visitNamesFn func(name string) bool) bool {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
if startFrom == "" {
|
|
startFrom = "-"
|
|
} else {
|
|
startFrom = "[" + startFrom
|
|
}
|
|
names := nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{
|
|
Min: startFrom,
|
|
Max: "+",
|
|
}).Val()
|
|
for _, n := range names {
|
|
if !visitNamesFn(n) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (nl *ItemList) NodeRangeBeforeExclusive(node *skiplist.SkipListElementReference, stopAt string) ([]string, error) {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
if stopAt == "" {
|
|
stopAt = "+"
|
|
} else {
|
|
stopAt = "(" + stopAt
|
|
}
|
|
return nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{
|
|
Min: "-",
|
|
Max: stopAt,
|
|
}).Result()
|
|
}
|
|
func (nl *ItemList) NodeRangeAfterExclusive(node *skiplist.SkipListElementReference, startFrom string) ([]string, error) {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
if startFrom == "" {
|
|
startFrom = "-"
|
|
} else {
|
|
startFrom = "(" + startFrom
|
|
}
|
|
return nl.client.ZRangeByLex(context.Background(), key, &redis.ZRangeBy{
|
|
Min: startFrom,
|
|
Max: "+",
|
|
}).Result()
|
|
}
|
|
|
|
func (nl *ItemList) NodeDeleteBeforeExclusive(node *skiplist.SkipListElementReference, stopAt string) error {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
if stopAt == "" {
|
|
stopAt = "+"
|
|
} else {
|
|
stopAt = "(" + stopAt
|
|
}
|
|
return nl.client.ZRemRangeByLex(context.Background(), key, "-", stopAt).Err()
|
|
}
|
|
func (nl *ItemList) NodeDeleteAfterExclusive(node *skiplist.SkipListElementReference, startFrom string) error {
|
|
key := fmt.Sprintf("%s%dm", nl.prefix, node.ElementPointer)
|
|
if startFrom == "" {
|
|
startFrom = "-"
|
|
} else {
|
|
startFrom = "(" + startFrom
|
|
}
|
|
return nl.client.ZRemRangeByLex(context.Background(), key, startFrom, "+").Err()
|
|
}
|
|
|
|
func (nl *ItemList) ItemAdd(lookupKey []byte, idIfKnown int64, names ...string) error {
|
|
if id, err := nl.skipList.InsertByKey(lookupKey, idIfKnown, nil); err != nil {
|
|
return err
|
|
} else {
|
|
if len(names) > 0 {
|
|
return nl.NodeAddMember(&skiplist.SkipListElementReference{
|
|
ElementPointer: id,
|
|
Key: lookupKey,
|
|
}, names...)
|
|
}
|
|
}
|
|
return nil
|
|
}
|