Skip to content

Commit 334d6cc

Browse files
authored
[backport] poet caching opts (#6201)
* configure poet /v1/info cache ttl in presets (#6198) ## Motivation For the cache to work, it needs to have non-zero TTL set * cache poet's /v1/pow_params with TTL (#6199) ## Motivation Similarly to /v1/info, we query /v1/pow_params very often (once per submit per node ID). As the contents returned from this endpoint change rarely (once per epoch), it makes sense to cache the result. * update changelog
1 parent 52b14e6 commit 334d6cc

File tree

8 files changed

+102
-26
lines changed

8 files changed

+102
-26
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
See [RELEASE](./RELEASE.md) for workflow instructions.
44

5+
## Release v1.6.6
6+
7+
### Improvements
8+
9+
* [#6198](https://github.com/spacemeshos/go-spacemesh/pull/6198) Configure default TTL for caching poet's /v1/info
10+
* [#6199](https://github.com/spacemeshos/go-spacemesh/pull/6199) Cache poet's /v1/pow_params
11+
512
## Release v1.6.5
613

714
### Improvements

activation/activation.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type PoetConfig struct {
4646
RequestRetryDelay time.Duration `mapstructure:"retry-delay"`
4747
PositioningATXSelectionTimeout time.Duration `mapstructure:"positioning-atx-selection-timeout"`
4848
CertifierInfoCacheTTL time.Duration `mapstructure:"certifier-info-cache-ttl"`
49+
PowParamsCacheTTL time.Duration `mapstructure:"pow-params-cache-ttl"`
4950
MaxRequestRetries int `mapstructure:"retry-max"`
5051
}
5152

@@ -54,6 +55,7 @@ func DefaultPoetConfig() PoetConfig {
5455
RequestRetryDelay: 400 * time.Millisecond,
5556
MaxRequestRetries: 10,
5657
CertifierInfoCacheTTL: 5 * time.Minute,
58+
PowParamsCacheTTL: 5 * time.Minute,
5759
}
5860
}
5961

activation/poet.go

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,29 @@ func (c *HTTPPoetClient) req(ctx context.Context, method, path string, reqBody,
333333
}
334334

335335
type certifierInfo struct {
336-
obtained time.Time
337-
url *url.URL
338-
pubkey []byte
336+
url *url.URL
337+
pubkey []byte
338+
}
339+
340+
type cachedData[T any] struct {
341+
mu sync.Mutex
342+
data T
343+
exp time.Time
344+
ttl time.Duration
345+
}
346+
347+
func (c *cachedData[T]) get(init func() (T, error)) (T, error) {
348+
c.mu.Lock()
349+
defer c.mu.Unlock()
350+
if time.Now().Before(c.exp) {
351+
return c.data, nil
352+
}
353+
d, err := init()
354+
if err == nil {
355+
c.data = d
356+
c.exp = time.Now().Add(c.ttl)
357+
}
358+
return d, err
339359
}
340360

341361
// poetService is a higher-level interface to communicate with a PoET service.
@@ -353,9 +373,8 @@ type poetService struct {
353373

354374
certifier certifierService
355375

356-
certifierInfoTTL time.Duration
357-
certifierInfo certifierInfo
358-
certifierInfoMutex sync.Mutex
376+
certifierInfoCache cachedData[*certifierInfo]
377+
powParamsCache cachedData[*PoetPowParams]
359378
}
360379

361380
type PoetServiceOpt func(*poetService)
@@ -394,12 +413,13 @@ func NewPoetServiceWithClient(
394413
opts ...PoetServiceOpt,
395414
) *poetService {
396415
poetClient := &poetService{
397-
db: db,
398-
logger: logger,
399-
client: client,
400-
requestTimeout: cfg.RequestTimeout,
401-
certifierInfoTTL: cfg.CertifierInfoCacheTTL,
402-
proofMembers: make(map[string][]types.Hash32, 1),
416+
db: db,
417+
logger: logger,
418+
client: client,
419+
requestTimeout: cfg.RequestTimeout,
420+
certifierInfoCache: cachedData[*certifierInfo]{ttl: cfg.CertifierInfoCacheTTL},
421+
powParamsCache: cachedData[*PoetPowParams]{ttl: cfg.PowParamsCacheTTL},
422+
proofMembers: make(map[string][]types.Hash32, 1),
403423
}
404424

405425
for _, opt := range opts {
@@ -435,7 +455,7 @@ func (c *poetService) authorize(
435455
logger.Debug("querying for poet pow parameters")
436456
powCtx, cancel := withConditionalTimeout(ctx, c.requestTimeout)
437457
defer cancel()
438-
powParams, err := c.client.PowParams(powCtx)
458+
powParams, err := c.powParams(powCtx)
439459
if err != nil {
440460
return nil, &PoetSvcUnstableError{msg: "failed to get PoW params", source: err}
441461
}
@@ -552,19 +572,22 @@ func (c *poetService) recertify(ctx context.Context, id types.NodeID) (*certifie
552572
}
553573

554574
func (c *poetService) getCertifierInfo(ctx context.Context) (*url.URL, []byte, error) {
555-
c.certifierInfoMutex.Lock()
556-
defer c.certifierInfoMutex.Unlock()
557-
if time.Since(c.certifierInfo.obtained) < c.certifierInfoTTL {
558-
return c.certifierInfo.url, c.certifierInfo.pubkey, nil
559-
}
560-
url, pubkey, err := c.client.CertifierInfo(ctx)
575+
info, err := c.certifierInfoCache.get(func() (*certifierInfo, error) {
576+
url, pubkey, err := c.client.CertifierInfo(ctx)
577+
if err != nil {
578+
return nil, fmt.Errorf("getting certifier info: %w", err)
579+
}
580+
return &certifierInfo{url: url, pubkey: pubkey}, nil
581+
})
561582
if err != nil {
562-
return nil, nil, fmt.Errorf("getting certifier info: %w", err)
563-
}
564-
c.certifierInfo = certifierInfo{
565-
obtained: time.Now(),
566-
url: url,
567-
pubkey: pubkey,
583+
return nil, nil, err
568584
}
569-
return c.certifierInfo.url, c.certifierInfo.pubkey, nil
585+
586+
return info.url, info.pubkey, nil
587+
}
588+
589+
func (c *poetService) powParams(ctx context.Context) (*PoetPowParams, error) {
590+
return c.powParamsCache.get(func() (*PoetPowParams, error) {
591+
return c.client.PowParams(ctx)
592+
})
570593
}

activation/poet_client_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,37 @@ func TestPoetService_CachesCertifierInfo(t *testing.T) {
421421
})
422422
}
423423
}
424+
425+
func TestPoetService_CachesPowParams(t *testing.T) {
426+
t.Parallel()
427+
type test struct {
428+
name string
429+
ttl time.Duration
430+
}
431+
for _, tc := range []test{
432+
{name: "cache enabled", ttl: time.Hour},
433+
{name: "cache disabled"},
434+
} {
435+
t.Run(tc.name, func(t *testing.T) {
436+
t.Parallel()
437+
cfg := DefaultPoetConfig()
438+
cfg.PowParamsCacheTTL = tc.ttl
439+
client := NewMockPoetClient(gomock.NewController(t))
440+
poet := NewPoetServiceWithClient(nil, client, cfg, zaptest.NewLogger(t))
441+
442+
params := PoetPowParams{
443+
Challenge: types.RandomBytes(10),
444+
Difficulty: 8,
445+
}
446+
exp := client.EXPECT().PowParams(gomock.Any()).Return(&params, nil)
447+
if tc.ttl == 0 {
448+
exp.Times(5)
449+
}
450+
for range 5 {
451+
got, err := poet.powParams(context.Background())
452+
require.NoError(t, err)
453+
require.Equal(t, params, *got)
454+
}
455+
})
456+
}
457+
}

config/mainnet.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ func MainnetConfig() Config {
163163
CycleGap: 12 * time.Hour,
164164
GracePeriod: 1 * time.Hour,
165165
PositioningATXSelectionTimeout: 50 * time.Minute,
166+
CertifierInfoCacheTTL: 5 * time.Minute,
167+
PowParamsCacheTTL: 5 * time.Minute,
166168
// RequestTimeout = RequestRetryDelay * 2 * MaxRequestRetries*(MaxRequestRetries+1)/2
167169
RequestTimeout: 1100 * time.Second,
168170
RequestRetryDelay: 10 * time.Second,

config/presets/fastnet.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,8 @@ func fastnet() config.Config {
9797
conf.POET.RequestTimeout = 12 * time.Second // RequestRetryDelay * 2 * MaxRequestRetries*(MaxRequestRetries+1)/2
9898
conf.POET.RequestRetryDelay = 1 * time.Second
9999
conf.POET.MaxRequestRetries = 3
100+
conf.POET.CertifierInfoCacheTTL = time.Minute
101+
conf.POET.PowParamsCacheTTL = 10 * time.Second
102+
100103
return conf
101104
}

config/presets/standalone.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ func standalone() config.Config {
8383
conf.POET.RequestTimeout = 12 * time.Second // RequestRetryDelay * 2 * MaxRequestRetries*(MaxRequestRetries+1)/2
8484
conf.POET.RequestRetryDelay = 1 * time.Second
8585
conf.POET.MaxRequestRetries = 3
86+
conf.POET.CertifierInfoCacheTTL = time.Minute
87+
conf.POET.PowParamsCacheTTL = 10 * time.Second
8688

8789
conf.P2P.DisableNatPort = true
8890

config/presets/testnet.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func testnet() config.Config {
118118
RequestTimeout: 550 * time.Second, // RequestRetryDelay * 2 * MaxRequestRetries*(MaxRequestRetries+1)/2
119119
RequestRetryDelay: 5 * time.Second,
120120
MaxRequestRetries: 10,
121+
122+
CertifierInfoCacheTTL: 5 * time.Minute,
123+
PowParamsCacheTTL: 5 * time.Minute,
121124
},
122125
POST: activation.PostConfig{
123126
MinNumUnits: 2,

0 commit comments

Comments
 (0)