@@ -37,7 +37,6 @@ type contentProvider struct {
3737 stats * zoekt.Stats
3838
3939 // mutable
40- err error
4140 idx uint32
4241 _data []byte
4342 _nl []uint32
@@ -51,7 +50,6 @@ type contentProvider struct {
5150func (p * contentProvider ) setDocument (docID uint32 ) {
5251 fileStart := p .id .boundaries [docID ]
5352
54- p .err = nil
5553 p .idx = docID
5654 p .fileSize = p .id .boundaries [docID + 1 ] - fileStart
5755
@@ -60,18 +58,34 @@ func (p *contentProvider) setDocument(docID uint32) {
6058 p ._data = nil
6159}
6260
63- func (p * contentProvider ) setError (err error ) {
64- if p .err == nil {
65- p .err = err
61+ // panicCorrupt stops a search as soon as it detects a corrupt shard invariant.
62+ // searchOneShard recovers the panic at the shard boundary, logs the error with
63+ // the query and stack trace, and increments Stats.Crashes. Callers therefore get
64+ // partial results with an explicit crashed-shard signal rather than a silent
65+ // non-match.
66+ func (p * contentProvider ) panicCorrupt (err error ) {
67+ shard := "unknown"
68+ if p .id .file != nil {
69+ shard = p .id .file .Name ()
70+ }
71+ repo := "unknown"
72+ if p .idx < uint32 (len (p .id .repos )) {
73+ repoID := p .id .repos [p .idx ]
74+ if int (repoID ) < len (p .id .repoMetaData ) {
75+ repo = p .id .repoMetaData [repoID ].Name
76+ }
6677 }
78+ panic (fmt .Errorf ("corrupt shard %q while searching repository %q, document %d: %w" , shard , repo , p .idx , err ))
6779}
6880
6981func (p * contentProvider ) docSections () []DocumentSection {
7082 if p ._sects == nil {
7183 var sz uint32
7284 var err error
7385 p ._sects , sz , err = p .id .readDocSections (p .idx , p ._sectBuf )
74- p .setError (err )
86+ if err != nil {
87+ p .panicCorrupt (fmt .Errorf ("reading document sections: %w" , err ))
88+ }
7589 p .stats .ContentBytesLoaded += int64 (sz )
7690 p ._sectBuf = p ._sects
7791 }
@@ -83,7 +97,9 @@ func (p *contentProvider) newlines() newlines {
8397 var sz uint32
8498 var err error
8599 p ._nl , sz , err = p .id .readNewlines (p .idx , p ._nlBuf )
86- p .setError (err )
100+ if err != nil {
101+ p .panicCorrupt (fmt .Errorf ("reading newline offsets: %w" , err ))
102+ }
87103 p ._nlBuf = p ._nl
88104 p .stats .ContentBytesLoaded += int64 (sz )
89105 }
@@ -98,7 +114,9 @@ func (p *contentProvider) data(fileName bool) []byte {
98114 if p ._data == nil {
99115 var err error
100116 p ._data , err = p .id .readContents (p .idx )
101- p .setError (err )
117+ if err != nil {
118+ p .panicCorrupt (fmt .Errorf ("reading content: %w" , err ))
119+ }
102120 p .stats .FilesLoaded ++
103121 p .stats .ContentBytesLoaded += int64 (len (p ._data ))
104122 }
@@ -128,7 +146,7 @@ func (p *contentProvider) findOffset(filename bool, r uint32) uint32 {
128146
129147 if p .id .metaData .PlainASCII {
130148 if r > fileEndByte - fileStartByte {
131- p .setError (fmt .Errorf ("corrupt index: document %d % s rune offset %d is after file size %d" , p . idx , kind , r , fileEndByte - fileStartByte ))
149+ p .panicCorrupt (fmt .Errorf ("% s rune offset %d is after file size %d" , kind , r , fileEndByte - fileStartByte ))
132150 return 0
133151 }
134152 return r
@@ -139,7 +157,7 @@ func (p *contentProvider) findOffset(filename bool, r uint32) uint32 {
139157 absR64 += uint64 (runeEnds [p .idx - 1 ])
140158 }
141159 if absR64 > uint64 (^ uint32 (0 )) {
142- p .setError (fmt .Errorf ("corrupt index: document %d % s rune offset %d overflows the corpus rune offset" , p . idx , kind , r ))
160+ p .panicCorrupt (fmt .Errorf ("% s rune offset %d overflows the corpus rune offset" , kind , r ))
143161 return 0
144162 }
145163 absR := uint32 (absR64 )
@@ -150,26 +168,26 @@ func (p *contentProvider) findOffset(filename bool, r uint32) uint32 {
150168
151169 if filename {
152170 if byteOff > uint64 (len (p .id .fileNameContent )) {
153- p .setError (fmt .Errorf ("corrupt index: document %d filename rune offset %d maps to byte offset %d past filename data size %d" , p . idx , absR , byteOff , len (p .id .fileNameContent )))
171+ p .panicCorrupt (fmt .Errorf ("filename rune offset %d maps to byte offset %d past filename data size %d" , absR , byteOff , len (p .id .fileNameContent )))
154172 return 0
155173 }
156174 data = p .id .fileNameContent [uint32 (byteOff ):]
157175 } else {
158176 corpusEnd := p .id .boundaries [len (p .id .boundaries )- 1 ]
159177 if byteOff > uint64 (corpusEnd ) {
160- p .setError (fmt .Errorf ("corrupt index: document %d content rune offset %d maps to byte offset %d past content data size %d" , p . idx , absR , byteOff , corpusEnd ))
178+ p .panicCorrupt (fmt .Errorf ("content rune offset %d maps to byte offset %d past content data size %d" , absR , byteOff , corpusEnd ))
161179 return 0
162180 }
163181 var err error
164182 data , err = p .id .readContentSlice (uint32 (byteOff ), 3 * runeOffsetFrequency )
165183 if err != nil {
166- p .setError (fmt .Errorf ("corrupt index: document %d content rune offset %d cannot load bytes at offset %d: %w" , p . idx , absR , byteOff , err ))
184+ p .panicCorrupt (fmt .Errorf ("content rune offset %d cannot load bytes at offset %d: %w" , absR , byteOff , err ))
167185 return 0
168186 }
169187 }
170188 for left > 0 {
171189 if len (data ) == 0 {
172- p .setError (fmt .Errorf ("corrupt index: document %d % s rune offset %d has no decode bytes at byte offset %d" , p . idx , kind , absR , byteOff ))
190+ p .panicCorrupt (fmt .Errorf ("% s rune offset %d has no decode bytes at byte offset %d" , kind , absR , byteOff ))
173191 return 0
174192 }
175193 _ , sz := utf8 .DecodeRune (data )
@@ -179,11 +197,11 @@ func (p *contentProvider) findOffset(filename bool, r uint32) uint32 {
179197 }
180198
181199 if byteOff < uint64 (fileStartByte ) {
182- p .setError (fmt .Errorf ("corrupt index: document %d % s rune offset %d maps to byte offset %d before file start %d" , p . idx , kind , absR , byteOff , fileStartByte ))
200+ p .panicCorrupt (fmt .Errorf ("% s rune offset %d maps to byte offset %d before file start %d" , kind , absR , byteOff , fileStartByte ))
183201 return 0
184202 }
185203 if byteOff > uint64 (fileEndByte ) {
186- p .setError (fmt .Errorf ("corrupt index: document %d % s rune offset %d maps to byte offset %d after file end %d" , p . idx , kind , absR , byteOff , fileEndByte ))
204+ p .panicCorrupt (fmt .Errorf ("% s rune offset %d maps to byte offset %d after file end %d" , kind , absR , byteOff , fileEndByte ))
187205 return 0
188206 }
189207
0 commit comments