Skip to content

Commit 3218703

Browse files
Merge pull request #5162 from ipfs/feat/improve-preload
always try to read ahead by at least 5 blocks in the PBDagReader
2 parents 2dcb7f0 + 7fd3404 commit 3218703

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

unixfs/io/dagreader_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,41 @@ func TestSeekAndReadLarge(t *testing.T) {
122122
}
123123
}
124124

125+
func TestReadAndCancel(t *testing.T) {
126+
dserv := testu.GetDAGServ()
127+
inbuf := make([]byte, 20000)
128+
rand.Read(inbuf)
129+
130+
node := testu.GetNode(t, dserv, inbuf, testu.UseProtoBufLeaves)
131+
ctx, closer := context.WithCancel(context.Background())
132+
defer closer()
133+
134+
reader, err := NewDagReader(ctx, node, dserv)
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
ctx, cancel := context.WithCancel(context.Background())
140+
buf := make([]byte, 100)
141+
_, err = reader.CtxReadFull(ctx, buf)
142+
if err != nil {
143+
t.Fatal(err)
144+
}
145+
if !bytes.Equal(buf, inbuf[0:100]) {
146+
t.Fatal("read failed")
147+
}
148+
cancel()
149+
150+
b, err := ioutil.ReadAll(reader)
151+
if err != nil {
152+
t.Fatal(err)
153+
}
154+
155+
if !bytes.Equal(inbuf[100:], b) {
156+
t.Fatal("buffers not equal")
157+
}
158+
}
159+
125160
func TestRelativeSeek(t *testing.T) {
126161
dserv := testu.GetDAGServ()
127162
ctx, closer := context.WithCancel(context.Background())

unixfs/io/pbdagreader.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,13 @@ func NewPBFileReader(ctx context.Context, n *mdag.ProtoNode, file *ft.FSNode, se
6363

6464
const preloadSize = 10
6565

66-
func (dr *PBDagReader) preloadNextNodes(ctx context.Context) {
67-
beg := dr.linkPosition
66+
func (dr *PBDagReader) preload(ctx context.Context, beg int) {
6867
end := beg + preloadSize
6968
if end >= len(dr.links) {
7069
end = len(dr.links)
7170
}
7271

73-
for i, p := range ipld.GetNodes(ctx, dr.serv, dr.links[beg:end]) {
74-
dr.promises[beg+i] = p
75-
}
72+
copy(dr.promises[beg:], ipld.GetNodes(ctx, dr.serv, dr.links[beg:end]))
7673
}
7774

7875
// precalcNextBuf follows the next link in line and loads it from the
@@ -87,15 +84,42 @@ func (dr *PBDagReader) precalcNextBuf(ctx context.Context) error {
8784
return io.EOF
8885
}
8986

90-
if dr.promises[dr.linkPosition] == nil {
91-
dr.preloadNextNodes(ctx)
87+
// If we drop to <= preloadSize/2 preloading nodes, preload the next 10.
88+
for i := dr.linkPosition; i < dr.linkPosition+preloadSize/2 && i < len(dr.promises); i++ {
89+
// TODO: check if canceled.
90+
if dr.promises[i] == nil {
91+
dr.preload(ctx, i)
92+
break
93+
}
9294
}
9395

9496
nxt, err := dr.promises[dr.linkPosition].Get(ctx)
95-
if err != nil {
97+
dr.promises[dr.linkPosition] = nil
98+
switch err {
99+
case nil:
100+
case context.DeadlineExceeded, context.Canceled:
101+
err = ctx.Err()
102+
if err != nil {
103+
return ctx.Err()
104+
}
105+
// In this case, the context used to *preload* the node has been canceled.
106+
// We need to retry the load with our context and we might as
107+
// well preload some extra nodes while we're at it.
108+
//
109+
// Note: When using `Read`, this code will never execute as
110+
// `Read` will use the global context. It only runs if the user
111+
// explicitly reads with a custom context (e.g., by calling
112+
// `CtxReadFull`).
113+
dr.preload(ctx, dr.linkPosition)
114+
nxt, err = dr.promises[dr.linkPosition].Get(ctx)
115+
dr.promises[dr.linkPosition] = nil
116+
if err != nil {
117+
return err
118+
}
119+
default:
96120
return err
97121
}
98-
dr.promises[dr.linkPosition] = nil
122+
99123
dr.linkPosition++
100124

101125
switch nxt := nxt.(type) {

0 commit comments

Comments
 (0)