jinaga-server 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test script to simulate multiple concurrent clients connecting to streaming feeds
5
+ * This will help validate the diagnostic logging and reproduce the push notification issues
6
+ */
7
+
8
+ const http = require('http');
9
+ const { performance } = require('perf_hooks');
10
+
11
+ // Configuration
12
+ const SERVER_HOST = 'localhost';
13
+ const SERVER_PORT = 8080;
14
+ const FEED_HASH = 'your-feed-hash-here'; // Replace with actual feed hash
15
+ const NUM_CLIENTS = 5;
16
+ const CONNECTION_DURATION = 30000; // 30 seconds
17
+ const STAGGER_DELAY = 1000; // 1 second between client connections
18
+
19
+ class StreamingClient {
20
+ constructor(clientId, feedHash) {
21
+ this.clientId = clientId;
22
+ this.feedHash = feedHash;
23
+ this.messageCount = 0;
24
+ this.connected = false;
25
+ this.startTime = null;
26
+ this.lastMessageTime = null;
27
+ }
28
+
29
+ connect() {
30
+ return new Promise((resolve, reject) => {
31
+ console.log(`[Client-${this.clientId}] Connecting to feed: ${this.feedHash}`);
32
+ this.startTime = performance.now();
33
+
34
+ const options = {
35
+ hostname: SERVER_HOST,
36
+ port: SERVER_PORT,
37
+ path: `/feeds/${this.feedHash}`,
38
+ method: 'GET',
39
+ headers: {
40
+ 'Accept': 'application/x-jinaga-feed-stream',
41
+ 'Connection': 'keep-alive',
42
+ 'Cache-Control': 'no-cache'
43
+ }
44
+ };
45
+
46
+ const req = http.request(options, (res) => {
47
+ console.log(`[Client-${this.clientId}] Connected - Status: ${res.statusCode}`);
48
+
49
+ if (res.statusCode !== 200) {
50
+ console.error(`[Client-${this.clientId}] Connection failed with status: ${res.statusCode}`);
51
+ reject(new Error(`HTTP ${res.statusCode}`));
52
+ return;
53
+ }
54
+
55
+ this.connected = true;
56
+ resolve();
57
+
58
+ res.on('data', (chunk) => {
59
+ const now = performance.now();
60
+ this.messageCount++;
61
+ this.lastMessageTime = now;
62
+
63
+ const timeSinceStart = Math.round(now - this.startTime);
64
+ const timeSinceLastMessage = this.lastMessageTime ? Math.round(now - this.lastMessageTime) : 0;
65
+
66
+ console.log(`[Client-${this.clientId}] Message #${this.messageCount} received - Time: ${timeSinceStart}ms, Gap: ${timeSinceLastMessage}ms, Size: ${chunk.length} bytes`);
67
+
68
+ try {
69
+ const data = chunk.toString().trim();
70
+ if (data) {
71
+ const parsed = JSON.parse(data);
72
+ console.log(`[Client-${this.clientId}] Parsed message - References: ${parsed.references?.length || 0}, Bookmark: ${parsed.bookmark?.substring(0, 8) || 'none'}...`);
73
+ }
74
+ } catch (error) {
75
+ console.warn(`[Client-${this.clientId}] Failed to parse message: ${error.message}`);
76
+ }
77
+ });
78
+
79
+ res.on('end', () => {
80
+ const duration = Math.round(performance.now() - this.startTime);
81
+ console.log(`[Client-${this.clientId}] Stream ended - Duration: ${duration}ms, Messages: ${this.messageCount}`);
82
+ this.connected = false;
83
+ });
84
+
85
+ res.on('error', (error) => {
86
+ console.error(`[Client-${this.clientId}] Stream error: ${error.message}`);
87
+ this.connected = false;
88
+ });
89
+ });
90
+
91
+ req.on('error', (error) => {
92
+ console.error(`[Client-${this.clientId}] Request error: ${error.message}`);
93
+ reject(error);
94
+ });
95
+
96
+ req.setTimeout(CONNECTION_DURATION + 5000, () => {
97
+ console.log(`[Client-${this.clientId}] Request timeout`);
98
+ req.destroy();
99
+ });
100
+
101
+ req.end();
102
+
103
+ // Auto-disconnect after specified duration
104
+ setTimeout(() => {
105
+ if (this.connected) {
106
+ console.log(`[Client-${this.clientId}] Auto-disconnecting after ${CONNECTION_DURATION}ms`);
107
+ req.destroy();
108
+ }
109
+ }, CONNECTION_DURATION);
110
+ });
111
+ }
112
+
113
+ getStats() {
114
+ const duration = this.startTime ? Math.round(performance.now() - this.startTime) : 0;
115
+ return {
116
+ clientId: this.clientId,
117
+ messageCount: this.messageCount,
118
+ connected: this.connected,
119
+ duration: duration
120
+ };
121
+ }
122
+ }
123
+
124
+ async function runConcurrentTest() {
125
+ console.log(`Starting concurrent streaming test with ${NUM_CLIENTS} clients`);
126
+ console.log(`Server: ${SERVER_HOST}:${SERVER_PORT}`);
127
+ console.log(`Feed Hash: ${FEED_HASH}`);
128
+ console.log(`Connection Duration: ${CONNECTION_DURATION}ms`);
129
+ console.log(`Stagger Delay: ${STAGGER_DELAY}ms`);
130
+ console.log('---');
131
+
132
+ const clients = [];
133
+ const connectionPromises = [];
134
+
135
+ // Create and connect clients with staggered timing
136
+ for (let i = 1; i <= NUM_CLIENTS; i++) {
137
+ const client = new StreamingClient(i, FEED_HASH);
138
+ clients.push(client);
139
+
140
+ // Stagger the connections
141
+ setTimeout(async () => {
142
+ try {
143
+ await client.connect();
144
+ } catch (error) {
145
+ console.error(`[Client-${i}] Connection failed: ${error.message}`);
146
+ }
147
+ }, (i - 1) * STAGGER_DELAY);
148
+ }
149
+
150
+ // Monitor client statistics
151
+ const statsInterval = setInterval(() => {
152
+ console.log('\n=== CLIENT STATISTICS ===');
153
+ clients.forEach(client => {
154
+ const stats = client.getStats();
155
+ console.log(`Client-${stats.clientId}: Messages=${stats.messageCount}, Connected=${stats.connected}, Duration=${stats.duration}ms`);
156
+ });
157
+ console.log('========================\n');
158
+ }, 10000); // Every 10 seconds
159
+
160
+ // Wait for test completion
161
+ setTimeout(() => {
162
+ clearInterval(statsInterval);
163
+
164
+ console.log('\n=== FINAL STATISTICS ===');
165
+ let totalMessages = 0;
166
+ let connectedClients = 0;
167
+
168
+ clients.forEach(client => {
169
+ const stats = client.getStats();
170
+ totalMessages += stats.messageCount;
171
+ if (stats.connected) connectedClients++;
172
+ console.log(`Client-${stats.clientId}: Messages=${stats.messageCount}, Connected=${stats.connected}, Duration=${stats.duration}ms`);
173
+ });
174
+
175
+ console.log(`\nSummary:`);
176
+ console.log(`- Total Messages Received: ${totalMessages}`);
177
+ console.log(`- Still Connected: ${connectedClients}/${NUM_CLIENTS}`);
178
+ console.log(`- Average Messages per Client: ${(totalMessages / NUM_CLIENTS).toFixed(1)}`);
179
+
180
+ // Check for potential issues
181
+ const messageCounts = clients.map(c => c.getStats().messageCount);
182
+ const minMessages = Math.min(...messageCounts);
183
+ const maxMessages = Math.max(...messageCounts);
184
+ const variance = maxMessages - minMessages;
185
+
186
+ if (variance > 5) {
187
+ console.log(`\n⚠️ POTENTIAL ISSUE DETECTED:`);
188
+ console.log(` Message count variance: ${variance} (min: ${minMessages}, max: ${maxMessages})`);
189
+ console.log(` This suggests some clients may not be receiving all notifications`);
190
+ } else {
191
+ console.log(`\n✅ Message distribution appears consistent`);
192
+ }
193
+
194
+ console.log('\nTest completed. Check server logs for detailed diagnostic information.');
195
+ process.exit(0);
196
+ }, CONNECTION_DURATION + (NUM_CLIENTS * STAGGER_DELAY) + 5000);
197
+ }
198
+
199
+ // Handle command line arguments
200
+ if (process.argv.length > 2) {
201
+ const feedHash = process.argv[2];
202
+ if (feedHash && feedHash !== 'your-feed-hash-here') {
203
+ FEED_HASH = feedHash;
204
+ console.log(`Using feed hash from command line: ${FEED_HASH}`);
205
+ }
206
+ }
207
+
208
+ if (FEED_HASH === 'your-feed-hash-here') {
209
+ console.error('Please provide a valid feed hash as a command line argument:');
210
+ console.error('node test-concurrent-streams.js <feed-hash>');
211
+ console.error('\nOr edit the FEED_HASH constant in this script.');
212
+ process.exit(1);
213
+ }
214
+
215
+ // Start the test
216
+ runConcurrentTest().catch(error => {
217
+ console.error('Test failed:', error);
218
+ process.exit(1);
219
+ });