Skip to content

Commit ebba9ea

Browse files
authored
fix: cache no results when IPNI errors (#297)
If IPNI resolution fails then return no results and cache that response so that subsequent requests do not cause us to go back to our tiered IPNI finder again and cause cloudfront request costs.
1 parent 04a0289 commit ebba9ea

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

pkg/service/providerindex/providerindex.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/libp2p/go-libp2p/core/peer"
1919
"github.com/multiformats/go-multicodec"
2020
mh "github.com/multiformats/go-multihash"
21+
"github.com/storacha/go-libstoracha/digestutil"
2122
"github.com/storacha/go-libstoracha/ipnipublisher/publisher"
2223
"github.com/storacha/go-libstoracha/metadata"
2324
"github.com/storacha/go-ucanto/did"
@@ -272,16 +273,16 @@ func (pi *ProviderIndexService) fetchFromIPNI(ctx context.Context, s trace.Span,
272273

273274
findRes, err := pi.findClient.Find(ctx, mh)
274275
if err != nil {
275-
return nil, err
276-
}
277-
278-
for _, mhres := range findRes.MultihashResults {
279-
results = append(results, mhres.ProviderResults...)
280-
}
276+
pi.log.Warnf("finding %s in IPNI: %s", digestutil.Format(mh), err)
277+
} else {
278+
for _, mhres := range findRes.MultihashResults {
279+
results = append(results, mhres.ProviderResults...)
280+
}
281281

282-
results, err = filterCodecs(results, targetClaims)
283-
if err != nil {
284-
return nil, err
282+
results, err = filterCodecs(results, targetClaims)
283+
if err != nil {
284+
return nil, err
285+
}
285286
}
286287
if len(results) == 0 {
287288
pi.cacheNoProviderResults(ctx, s, mh, targetClaims)

pkg/service/providerindex/providerindex_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ func TestGetProviderResults(t *testing.T) {
279279
require.Error(t, err)
280280
})
281281

282-
t.Run("error fetching from IPNI returns an error", func(t *testing.T) {
282+
t.Run("error fetching from IPNI returns empty results", func(t *testing.T) {
283283
mockStore := types.NewMockProviderStore(t)
284284
mockNoProviderStore := types.NewMockNoProviderStore(t)
285285
mockIpniFinder := extmocks.NewMockIpniFinder(t)
@@ -294,10 +294,13 @@ func TestGetProviderResults(t *testing.T) {
294294
mockNoProviderStore.EXPECT().Members(extmocks.AnyContext, someHash).Return(nil, types.ErrKeyNotFound)
295295
mockLegacyClaims.EXPECT().Find(extmocks.AnyContext, someHash, []multicodec.Code{0}).Return(nil, nil)
296296
mockIpniFinder.EXPECT().Find(extmocks.AnyContext, someHash).Return(nil, errors.New("some error"))
297+
mockNoProviderStore.EXPECT().Add(extmocks.AnyContext, someHash, multicodec.Code(0)).Return(1, nil)
298+
mockNoProviderStore.EXPECT().SetExpirable(extmocks.AnyContext, someHash, true).Return(nil)
297299

298-
_, err := providerIndex.getProviderResults(context.Background(), someHash, []multicodec.Code{0})
300+
results, err := providerIndex.getProviderResults(context.Background(), someHash, []multicodec.Code{0})
299301

300-
require.Error(t, err)
302+
require.NoError(t, err)
303+
require.Empty(t, results)
301304
})
302305

303306
t.Run("error in legacy claims service returns an error", func(t *testing.T) {
@@ -323,7 +326,7 @@ func TestGetProviderResults(t *testing.T) {
323326
require.Error(t, err)
324327
})
325328

326-
t.Run("errors in both legacy claims service and IPNI returns wrapped error", func(t *testing.T) {
329+
t.Run("errors in both legacy claims service and IPNI no providers cache returns wrapped error", func(t *testing.T) {
327330
mockStore := types.NewMockProviderStore(t)
328331
mockNoProviderStore := types.NewMockNoProviderStore(t)
329332
mockIpniFinder := extmocks.NewMockIpniFinder(t)
@@ -337,17 +340,16 @@ func TestGetProviderResults(t *testing.T) {
337340

338341
// Simulate a cache miss.
339342
mockStore.EXPECT().Members(extmocks.AnyContext, someHash).Return(nil, types.ErrKeyNotFound)
340-
mockNoProviderStore.EXPECT().Members(extmocks.AnyContext, someHash).Return(nil, types.ErrKeyNotFound)
343+
mockNoProviderStore.EXPECT().Members(extmocks.AnyContext, someHash).Return(nil, errors.New("no providers error"))
341344

342345
// Both queries error.
343-
mockIpniFinder.EXPECT().Find(extmocks.AnyContext, someHash).Return(nil, errors.New("ipni error"))
344346
mockLegacyClaims.EXPECT().Find(extmocks.AnyContext, someHash, targetClaim).Return(nil, errors.New("legacy error"))
345347

346348
results, err := providerIndex.getProviderResults(context.Background(), someHash, targetClaim)
347349
require.Nil(t, results)
348350
require.Error(t, err)
349351
// Verify that the final error message contains both errors.
350-
require.Contains(t, err.Error(), "fetching from IPNI failed: ipni error")
352+
require.Contains(t, err.Error(), "fetching from IPNI failed: no providers error")
351353
require.Contains(t, err.Error(), "fetching from legacy services failed: legacy error")
352354
})
353355

@@ -482,6 +484,9 @@ func TestGetProviderResults(t *testing.T) {
482484
// Expect caching of the legacy result.
483485
mockStore.EXPECT().Add(extmocks.AnyContext, someHash, expectedResult).Return(1, nil)
484486
mockStore.EXPECT().SetExpirable(extmocks.AnyContext, someHash, true).Return(nil)
487+
// Expect caching no IPNI results
488+
mockNoProviderStore.EXPECT().Add(extmocks.AnyContext, someHash, multicodec.Code(metadata.LocationCommitmentID)).Return(1, nil)
489+
mockNoProviderStore.EXPECT().SetExpirable(extmocks.AnyContext, someHash, true).Return(nil)
485490

486491
results, err := providerIndex.getProviderResults(context.Background(), someHash, targetClaim)
487492
require.NoError(t, err)
@@ -543,6 +548,9 @@ func TestGetProviderResults(t *testing.T) {
543548
// Expect caching of the legacy result.
544549
mockStore.EXPECT().Add(extmocks.AnyContext, someHash, expectedResult).Return(1, nil)
545550
mockStore.EXPECT().SetExpirable(extmocks.AnyContext, someHash, true).Return(nil)
551+
// Expect caching no IPNI results
552+
mockNoProviderStore.EXPECT().Add(extmocks.AnyContext, someHash, multicodec.Code(metadata.LocationCommitmentID)).Return(1, nil)
553+
mockNoProviderStore.EXPECT().SetExpirable(extmocks.AnyContext, someHash, true).Return(nil)
546554

547555
resultChan := make(chan result.Result[[]model.ProviderResult, error], 1)
548556
go func() {

0 commit comments

Comments
 (0)