Skip to content

Commit b3099f0

Browse files
Add load test
Signed-off-by: Xavier Geerinck <[email protected]>
1 parent ba2eee1 commit b3099f0

File tree

5 files changed

+51
-109
lines changed

5 files changed

+51
-109
lines changed

src/implementation/Client/HTTPClient/HTTPClient.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fetch from "node-fetch";
22
import { CommunicationProtocolEnum } from "../../..";
33
import IClient from "../../../interfaces/Client/IClient";
4+
import http from "node:http";
5+
import https from "node:https";
46

57
export default class HTTPClient implements IClient {
68
private readonly isInitialized: boolean;
@@ -9,6 +11,9 @@ export default class HTTPClient implements IClient {
911
private readonly clientPort: string;
1012
private readonly clientUrl: string;
1113

14+
private readonly httpAgent;
15+
private readonly httpsAgent;
16+
1217
constructor(host = "127.0.0.1", port = "50050") {
1318
this.isInitialized = true;
1419
this.clientHost = host;
@@ -21,6 +26,9 @@ export default class HTTPClient implements IClient {
2126
}
2227

2328
this.client = fetch;
29+
30+
this.httpAgent = new http.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000 });
31+
this.httpsAgent = new https.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000 });
2432
}
2533

2634
getClient(): typeof fetch {
@@ -64,7 +72,14 @@ export default class HTTPClient implements IClient {
6472
}
6573
}
6674

75+
6776
const urlFull = url.startsWith("http") ? url : `${this.clientUrl}${url}`;
77+
78+
// Decide which agent to use
79+
// we use an agent so we can reuse an open connection, limiting handshake requirements
80+
const agent = urlFull.startsWith("https") ? this.httpsAgent : this.httpAgent;
81+
params.agent = agent;
82+
6883
// console.log(`${params.method} - ${urlFull} (${params.body})`);
6984
const res = await fetch(urlFull, params);
7085

test/e2e/actors.http.test.ts

Lines changed: 19 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -38,86 +38,25 @@ describe('http/actors', () => {
3838
await server.startServer(); // Start the general server
3939
});
4040

41-
describe('activation/deactivation', () => {
42-
it('should correctly deactivate and activate an actor', async () => {
43-
// An actor is activated when we create the object and it has been added to the tracking table
44-
// for a good E2E test we thus check:
45-
// * has it been added to the tracking table?
46-
// -> Indirectly, we expect this by calling the Dapr client, which should be able to find the Actor in its tracking table
47-
// -> we thus use the client rather than the server methods
48-
// * has the method onActivate been called on the implemented actor?
49-
// -> create a specific actor implementation for this, since it's threaded and hard to mock else
50-
51-
// Reset state store
52-
// for this we activate and deactivate the actor first so we know it exists
53-
await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsActivated");
54-
await server.actor.deactivateActor(DemoActorActivateImpl.name, "my-actor-id");
55-
await client.actor.stateTransaction(DemoActorActivateImpl.name, "my-actor-id", [
56-
{
57-
operation: "upsert",
58-
request: {
59-
key: "is_activated",
60-
value: false
61-
}
62-
},
63-
{
64-
operation: "upsert",
65-
request: {
66-
key: "is_deactivated",
67-
value: false
68-
}
69-
},
70-
{
71-
operation: "upsert",
72-
request: {
73-
key: "call_count_activated",
74-
value: 0
75-
}
76-
},
77-
{
78-
operation: "upsert",
79-
request: {
80-
key: "call_count_deactivated",
81-
value: 0
82-
}
83-
},
84-
]);
85-
86-
// Activate Actor
87-
const res1 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsDeactivated");
88-
const res2 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsActivated");
89-
const res3 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getDeactivatedCallCount");
90-
const res4 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getActivatedCallCount");
91-
expect(res1).toEqual(false);
92-
expect(res2).toEqual(true);
93-
expect(res3).toEqual(0);
94-
expect(res4).toEqual(1);
95-
96-
// Deactivate Actor
97-
await server.actor.deactivateActor(DemoActorActivateImpl.name, "my-actor-id");
98-
99-
// Now call the getIsDeactivated and getDeactivatedCallCount again, which should change since it was deactivated
100-
// note: it will be reactivated again now since we call the invoke
101-
const res5 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsDeactivated");
102-
const res6 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsActivated");
103-
const res7 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getDeactivatedCallCount");
104-
const res8 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getActivatedCallCount");
105-
expect(res5).toEqual(true);
106-
expect(res6).toEqual(true);
107-
expect(res7).toEqual(1);
108-
expect(res8).toEqual(2);
109-
110-
// Deactivate Actor
111-
await server.actor.deactivateActor(DemoActorActivateImpl.name, "my-actor-id");
112-
113-
const res9 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsDeactivated");
114-
const res10 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getIsActivated");
115-
const res11 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getDeactivatedCallCount");
116-
const res12 = await client.actor.invoke("GET", DemoActorActivateImpl.name, "my-actor-id", "getActivatedCallCount");
117-
expect(res9).toEqual(true);
118-
expect(res10).toEqual(true);
119-
expect(res11).toEqual(2);
120-
expect(res12).toEqual(3);
41+
afterAll(async () => {
42+
await server.stopServer();
43+
});
44+
45+
describe('actorProxy', () => {
46+
it('should be able to create an actor object through the proxy', async () => {
47+
const builder = new ActorProxyBuilder<DemoActorCounterImpl>(DemoActorCounterImpl, client);
48+
const actor = builder.build(ActorId.createRandomId());
49+
50+
const c1 = await actor.getCounter();
51+
expect(c1).toEqual(0);
52+
53+
await actor.countBy(1);
54+
const c2 = await actor.getCounter();
55+
expect(c2).toEqual(1);
56+
57+
await actor.countBy(5);
58+
const c3 = await actor.getCounter();
59+
expect(c3).toEqual(6);
12160
});
12261
});
12362

test/e2e/main.grpc.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ describe('grpc/main', () => {
2828
await server.startServer();
2929
});
3030

31+
afterAll(async () => {
32+
await server.stopServer();
33+
});
34+
3135
describe('binding', () => {
3236
it('should be able to receive events', async () => {
3337
await client.binding.send('binding-mqtt', 'create', { hello: 'world' });

test/e2e/main.http.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ describe('http/main', () => {
2828
await server.startServer();
2929
});
3030

31+
afterAll(async () => {
32+
await server.stopServer();
33+
});
34+
3135
describe('binding', () => {
3236
it('should be able to receive events', async () => {
3337
await client.binding.send('binding-mqtt', 'create', { hello: 'world' });

test/load/pubsub.http.test.ts

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,20 @@
1-
import { CommunicationProtocolEnum, DaprClient, DaprServer, HttpMethod } from '../../src';
1+
import { CommunicationProtocolEnum, DaprClient } from '../../src';
22

3-
const serverHost = '127.0.0.1';
4-
const serverPort = '50001';
53
const daprHost = '127.0.0.1';
64
const daprPort = '50000'; // Dapr Sidecar Port of this Example Server
7-
const daprAppId = 'test-suite';
85

96
describe('load/http', () => {
10-
let server: DaprServer;
117
let client: DaprClient;
12-
// const mockBindingReceive = jest.fn(async (data: object) => console.log('mockBindingReceive'));
13-
// const mockPubSubSubscribe = jest.fn(async (data: object) => console.log('mockPubSubSubscribe'));
148

159
// We need to start listening on some endpoints already
1610
// this because Dapr is not dynamic and registers endpoints on boot
1711
beforeAll(async () => {
18-
server = new DaprServer(serverHost, serverPort, daprHost, daprPort, CommunicationProtocolEnum.HTTP);
1912
client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.HTTP);
20-
21-
// await server.binding.receive('binding-mqtt', mockBindingReceive);
22-
23-
// Start server
24-
await server.startServer();
2513
});
2614

27-
afterAll(async () => {
28-
await server.stopServer();
29-
})
30-
3115
describe('pubsub', () => {
32-
it('should be able to send 500 events as quickly as possible without errors', async () => {
33-
const amountOfCalls = 1;
16+
it('should be able to send 2500 events as quickly as possible without errors', async () => {
17+
const amountOfCalls = 2500;
3418

3519
// Create the promises
3620
let promises = [];
@@ -40,17 +24,13 @@ describe('load/http', () => {
4024
}
4125

4226
// Await the promises
27+
const tStart = Date.now();
4328
const res = await Promise.all(promises);
44-
console.log(res);
45-
46-
// Delay a bit for event to arrive
47-
// await new Promise((resolve, reject) => setTimeout(resolve, 250));
48-
49-
// expect(mockPubSubSubscribe.mock.calls.length).toBe(1);
29+
const tEnd = Date.now();
5030

51-
// Also test for receiving data
52-
// @ts-ignore
53-
// expect(mockPubSubSubscribe.mock.calls[0][0]['hello']).toEqual('world');
54-
});
31+
expect(res.filter(i => i === true).length).toEqual(amountOfCalls);
32+
console.log(`Execution time: ${tEnd - tStart}ms`);
33+
// @todo: do we add an execution time test?
34+
}, 30 * 1000);
5535
});
5636
});

0 commit comments

Comments
 (0)