redis/iterator.go

78 lines
1.5 KiB
Go
Raw Normal View History

2016-04-13 11:52:47 +03:00
package redis
2019-06-04 13:30:47 +03:00
import (
2020-03-11 17:26:42 +03:00
"context"
2019-06-04 13:30:47 +03:00
"sync"
)
2016-04-13 11:52:47 +03:00
// ScanIterator is used to incrementally iterate over a collection of elements.
// It's safe for concurrent use by multiple goroutines.
type ScanIterator struct {
2017-01-03 13:44:06 +03:00
mu sync.Mutex // protects Scanner and pos
cmd *ScanCmd
2016-04-13 11:52:47 +03:00
pos int
}
// Err returns the last iterator error, if any.
func (it *ScanIterator) Err() error {
it.mu.Lock()
2017-01-03 13:44:06 +03:00
err := it.cmd.Err()
2016-04-13 11:52:47 +03:00
it.mu.Unlock()
return err
}
// Next advances the cursor and returns true if more values can be read.
2020-03-11 17:26:42 +03:00
func (it *ScanIterator) Next(ctx context.Context) bool {
2016-04-13 11:52:47 +03:00
it.mu.Lock()
defer it.mu.Unlock()
// Instantly return on errors.
2017-01-03 13:44:06 +03:00
if it.cmd.Err() != nil {
2016-04-13 11:52:47 +03:00
return false
}
// Advance cursor, check if we are still within range.
2017-01-03 13:44:06 +03:00
if it.pos < len(it.cmd.page) {
2016-04-13 11:52:47 +03:00
it.pos++
return true
}
2016-09-08 16:07:49 +03:00
for {
2016-09-08 16:40:45 +03:00
// Return if there is no more data to fetch.
2017-01-03 13:44:06 +03:00
if it.cmd.cursor == 0 {
2016-09-08 16:07:49 +03:00
return false
}
2016-04-13 11:52:47 +03:00
2016-09-08 16:07:49 +03:00
// Fetch next page.
switch it.cmd.args[0] {
case "scan", "qscan":
2019-08-24 11:55:13 +03:00
it.cmd.args[1] = it.cmd.cursor
default:
2019-08-24 11:55:13 +03:00
it.cmd.args[2] = it.cmd.cursor
2016-09-08 16:07:49 +03:00
}
2017-01-03 13:44:06 +03:00
2020-03-11 17:26:42 +03:00
err := it.cmd.process(ctx, it.cmd)
2017-01-03 13:44:06 +03:00
if err != nil {
2016-09-08 16:07:49 +03:00
return false
}
2016-04-13 11:52:47 +03:00
2016-09-08 16:07:49 +03:00
it.pos = 1
2016-09-08 16:40:45 +03:00
2017-01-03 13:44:06 +03:00
// Redis can occasionally return empty page.
if len(it.cmd.page) > 0 {
2016-09-08 16:07:49 +03:00
return true
}
}
2016-04-13 11:52:47 +03:00
}
// Val returns the key/field at the current cursor position.
func (it *ScanIterator) Val() string {
var v string
it.mu.Lock()
2017-01-03 13:44:06 +03:00
if it.cmd.Err() == nil && it.pos > 0 && it.pos <= len(it.cmd.page) {
v = it.cmd.page[it.pos-1]
2016-04-13 11:52:47 +03:00
}
it.mu.Unlock()
return v
}