From 642cde679c11e77b951268a421608ae03eaa9725 Mon Sep 17 00:00:00 2001 From: Merlin Ran Date: Wed, 22 Sep 2021 07:23:08 -0700 Subject: [PATCH] switch to the production CID gravity API Signed-off-by: Merlin Ran --- README.md | 7 ++++++ main.go | 2 +- service/pricing/cid_gravity.go | 39 +++++++++++++++++------------ service/pricing/cid_gravity_test.go | 8 +++++- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ac59612..73c8e8c 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,13 @@ If you have `bidbot` daemon running, we recommend you explore the following avai Do you think `bidbot` can have other commands that would make your life easier? We're interested in knowing about that! +# CID gravity integration + +[CID gravity](https://www.cidgravity.com) is a tool for storage providers to manage clients and price tiers. If integrated, bidbot can bid based on the configuration there, rather than locally configured `--ask-price` and `--verified-ask-price`. There are only two parameters involved. + +* `--cid-gravity-key`. You should be able to generate one by clicking the "Integrations" menu item from the CID gravity console. +* `--cid-gravity-default-reject`. By default, if bidbot can not reach the CID gravity API for some reason, it bids based on the locally configured price. If you want it to stop bidding in that case, set this to true. + ## Contributing Pull requests and bug reports are very welcome ❤️ diff --git a/main.go b/main.go index e00027b..3f1f0bf 100644 --- a/main.go +++ b/main.go @@ -174,7 +174,7 @@ Zero means no limits`, { Name: "cid-gravity-default-reject", - DefValue: true, + DefValue: false, Description: "When CID gravity is enabled, stop bidding if there's any problem loading cid-gravity pricing rules.", }, diff --git a/service/pricing/cid_gravity.go b/service/pricing/cid_gravity.go index eb4a4ef..b15a035 100644 --- a/service/pricing/cid_gravity.go +++ b/service/pricing/cid_gravity.go @@ -15,16 +15,15 @@ import ( ) const ( - // Use cached rules if they are loaded no earlier than this period. - cidGravityCachePeriod = time.Minute - // If loading rules from CID gravity API takes longer than this timeout, turn it into background and use the - // cached rules for prices lookup. + // If loading rules from CID gravity API takes longer than this timeout, turn it into background. cidGravityLoadRulesTimeout = 5 * time.Second ) var ( log = golog.Logger("bidbot/pricing") - cidGravityAPIUrl = "https://staging-api.cidgravity.com/api/integrations/bidbot" + cidGravityAPIUrl = "https://api.cidgravity.com/api/integrations/bidbot" + // Use cached rules if they are loaded no earlier than this period. + cidGravityCachePeriod = time.Minute ) // CIDGravityRules is the format CID gravity API returns. Needs to be public to unmarshal JSON payload. Do not use. @@ -83,15 +82,19 @@ func newClientRulesFor(apiKey, clientAddress string) *clientRules { } // PricesFor checks the CID gravity rules for one specific client address and returns the resolved prices for the -// auction. The rules are cached locally for some time. It returns valid = false if the rules were never loaded from the -// CID gravity API. +// auction. The rules are cached locally for some time. It returns valid = false if the cached rules were expired but +// couldn't be reloaded from the CID gravity API in time. func (cg *clientRules) PricesFor(auction *pb.Auction) (prices ResolvedPrices, valid bool) { - cg.maybeReloadRules(cidGravityAPIUrl, cidGravityLoadRulesTimeout, cidGravityCachePeriod) + valid = cg.maybeReloadRules(cidGravityAPIUrl, cidGravityLoadRulesTimeout, cidGravityCachePeriod) + if !valid { + return + } rules := cg.rules.Load() + // preventive but should not happen if rules == nil { + valid = false return } - valid = true if rules.(*CIDGravityRules).Blocked { return } @@ -115,19 +118,20 @@ func (cg *clientRules) PricesFor(auction *pb.Auction) (prices ResolvedPrices, va return } -// maybeReloadRules reloads rules from the CID gravity API if the cache is expired. It reloads only once if being called -// concurrently. When loading takes more than the timeout, reloading turns to background and the method returns. -func (cg *clientRules) maybeReloadRules(url string, timeout time.Duration, cachePeriod time.Duration) { +// maybeReloadRules reloads rules from the CID gravity API if the cache expires. It reloads only once if being called +// concurrently. When loading takes more than the timeout, reloading turns to background and the method returns. The +// return value indicates if the cached rules are valid. +func (cg *clientRules) maybeReloadRules(url string, timeout time.Duration, cachePeriod time.Duration) bool { cg.lkLoadRules.Lock() defer cg.lkLoadRules.Unlock() lastUpdated := cg.rulesLastUpdated.Load().(time.Time) if time.Since(lastUpdated) < cachePeriod { - return + return true } // use buffered channel to avoid blocking the goroutine when the receiver is gone. chErr := make(chan error, 1) go func() { - chErr <- func() error { + err := func() error { body := fmt.Sprintf(`{"clientAddress":"%s"}`, cg.clientAddress) req, err := http.NewRequest(http.MethodGet, url, strings.NewReader(body)) if err != nil { @@ -166,13 +170,16 @@ func (cg *clientRules) maybeReloadRules(url string, timeout time.Duration, cache cg.rules.Store(&rules) return nil }() + log.Errorf("loading rules from API: %v", err) + chErr <- err close(chErr) }() select { case err := <-chErr: - if err != nil { - log.Errorf("loading rules from API: %v", err) + if err == nil { + return true } case <-time.After(timeout): } + return false } diff --git a/service/pricing/cid_gravity_test.go b/service/pricing/cid_gravity_test.go index 3ef00de..ce987be 100644 --- a/service/pricing/cid_gravity_test.go +++ b/service/pricing/cid_gravity_test.go @@ -15,6 +15,7 @@ import ( func TestPriceFor(t *testing.T) { cidGravityAPIUrl = "http://localhost:invalid" // do not care about rules loading + cidGravityCachePeriod = time.Second rules := &CIDGravityRules{ PricingRules: []struct { Verified bool @@ -64,11 +65,12 @@ func TestPriceFor(t *testing.T) { cg := newClientRulesFor("key", auction.ClientAddress) rp, valid := cg.PricesFor(auction) - assert.False(t, valid) + assert.False(t, valid, "prices should be invalid before the rules are loaded") assert.False(t, rp.UnverifiedPriceValid) assert.False(t, rp.VerifiedPriceValid) cg.rules.Store(rules) + cg.rulesLastUpdated.Store(time.Now()) rp, valid = cg.PricesFor(auction) assert.True(t, valid) assert.False(t, rp.UnverifiedPriceValid) @@ -103,6 +105,10 @@ func TestPriceFor(t *testing.T) { assert.True(t, valid) assert.False(t, rp.UnverifiedPriceValid) assert.False(t, rp.VerifiedPriceValid) + + time.Sleep(cidGravityCachePeriod) + rp, valid = cg.PricesFor(auction) + assert.False(t, valid, "prices should be invalid when the rules expire") } func TestMaybeReloadRules(t *testing.T) {