@@ -83,27 +83,30 @@ func (c *cachedSeekable) OpenRangeReader(ctx context.Context, off int64, length
8383 attribute .Bool ("compressed" , compressed ),
8484 ))
8585
86- if compressed {
87- rc , err := c .openReaderCompressed (ctx , off , frameTable )
88- if err != nil {
89- recordError (span , err )
90- span .End ()
91-
92- return nil , err
86+ var rc io.ReadCloser
87+ var err error
88+ switch {
89+ case compressed :
90+ rc , err = c .openReaderCompressed (ctx , off , frameTable )
91+ default :
92+ if err = c .validateReadParams (length , off ); err == nil {
93+ rc , err = c .openReaderUncompressed (ctx , off , length )
9394 }
94-
95- rc = withSpan (rc , span )
96-
97- return rc , nil
9895 }
9996
100- if err := c . validateReadParams ( length , off ); err != nil {
97+ if err != nil {
10198 recordError (span , err )
10299 span .End ()
103100
104101 return nil , err
105102 }
106103
104+ return newSpanReader (rc , span , ctx ), nil
105+ }
106+
107+ // openReaderUncompressed is the sequential NFS-then-remote path for
108+ // uncompressed chunks.
109+ func (c * cachedSeekable ) openReaderUncompressed (ctx context.Context , off , length int64 ) (io.ReadCloser , error ) {
107110 timer := cacheSlabReadTimerFactory .Begin (
108111 attribute .String (nfsCacheOperationAttr , nfsCacheOperationAttrReadAt ),
109112 attribute .Bool ("compressed" , false ),
@@ -116,10 +119,7 @@ func (c *cachedSeekable) OpenRangeReader(ctx context.Context, off int64, length
116119 recordCacheRead (ctx , true , length , cacheTypeSeekable , cacheOpOpenRangeReader )
117120 timer .Success (ctx , length )
118121
119- rc := io .ReadCloser (& fsRangeReadCloser {Reader : io .NewSectionReader (fp , 0 , length ), file : fp })
120- rc = withSpan (rc , span )
121-
122- return rc , nil
122+ return newSectionReader (fp , 0 , length ), nil
123123 }
124124
125125 if ! os .IsNotExist (err ) {
@@ -130,50 +130,23 @@ func (c *cachedSeekable) OpenRangeReader(ctx context.Context, off int64, length
130130
131131 rc , err := c .inner .OpenRangeReader (ctx , off , length , nil )
132132 if err != nil {
133- recordError (span , err )
134- span .End ()
135-
136133 return nil , fmt .Errorf ("failed to open inner range reader: %w" , err )
137134 }
138135
139136 recordCacheRead (ctx , false , length , cacheTypeSeekable , cacheOpOpenRangeReader )
140137
141138 if ! skipCacheWriteback (ctx ) {
142- rc = newCacheWriteThroughReader (rc , c , ctx , off , length , chunkPath )
139+ rc = newCacheWritebackReader (rc , c , ctx , off , length , chunkPath )
143140 }
144141
145- rc = withSpan (rc , span )
146-
147142 return rc , nil
148143}
149144
150- // withSpan wraps a reader with an OTEL span that ends on Close.
151- func withSpan (rc io.ReadCloser , span trace.Span ) io.ReadCloser {
152- return & spanReadCloser {inner : rc , span : span }
153- }
154-
155- type spanReadCloser struct {
156- inner io.ReadCloser
157- span trace.Span
158- }
159-
160- func (r * spanReadCloser ) Read (p []byte ) (int , error ) {
161- return r .inner .Read (p )
162- }
163-
164- func (r * spanReadCloser ) Close () error {
165- err := r .inner .Close ()
166- recordError (r .span , err )
167- r .span .End ()
168-
169- return err
170- }
171-
172- // newCacheWriteThroughReader wraps a reader, buffering all data read through it.
173- // On Close, it asynchronously writes the buffered data to the NFS cache only
174- // if the total bytes read match the expected length (to avoid caching truncated data).
175- func newCacheWriteThroughReader (inner io.ReadCloser , cache * cachedSeekable , ctx context.Context , off , expectedLen int64 , chunkPath string ) io.ReadCloser {
176- return & cacheWriteThroughReader {
145+ // writebackReader buffers all data read through it. On Close, it
146+ // asynchronously writes the buffered data to the NFS cache only if the
147+ // total bytes read match the expected length (to avoid caching truncated data).
148+ func newCacheWritebackReader (inner io.ReadCloser , cache * cachedSeekable , ctx context.Context , off , expectedLen int64 , chunkPath string ) io.ReadCloser {
149+ return & cacheWritebackReader {
177150 inner : inner ,
178151 buf : bytes .NewBuffer (make ([]byte , 0 , expectedLen )),
179152 cache : cache ,
@@ -184,7 +157,7 @@ func newCacheWriteThroughReader(inner io.ReadCloser, cache *cachedSeekable, ctx
184157 }
185158}
186159
187- type cacheWriteThroughReader struct {
160+ type cacheWritebackReader struct {
188161 inner io.ReadCloser
189162 buf * bytes.Buffer
190163 cache * cachedSeekable
@@ -194,7 +167,7 @@ type cacheWriteThroughReader struct {
194167 chunkPath string
195168}
196169
197- func (r * cacheWriteThroughReader ) Read (p []byte ) (int , error ) {
170+ func (r * cacheWritebackReader ) Read (p []byte ) (int , error ) {
198171 n , err := r .inner .Read (p )
199172 if n > 0 {
200173 r .buf .Write (p [:n ])
@@ -203,12 +176,11 @@ func (r *cacheWriteThroughReader) Read(p []byte) (int, error) {
203176 return n , err
204177}
205178
206- func (r * cacheWriteThroughReader ) Close () error {
179+ func (r * cacheWritebackReader ) Close () error {
207180 closeErr := r .inner .Close ()
208181
209182 // Only cache when the total bytes read match the expected length.
210- // Unlike ReadAt where io.EOF can justify a short read (last chunk),
211- // a streaming reader always ends with EOF regardless of whether the
183+ // A streaming reader always ends with EOF regardless of whether the
212184 // data was truncated, so the byte count is the only reliable check.
213185 if isCompleteRead (r .buf .Len (), int (r .expectedLen ), nil ) {
214186 data := make ([]byte , r .buf .Len ())
0 commit comments