Skip to content

Commit 9634137

Browse files
authored
feat: Use txService and parallel requests (#220)
1 parent 0f83f43 commit 9634137

File tree

8 files changed

+29
-74
lines changed

8 files changed

+29
-74
lines changed

.github/workflows/node.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,11 @@ jobs:
5353
run: yarn build
5454
env:
5555
VITE_CONFIG_SERVICE_URL: "https://safe-config.staging.5afe.dev"
56-
VITE_CLIENT_GATEWAY_URL: "https://safe-client.staging.5afe.dev"
5756
- name: Build Production React App
5857
if: (github.event_name == 'release' && github.event.action == 'released')
5958
run: yarn build
6059
env:
6160
VITE_CONFIG_SERVICE_URL: "https://safe-config.safe.global"
62-
VITE_CLIENT_GATEWAY_URL: "https://safe-client.safe.global"
6361
- name: Deploy to Staging S3 bucket
6462
if: github.ref == 'refs/heads/main'
6563
run: aws s3 sync ./build/ "s3://$BUCKET_NAME/current" --delete

example.env

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11

22
# Config service url
3-
VITE_CONFIG_SERVICE_URL=https://safe-config.safe.global
4-
5-
# Client Gateway url
6-
VITE_CLIENT_GATEWAY_URL=https://safe-client.safe.global
3+
VITE_CONFIG_SERVICE_URL=https://safe-config.safe.global

src/App.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import Header from "src/components/header/Header";
77
import ChainStatusTable from "src/components/chain-status-table/ChainStatusTable";
88

99
const VITE_CONFIG_SERVICE_URL = import.meta.env.VITE_CONFIG_SERVICE_URL;
10-
const VITE_CLIENT_GATEWAY_URL = import.meta.env.VITE_CLIENT_GATEWAY_URL;
1110

1211
function App() {
1312
const { theme, switchThemeMode, isDarkTheme } = useTheme();
@@ -25,10 +24,7 @@ function App() {
2524
sx={{ marginBottom: "36px", marginTop: "42px" }}
2625
>
2726
{/* Chain status table */}
28-
<ChainStatusTable
29-
configServiceUrl={VITE_CONFIG_SERVICE_URL}
30-
clientGatewayUrl={VITE_CLIENT_GATEWAY_URL}
31-
/>
27+
<ChainStatusTable configServiceUrl={VITE_CONFIG_SERVICE_URL} />
3228
</Container>
3329
</ThemeProvider>
3430
);

src/api/getChainStatus.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import chainStatus from "src/models/chainStatus";
44
import { API_TIMEOUT } from "src/config/api";
55

66
async function getChainStatus(
7-
clientGatewayUrl: string,
87
chain: chain,
98
options?: RawAxiosRequestConfig,
109
): Promise<chainStatus> {
11-
const endpoint = `${clientGatewayUrl}/v1/chains/${chain.chainId}/about/indexing`;
10+
const endpoint = `${chain.transactionService}/api/v1/about/indexing`;
1211

1312
const { data } = await axios.get(endpoint, {
1413
...options,

src/components/chain-status-table/ChainStatusRow.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,15 @@ import SyncedLabel from "src/components/synced-label/SyncedLabel";
1414
import memoizedGetBlock from "src/utils/memoizedGetBlock";
1515
import { CHAIN_STATUS_POLLING_INTERVAL } from "src/config/api";
1616

17-
function ChainStatusRow({
18-
chain,
19-
clientGatewayUrl,
20-
}: {
21-
chain: chain;
22-
clientGatewayUrl: string;
23-
}) {
17+
function ChainStatusRow({ chain }: { chain: chain }) {
2418
// endpoint to the chain status from the transaction service
2519
const fetchChainStatus = useCallback(
2620
async (signal: AbortSignal) => {
27-
return getChainStatus(clientGatewayUrl, chain, {
21+
return getChainStatus(chain, {
2822
signal,
2923
});
3024
},
31-
[clientGatewayUrl, chain],
25+
[chain],
3226
);
3327

3428
// fetch chain status with a polling

src/components/chain-status-table/ChainStatusTable.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ import ChainStatusRow from "src/components/chain-status-table/ChainStatusRow";
1818

1919
type StatusTableProps = {
2020
configServiceUrl: string;
21-
clientGatewayUrl: string;
2221
};
2322

24-
function ChainStatusTable({
25-
configServiceUrl,
26-
clientGatewayUrl,
27-
}: StatusTableProps) {
23+
function ChainStatusTable({ configServiceUrl }: StatusTableProps) {
2824
// endpoint to fetch all chains from the config service
2925
const fetchChains = useCallback(
3026
(signal: AbortSignal) => {
@@ -80,11 +76,7 @@ function ChainStatusTable({
8076
<TableBody>
8177
{!isLoading &&
8278
chains?.map((chain) => (
83-
<ChainStatusRow
84-
key={chain.chainId}
85-
chain={chain}
86-
clientGatewayUrl={clientGatewayUrl}
87-
/>
79+
<ChainStatusRow key={chain.chainId} chain={chain} />
8880
))}
8981
</TableBody>
9082
</Table>

src/hooks/useApi.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ type useApiHookReturnValue<T> = {
88
data?: T;
99
};
1010

11-
const pollingManager = new PollingManager();
12-
1311
function useApi<T>(
1412
apiCall: apiCallParam<T>,
1513
pollingTime?: number,
@@ -37,7 +35,7 @@ function useApi<T>(
3735

3836
if (pollingTime) {
3937
// Use polling manager for rate-limited polling
40-
pollingManager.addTask(
38+
PollingManager.addTask(
4139
taskId,
4240
stableApiCall,
4341
onSuccess,
@@ -58,7 +56,7 @@ function useApi<T>(
5856

5957
return () => {
6058
if (taskIdRef.current) {
61-
pollingManager.removeTask(taskIdRef.current);
59+
PollingManager.removeTask(taskIdRef.current);
6260
}
6361
};
6462
}, [stableApiCall, pollingTime]);

src/utils/pollingManager.ts

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { MAX_REQUESTS_PER_SECOND } from "src/config/api";
2-
31
type PollingTask<T = unknown> = {
42
id: string;
53
apiCall: (signal: AbortSignal) => Promise<T>;
@@ -14,9 +12,6 @@ class PollingManager {
1412
private tasks: Map<string, PollingTask<unknown>> = new Map();
1513
private isRunning = false;
1614
private isPaused = false;
17-
private maxRequestsPerSecond = MAX_REQUESTS_PER_SECOND;
18-
private minInterval = 1000 / this.maxRequestsPerSecond;
19-
private lastRequestTime = 0;
2015
private visibilityChangeListener: (() => void) | null = null;
2116

2217
constructor() {
@@ -57,59 +52,45 @@ class PollingManager {
5752
}
5853
}
5954

60-
private async executeNextTask(): Promise<void> {
55+
private async executeDueTasks(): Promise<void> {
6156
if (this.isPaused) {
6257
return;
6358
}
6459

6560
const now = Date.now();
6661

67-
// Find the next task that should be executed
68-
let nextTask: PollingTask<unknown> | null = null;
69-
let earliestDue = Infinity;
62+
// Find all tasks that are due for execution
63+
const dueTasks: PollingTask<unknown>[] = [];
7064

7165
for (const task of this.tasks.values()) {
7266
const timeSinceLastExecution = now - task.lastExecuted;
7367
if (timeSinceLastExecution >= task.pollingInterval) {
74-
const dueTime = task.lastExecuted + task.pollingInterval;
75-
if (dueTime < earliestDue) {
76-
earliestDue = dueTime;
77-
nextTask = task;
78-
}
68+
dueTasks.push(task);
7969
}
8070
}
8171

82-
if (!nextTask) return;
83-
84-
// Respect rate limiting
85-
const timeSinceLastRequest = now - this.lastRequestTime;
86-
if (timeSinceLastRequest < this.minInterval) {
87-
const delay = this.minInterval - timeSinceLastRequest;
88-
await new Promise((resolve) => setTimeout(resolve, delay));
89-
}
90-
91-
if (this.isPaused) {
92-
return;
93-
}
72+
if (dueTasks.length === 0) return;
9473

95-
// Execute the task
96-
try {
97-
this.lastRequestTime = Date.now();
98-
nextTask.lastExecuted = this.lastRequestTime;
74+
// Execute all due tasks in parallel
75+
const promises = dueTasks.map(async (task) => {
76+
try {
77+
task.lastExecuted = Date.now();
78+
const result = await task.apiCall(task.abortController.signal);
79+
task.onSuccess(result);
80+
} catch (error) {
81+
task.onError(error);
82+
}
83+
});
9984

100-
const result = await nextTask.apiCall(nextTask.abortController.signal);
101-
nextTask.onSuccess(result);
102-
} catch (error) {
103-
nextTask.onError(error);
104-
}
85+
await Promise.all(promises);
10586
}
10687

10788
private async runPollingLoop(): Promise<void> {
10889
while (this.isRunning && this.tasks.size > 0) {
109-
await this.executeNextTask();
90+
await this.executeDueTasks();
11091

11192
// Delay to prevent tight loop
112-
const delay = this.isPaused ? 1000 : 50;
93+
const delay = this.isPaused ? 1000 : 100;
11394
await new Promise((resolve) => setTimeout(resolve, delay));
11495
}
11596
this.isRunning = false;
@@ -183,4 +164,4 @@ class PollingManager {
183164
}
184165
}
185166

186-
export default PollingManager;
167+
export default new PollingManager();

0 commit comments

Comments
 (0)