1
+ import { unstable_cache } from "next/cache" ;
2
+ import { cache } from 'react' ;
1
3
import superjson from "superjson" ;
2
- import { z } from ' zod' ;
4
+ import { z } from " zod" ;
3
5
6
+ // Your imports
4
7
import { Cluster , clients , priceFeedsSchema } from "../services/pyth" ;
5
8
6
- export const getPublishersForFeed = async (
7
- cluster : Cluster ,
8
- ) => {
9
- "use cache" ;
10
- const start = performance . now ( ) ;
11
- const data = await clients [ cluster ] . getData ( ) ;
9
+ const getDataCached = cache ( async ( cluster : Cluster ) => {
10
+ return clients [ cluster ] . getData ( ) ;
11
+ } ) ;
12
+ const MAX_CACHE_SIZE_STRING = 2 * 1024 * 1024 ;
13
+
14
+ const getPublishersForFeed = unstable_cache ( async ( cluster : Cluster , chunk ?: number ) => {
15
+ const data = await getDataCached ( cluster ) ;
12
16
const result : Record < string , string [ ] > = { } ;
13
17
for ( const key of data . productPrice . keys ( ) ) {
14
18
const price = data . productPrice . get ( key ) ;
15
19
result [ key ] = price ?. priceComponents . map ( ( { publisher } ) => publisher . toBase58 ( ) ) ?? [ ] ;
16
20
}
17
- const end = performance . now ( ) ;
18
- // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
19
- console . log ( `getPublishersForFeed: ${ end - start } ms` ) ;
20
- return result ;
21
- } ;
21
+ const stringifiedResult = superjson . stringify ( result ) ;
22
22
23
- const getFeeds = async ( cluster : Cluster ) => {
24
- "use cache" ;
25
- const start = performance . now ( ) ;
26
- const data = await clients [ cluster ] . getData ( ) ;
27
-
28
- const result = superjson . stringify ( priceFeedsSchema . parse ( data . symbols . filter (
29
- ( symbol ) =>
30
- data . productFromSymbol . get ( symbol ) ?. display_symbol !== undefined ,
31
- ) . map ( ( symbol ) => ( {
32
- symbol,
33
- product : data . productFromSymbol . get ( symbol ) ,
34
- price : {
35
- ...data . productPrice . get ( symbol ) ,
36
- priceComponents : data . productPrice . get ( symbol ) ?. priceComponents . map ( ( { publisher } ) => ( {
37
- publisher : publisher . toBase58 ( ) ,
38
- } ) ) ?? [ ] ,
39
- } ,
40
- } ) ) ) )
41
- const end = performance . now ( ) ;
42
- // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
43
- console . log ( `getFeeds: ${ end - start } ms` ) ;
44
- return result ;
45
- }
23
+ const chunksNumber = Math . ceil ( stringifiedResult . length / MAX_CACHE_SIZE_STRING ) ;
24
+ const chunks = [ ] ;
25
+ for ( let i = 0 ; i < chunksNumber ; i ++ ) {
26
+ chunks . push ( stringifiedResult . slice ( i * MAX_CACHE_SIZE_STRING , ( i + 1 ) * MAX_CACHE_SIZE_STRING ) ) ;
27
+ }
28
+ return {
29
+ chunk : chunks [ chunk ?? 0 ] ,
30
+ chunksNumber,
31
+ } ;
32
+ } , [ ] , { revalidate : false } ) ;
46
33
47
- export const getFeedsForPublisherCached = async (
48
- cluster : Cluster ,
49
- publisher : string ,
50
- ) => {
51
- const start = performance . now ( ) ;
52
- const rawFeeds = await getFeeds ( cluster ) ;
53
- const feeds = superjson . parse < z . infer < typeof priceFeedsSchema > > ( rawFeeds ) ;
54
- const end = performance . now ( ) ;
55
- // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
56
- console . log ( `getFeedsForPublisherCached: ${ end - start } ms` ) ;
57
- return priceFeedsSchema . parse ( feeds . filter ( ( { price } ) =>
58
- price . priceComponents . some (
59
- ( component ) => component . publisher . toString ( ) === publisher ,
60
- ) ,
61
- ) ) ;
62
- } ;
34
+ const _getFeeds = unstable_cache ( async ( cluster : Cluster , chunk ?: number ) => {
35
+ // eslint-disable-next-line no-console
36
+ console . log ( 'getFeeds' , cluster , chunk ) ;
37
+ const data = await getDataCached ( cluster ) ;
38
+ const parsedData = priceFeedsSchema . parse (
39
+ data . symbols
40
+ . filter (
41
+ ( symbol ) =>
42
+ data . productFromSymbol . get ( symbol ) ?. display_symbol !== undefined
43
+ )
44
+ . map ( ( symbol ) => ( {
45
+ symbol,
46
+ product : data . productFromSymbol . get ( symbol ) ,
47
+ price : {
48
+ ...data . productPrice . get ( symbol ) ,
49
+ priceComponents :
50
+ data . productPrice
51
+ . get ( symbol )
52
+ ?. priceComponents . map ( ( { publisher } ) => ( {
53
+ publisher : publisher . toBase58 ( ) ,
54
+ } ) ) ?? [ ] ,
55
+ } ,
56
+ } ) )
57
+ )
58
+ const result = superjson . stringify (
59
+ parsedData
60
+ ) ;
61
+ const chunksNumber = Math . ceil ( result . length / MAX_CACHE_SIZE_STRING ) ;
62
+ const chunks = [ ] ;
63
+ for ( let i = 0 ; i < chunksNumber ; i ++ ) {
64
+ chunks . push ( result . slice ( i * MAX_CACHE_SIZE_STRING , ( i + 1 ) * MAX_CACHE_SIZE_STRING ) ) ;
65
+ }
66
+ return {
67
+ chunk : chunks [ chunk ?? 0 ] ,
68
+ chunksNumber,
69
+ } ;
70
+ } , [ ] , { revalidate : false } ) ;
63
71
64
72
export const getFeedsCached = async ( cluster : Cluster ) => {
65
- "use cache" ;
66
73
const start = performance . now ( ) ;
67
- const rawFeeds = await getFeeds ( cluster ) ;
74
+ const { chunk, chunksNumber } = await _getFeeds ( cluster ) ; // uses cache
75
+ const rawResults = await Promise . all ( Array . from ( { length : chunksNumber - 1 } , ( _ , i ) => _getFeeds ( cluster , i + 1 ) ) ) ;
76
+ const rawJson = [ chunk , ...rawResults . map ( ( { chunk } ) => chunk ) ] . join ( '' ) ;
77
+ const data = superjson . parse < z . infer < typeof priceFeedsSchema > > ( rawJson ) ;
68
78
const end = performance . now ( ) ;
69
79
// eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
70
80
console . log ( `getFeedsCached: ${ end - start } ms` ) ;
71
- return superjson . parse < z . infer < typeof priceFeedsSchema > > ( rawFeeds ) ;
81
+ return data ;
72
82
} ;
73
83
74
84
export const getPublishersForFeedCached = async ( cluster : Cluster , symbol : string ) => {
75
85
const start = performance . now ( ) ;
76
- const data = await getPublishersForFeed ( cluster ) ;
86
+ const { chunk, chunksNumber } = await getPublishersForFeed ( cluster ) ; // uses cache
87
+ const rawResults = await Promise . all ( Array . from ( { length : chunksNumber - 1 } , ( _ , i ) => getPublishersForFeed ( cluster , i + 1 ) ) ) ;
88
+ const rawJson = [ chunk , ...rawResults . map ( ( { chunk } ) => chunk ) ] . join ( '' ) ;
89
+ const data = superjson . parse < Record < string , string [ ] > > ( rawJson ) ;
77
90
const end = performance . now ( ) ;
78
91
// eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
79
92
console . log ( `getPublishersForFeedCached: ${ end - start } ms` ) ;
80
93
return data [ symbol ] ;
81
- } ;
94
+ } ;
95
+
96
+ export const getFeedsForPublisherCached = async (
97
+ cluster : Cluster ,
98
+ publisher : string
99
+ ) => {
100
+ const start = performance . now ( ) ;
101
+ const data = await getFeedsCached ( cluster ) ; // uses cache
102
+ const end = performance . now ( ) ;
103
+ // eslint-disable-next-line no-console, @typescript-eslint/restrict-template-expressions
104
+ console . log ( `getFeedsForPublisherCached: ${ end - start } ms.` ) ;
105
+ return priceFeedsSchema . parse (
106
+ data . filter ( ( { price } ) =>
107
+ price . priceComponents . some (
108
+ ( component ) => component . publisher . toString ( ) === publisher
109
+ )
110
+ )
111
+ ) ;
112
+ } ;
0 commit comments