mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
swarm/storage/feed/lookup: First LE that works
This commit is contained in:
parent
db83ba4067
commit
9f31234beb
2 changed files with 166 additions and 11 deletions
|
|
@ -20,7 +20,10 @@ so they can be found
|
||||||
*/
|
*/
|
||||||
package lookup
|
package lookup
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
const maxuint64 = ^uint64(0)
|
const maxuint64 = ^uint64(0)
|
||||||
|
|
||||||
|
|
@ -43,7 +46,7 @@ type Algorithm func(ctx context.Context, now uint64, hint Epoch, read ReadFunc)
|
||||||
// read() will be called on each lookup attempt
|
// read() will be called on each lookup attempt
|
||||||
// Returns an error only if read() returns an error
|
// Returns an error only if read() returns an error
|
||||||
// Returns nil if an update was not found
|
// Returns nil if an update was not found
|
||||||
var Lookup Algorithm = FluzCapacitorAlgorithm
|
var Lookup Algorithm = LongEarthAlgorithm
|
||||||
|
|
||||||
// ReadFunc is a handler called by Lookup each time it attempts to find a value
|
// ReadFunc is a handler called by Lookup each time it attempts to find a value
|
||||||
// It should return <nil> if a value is not found
|
// It should return <nil> if a value is not found
|
||||||
|
|
@ -180,4 +183,133 @@ func FluzCapacitorAlgorithm(ctx context.Context, now uint64, hint Epoch, read Re
|
||||||
}
|
}
|
||||||
t = base - 1
|
t = base - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepFunc func(ctx context.Context, t uint64, hint Epoch) interface{}
|
||||||
|
|
||||||
|
func LongEarthAlgorithm(ctx context.Context, now uint64, hint Epoch, read ReadFunc) (interface{}, error) {
|
||||||
|
|
||||||
|
errc := make(chan error)
|
||||||
|
|
||||||
|
var step StepFunc
|
||||||
|
step = func(ctxS context.Context, t uint64, hint Epoch) interface{} {
|
||||||
|
var valueA, valueB, valueR interface{}
|
||||||
|
|
||||||
|
ctxR, cancelR := context.WithCancel(ctxS)
|
||||||
|
ctxA, cancelA := context.WithCancel(ctxS)
|
||||||
|
ctxB, cancelB := context.WithCancel(ctxS)
|
||||||
|
|
||||||
|
epoch := GetNextEpoch(hint, t)
|
||||||
|
|
||||||
|
lookAhead := func() {
|
||||||
|
valueA = step(ctxA, t, epoch)
|
||||||
|
if valueA != nil {
|
||||||
|
cancelB()
|
||||||
|
cancelR()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lookBack := func() {
|
||||||
|
var err error
|
||||||
|
if epoch.Base() == hint.Base() {
|
||||||
|
// we have reached the hint itself
|
||||||
|
if hint == worstHint {
|
||||||
|
valueB = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check it out
|
||||||
|
valueB, err = read(ctxB, hint, now)
|
||||||
|
if valueB != nil || err == context.Canceled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// bad hint.
|
||||||
|
valueB = step(ctxB, hint.Base(), worstHint)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
base := epoch.Base()
|
||||||
|
if base == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valueB = step(ctxB, base-1, hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer cancelR()
|
||||||
|
var err error
|
||||||
|
valueR, err = read(ctxR, epoch, now)
|
||||||
|
if valueR == nil {
|
||||||
|
cancelA()
|
||||||
|
} else {
|
||||||
|
cancelB()
|
||||||
|
}
|
||||||
|
if err != nil && err != context.Canceled {
|
||||||
|
errc <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer cancelA()
|
||||||
|
|
||||||
|
if epoch.Level == LowestLevel || epoch.Equals(hint) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(250 * time.Millisecond):
|
||||||
|
lookAhead()
|
||||||
|
case <-ctxR.Done():
|
||||||
|
if valueR != nil {
|
||||||
|
lookAhead()
|
||||||
|
}
|
||||||
|
case <-ctxA.Done():
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer cancelB()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(250 * time.Millisecond):
|
||||||
|
lookBack()
|
||||||
|
case <-ctxR.Done():
|
||||||
|
if valueR == nil {
|
||||||
|
lookBack()
|
||||||
|
}
|
||||||
|
case <-ctxB.Done():
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-ctxA.Done()
|
||||||
|
if valueA != nil {
|
||||||
|
return valueA
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ctxR.Done()
|
||||||
|
if valueR != nil {
|
||||||
|
return valueR
|
||||||
|
}
|
||||||
|
<-ctxB.Done()
|
||||||
|
return valueB
|
||||||
|
}
|
||||||
|
|
||||||
|
var value interface{}
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
value = step(ctx, now, hint)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return value, nil
|
||||||
|
case err := <-errc:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/log"
|
"github.com/ethereum/go-ethereum/swarm/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
||||||
|
|
@ -31,6 +33,10 @@ type Data struct {
|
||||||
Time uint64
|
Time uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Data) String() string {
|
||||||
|
return fmt.Sprintf("%d-%d", d.Payload, d.Time)
|
||||||
|
}
|
||||||
|
|
||||||
type Store map[lookup.EpochID]*Data
|
type Store map[lookup.EpochID]*Data
|
||||||
|
|
||||||
func write(store Store, epoch lookup.Epoch, value *Data) {
|
func write(store Store, epoch lookup.Epoch, value *Data) {
|
||||||
|
|
@ -50,15 +56,21 @@ const Day = 60 * 60 * 24
|
||||||
const Year = Day * 365
|
const Year = Day * 365
|
||||||
const Month = Day * 30
|
const Month = Day * 30
|
||||||
|
|
||||||
func makeReadFunc(store Store, counter *int) lookup.ReadFunc {
|
func makeReadFunc(store Store, counter *int32) lookup.ReadFunc {
|
||||||
return func(ctx context.Context, epoch lookup.Epoch, now uint64) (interface{}, error) {
|
return func(ctx context.Context, epoch lookup.Epoch, now uint64) (interface{}, error) {
|
||||||
*counter++
|
atomic.AddInt32(counter, 1)
|
||||||
|
select {
|
||||||
|
case <-time.After(1000 * time.Millisecond):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
data := store[epoch.ID()]
|
data := store[epoch.ID()]
|
||||||
var valueStr string
|
var valueStr string
|
||||||
if data != nil {
|
if data != nil {
|
||||||
valueStr = fmt.Sprintf("%d", data.Payload)
|
valueStr = fmt.Sprintf("%d", data.Payload)
|
||||||
}
|
}
|
||||||
log.Debug("Read: %d-%d, value='%s'\n", epoch.Base(), epoch.Level, valueStr)
|
log.Debug("Read: %d-%d, value='%s'\n", epoch.Base(), epoch.Level, valueStr)
|
||||||
|
//fmt.Printf("Read: %d-%d, value='%s'\n", epoch.Base(), epoch.Level, valueStr)
|
||||||
if data != nil && data.Time <= now {
|
if data != nil && data.Time <= now {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
@ -67,9 +79,8 @@ func makeReadFunc(store Store, counter *int) lookup.ReadFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLookup(t *testing.T) {
|
func TestLookup(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
|
|
||||||
// write an update every month for 12 months 3 years ago and then silence for two years
|
// write an update every month for 12 months 3 years ago and then silence for two years
|
||||||
|
|
@ -93,6 +104,7 @@ func TestLookup(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("readcount=%d\n", readCount)
|
||||||
|
|
||||||
readCountWithoutHint := readCount
|
readCountWithoutHint := readCount
|
||||||
|
|
||||||
|
|
@ -142,7 +154,7 @@ func TestLookup(t *testing.T) {
|
||||||
func TestOneUpdateAt0(t *testing.T) {
|
func TestOneUpdateAt0(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
|
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
now := uint64(1533903729)
|
now := uint64(1533903729)
|
||||||
|
|
@ -167,7 +179,7 @@ func TestOneUpdateAt0(t *testing.T) {
|
||||||
func TestBadHint(t *testing.T) {
|
func TestBadHint(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
|
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
now := uint64(1533903729)
|
now := uint64(1533903729)
|
||||||
|
|
@ -299,7 +311,7 @@ func TestContextCancellation(t *testing.T) {
|
||||||
func TestLookupFail(t *testing.T) {
|
func TestLookupFail(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
|
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
now := uint64(1533903729)
|
now := uint64(1533903729)
|
||||||
|
|
@ -324,7 +336,7 @@ func TestLookupFail(t *testing.T) {
|
||||||
func TestHighFreqUpdates(t *testing.T) {
|
func TestHighFreqUpdates(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
|
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
now := uint64(1533903729)
|
now := uint64(1533903729)
|
||||||
|
|
@ -388,7 +400,7 @@ func TestHighFreqUpdates(t *testing.T) {
|
||||||
func TestSparseUpdates(t *testing.T) {
|
func TestSparseUpdates(t *testing.T) {
|
||||||
|
|
||||||
store := make(Store)
|
store := make(Store)
|
||||||
readCount := 0
|
var readCount int32 = 0
|
||||||
readFunc := makeReadFunc(store, &readCount)
|
readFunc := makeReadFunc(store, &readCount)
|
||||||
|
|
||||||
// write an update every 5 years 3 times starting in Jan 1st 1970 and then silence
|
// write an update every 5 years 3 times starting in Jan 1st 1970 and then silence
|
||||||
|
|
@ -513,3 +525,14 @@ func CookGetNextLevelTests(t *testing.T) {
|
||||||
}
|
}
|
||||||
fmt.Println(st)
|
fmt.Println(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTest(t *testing.T) {
|
||||||
|
hint := lookup.Epoch{
|
||||||
|
Time: 20,
|
||||||
|
Level: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
e := lookup.GetNextEpoch(hint, 21)
|
||||||
|
|
||||||
|
fmt.Println(e)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue