Skip to content
This repository has been archived by the owner on Nov 24, 2022. It is now read-only.

Commit

Permalink
switch to the production CID gravity API
Browse files Browse the repository at this point in the history
Signed-off-by: Merlin Ran <[email protected]>
  • Loading branch information
merlinran committed Sep 22, 2021
1 parent 319ef9f commit 642cde6
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 18 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ❤️
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
},

Expand Down
39 changes: 23 additions & 16 deletions service/pricing/cid_gravity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
8 changes: 7 additions & 1 deletion service/pricing/cid_gravity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 642cde6

Please sign in to comment.