jinaga-server 3.5.2 → 3.5.4

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.
Files changed (57) hide show
  1. package/dist/authorization/authorization-keystore.js +2 -2
  2. package/dist/authorization/authorization-keystore.js.map +1 -1
  3. package/dist/http/csv-metadata.d.ts +24 -0
  4. package/dist/http/csv-metadata.js +3 -0
  5. package/dist/http/csv-metadata.js.map +1 -0
  6. package/dist/http/csv-validator.d.ts +23 -0
  7. package/dist/http/csv-validator.js +83 -0
  8. package/dist/http/csv-validator.js.map +1 -0
  9. package/dist/http/output-formatters.d.ts +28 -0
  10. package/dist/http/output-formatters.js +197 -0
  11. package/dist/http/output-formatters.js.map +1 -0
  12. package/dist/http/result-stream.d.ts +35 -0
  13. package/dist/http/result-stream.js +78 -0
  14. package/dist/http/result-stream.js.map +1 -0
  15. package/dist/http/router.d.ts +7 -1
  16. package/dist/http/router.js +98 -47
  17. package/dist/http/router.js.map +1 -1
  18. package/dist/postgres/postgres-store.js +7 -5
  19. package/dist/postgres/postgres-store.js.map +1 -1
  20. package/dist/postgres/purge-sql.js +15 -2
  21. package/dist/postgres/purge-sql.js.map +1 -1
  22. package/dist/postgres/specification-result-sql.d.ts +2 -0
  23. package/dist/postgres/specification-result-sql.js +49 -5
  24. package/dist/postgres/specification-result-sql.js.map +1 -1
  25. package/dist/postgres/specification-sql.js +28 -6
  26. package/dist/postgres/specification-sql.js.map +1 -1
  27. package/docs/design/feeds-persistent-proxy-plan.md +294 -0
  28. package/integration-test/integration-test/jinaga-test/package-lock.json +6 -0
  29. package/integration-test/jinaga-test/Dockerfile +1 -6
  30. package/integration-test/jinaga-test/http-test-helpers.js +77 -0
  31. package/integration-test/jinaga-test/jinaga.test.js +9 -9
  32. package/integration-test/jinaga-test/package-lock.json +329 -32
  33. package/integration-test/jinaga-test/package.json +5 -1
  34. package/integration-test/jinaga-test/read-csv.test.js +350 -0
  35. package/integration-test/jinaga-test/read-endpoint.test.js +442 -0
  36. package/integration-test/jinaga-test/read-performance.test.js +221 -0
  37. package/integration-test/jinaga-test/read-streaming.test.js +199 -0
  38. package/integration-test/jinaga-test/specification-conditions.test.js +147 -0
  39. package/integration-test/jinaga-test/test-helpers.js +12 -0
  40. package/integration-test/jinaga-test/time-projection.test.js +235 -0
  41. package/package.json +3 -2
  42. package/src/authorization/authorization-keystore.ts +4 -4
  43. package/src/http/csv-metadata.ts +34 -0
  44. package/src/http/csv-validator.ts +90 -0
  45. package/src/http/output-formatters.ts +196 -0
  46. package/src/http/result-stream.ts +69 -0
  47. package/src/http/router.ts +117 -34
  48. package/src/postgres/postgres-store.ts +13 -6
  49. package/src/postgres/purge-sql.ts +18 -2
  50. package/src/postgres/specification-result-sql.ts +70 -8
  51. package/src/postgres/specification-sql.ts +36 -8
  52. package/test/http/csv-validatorSpec.ts +205 -0
  53. package/test/http/result-streamSpec.ts +151 -0
  54. package/test/postgres/postgresReadSpec.ts +192 -14
  55. package/test/postgres/postgresSpecificationSpec.ts +3 -3
  56. package/test/postgres/specificationConditionsSpec.ts +268 -0
  57. package/documentation/streaming-feeds-full-results-solution.md +0 -230
@@ -1,230 +0,0 @@
1
- # Solution: Full Results Delivery for Streamed Feeds Endpoint
2
-
3
- ## Problem Analysis
4
-
5
- The current streaming feeds endpoint has a critical limitation: it only delivers the first 100 results from the initial query and then switches to push-only mode. This means clients never receive complete historical data for feeds with more than 100 matching results.
6
-
7
- ## Proposed Solution: Continuous Initial Query with Pagination
8
-
9
- ### Core Design Principle
10
-
11
- Modify the streaming implementation to **continue fetching and delivering all historical results** before transitioning to real-time push mode, while maintaining the streaming connection throughout the process.
12
-
13
- ### Implementation Strategy
14
-
15
- #### 1. Enhanced `streamFeed` Method
16
-
17
- ```typescript
18
- private async streamFeed(user: RequestUser | null, params: { [key: string]: string }, query: qs.ParsedQs): Promise<Stream<FeedResponse> | null> {
19
- const feedHash = params["hash"];
20
- if (!feedHash) {
21
- return null;
22
- }
23
-
24
- const feedDefinition = await this.feedCache.getFeed(feedHash);
25
- if (!feedDefinition) {
26
- return null;
27
- }
28
-
29
- let bookmark = query["b"] as string ?? "";
30
- const userIdentity = serializeUserIdentity(user);
31
- const start = feedDefinition.feed.given.map(label => feedDefinition.namedStart[label.name]);
32
- const givenHash = computeObjectHash(feedDefinition.namedStart);
33
-
34
- const stream = new Stream<FeedResponse>();
35
-
36
- // NEW: Continuous initial query until exhausted
37
- bookmark = await this.streamAllInitialResults(userIdentity, feedDefinition, start, bookmark, stream);
38
-
39
- // Set up real-time listeners after initial data is complete
40
- const inverses = invertSpecification(feedDefinition.feed);
41
- const listeners = inverses.map(inverse => this.factManager.addSpecificationListener(
42
- inverse.inverseSpecification,
43
- async (results) => {
44
- const matchingResults = results.filter(pr =>
45
- givenHash === computeTupleSubsetHash(pr.tuple, inverse.givenSubset));
46
- if (matchingResults.length != 0) {
47
- bookmark = await this.streamFeedResponse(userIdentity, feedDefinition, start, bookmark, stream, true);
48
- }
49
- }
50
- ));
51
-
52
- stream.done(() => {
53
- for (const listener of listeners) {
54
- this.factManager.removeSpecificationListener(listener);
55
- }
56
- });
57
-
58
- return stream;
59
- }
60
- ```
61
-
62
- #### 2. New `streamAllInitialResults` Method
63
-
64
- ```typescript
65
- private async streamAllInitialResults(
66
- userIdentity: UserIdentity | null,
67
- feedDefinition: FeedObject,
68
- start: FactReference[],
69
- initialBookmark: string,
70
- stream: Stream<FeedResponse>
71
- ): Promise<string> {
72
- let bookmark = initialBookmark;
73
- let hasMoreResults = true;
74
- let pageCount = 0;
75
- const maxPages = 1000; // Safety limit to prevent infinite loops
76
-
77
- while (hasMoreResults && pageCount < maxPages) {
78
- const results = await this.authorization.feed(userIdentity, feedDefinition.feed, start, bookmark);
79
-
80
- // Check if we got results
81
- if (results.tuples.length === 0) {
82
- hasMoreResults = false;
83
- break;
84
- }
85
-
86
- // Process and send results
87
- const references = results.tuples.flatMap(t => t.facts).filter((value, index, self) =>
88
- self.findIndex(f => f.hash === value.hash && f.type === value.type) === index
89
- );
90
-
91
- if (references.length > 0) {
92
- const response: FeedResponse = {
93
- references,
94
- bookmark: results.bookmark
95
- };
96
- stream.feed(response);
97
- }
98
-
99
- // Update bookmark for next iteration
100
- const newBookmark = results.bookmark;
101
- if (newBookmark === bookmark) {
102
- // No progress made, avoid infinite loop
103
- hasMoreResults = false;
104
- } else {
105
- bookmark = newBookmark;
106
- }
107
-
108
- // Check if we got fewer results than the page size (indicates end)
109
- if (results.tuples.length < 100) {
110
- hasMoreResults = false;
111
- }
112
-
113
- pageCount++;
114
-
115
- // Add small delay to prevent overwhelming the database
116
- if (hasMoreResults && pageCount % 10 === 0) {
117
- await new Promise(resolve => setTimeout(resolve, 10));
118
- }
119
- }
120
-
121
- return bookmark;
122
- }
123
- ```
124
-
125
- ### Key Design Features
126
-
127
- #### 1. **Continuous Pagination**
128
- - Loops through all available pages until no more results
129
- - Uses bookmark progression to detect completion
130
- - Includes safety limits to prevent infinite loops
131
-
132
- #### 2. **Stream Integrity**
133
- - Maintains single persistent connection throughout
134
- - Delivers results incrementally as pages are fetched
135
- - No interruption to client connection
136
-
137
- #### 3. **Performance Safeguards**
138
- - Maximum page limit (1000 pages = 100,000 results max)
139
- - Periodic delays to prevent database overload
140
- - Early termination on duplicate bookmarks
141
-
142
- #### 4. **Backward Compatibility**
143
- - Preserves existing API contract
144
- - Maintains same response format
145
- - No changes required to client implementations
146
-
147
- ### Alternative Approaches Considered
148
-
149
- #### Option A: Async Background Fetching
150
- **Rejected**: Would complicate error handling and resource cleanup
151
-
152
- #### Option B: Configurable Page Limits
153
- **Rejected**: Adds complexity without solving the core issue
154
-
155
- #### Option C: Separate Historical vs Real-time Endpoints
156
- **Rejected**: Breaks the unified streaming model
157
-
158
- ### Implementation Considerations
159
-
160
- #### 1. **Database Impact**
161
- - Multiple sequential queries instead of single query
162
- - Potential increased load on connection pool
163
- - Mitigation: Rate limiting and connection reuse
164
-
165
- #### 2. **Memory Management**
166
- - Stream queue may grow during rapid pagination
167
- - Mitigation: Client processing should keep pace with delivery
168
- - Consider: Optional memory pressure monitoring
169
-
170
- #### 3. **Timeout Handling**
171
- - 5-minute stream timeout may be insufficient for large datasets
172
- - Consider: Dynamic timeout based on data volume
173
- - Alternative: Configurable timeout per feed
174
-
175
- #### 4. **Error Recovery**
176
- - Database errors during pagination should not break stream
177
- - Implement: Retry logic for individual page failures
178
- - Fallback: Continue from last successful bookmark
179
-
180
- ### Testing Strategy
181
-
182
- #### 1. **Unit Tests**
183
- - Test pagination logic with various result set sizes
184
- - Verify bookmark progression and termination conditions
185
- - Test error scenarios and recovery
186
-
187
- #### 2. **Integration Tests**
188
- - Large dataset streaming (1000+ results)
189
- - Concurrent stream handling
190
- - Database connection pool behavior under load
191
-
192
- #### 3. **Performance Tests**
193
- - Memory usage during large result streaming
194
- - Database query performance impact
195
- - Client processing capability validation
196
-
197
- ### Deployment Considerations
198
-
199
- #### 1. **Gradual Rollout**
200
- - Feature flag to enable/disable full pagination
201
- - Monitor database performance impact
202
- - Rollback capability if issues arise
203
-
204
- #### 2. **Monitoring**
205
- - Track average pages per stream
206
- - Monitor database connection pool utilization
207
- - Alert on streams exceeding safety limits
208
-
209
- #### 3. **Configuration**
210
- - Make page limit configurable
211
- - Allow timeout adjustment per deployment
212
- - Enable/disable rate limiting
213
-
214
- ## Expected Outcomes
215
-
216
- ### Benefits
217
- 1. **Complete Data Delivery**: Clients receive all historical results
218
- 2. **Unified Experience**: Single endpoint for both historical and real-time data
219
- 3. **Streaming Efficiency**: Maintains connection throughout data delivery
220
- 4. **Backward Compatibility**: No breaking changes to existing clients
221
-
222
- ### Trade-offs
223
- 1. **Increased Database Load**: More queries per stream initialization
224
- 2. **Longer Initial Response Time**: Complete data delivery takes longer
225
- 3. **Memory Usage**: Larger stream queues during pagination
226
- 4. **Complexity**: More sophisticated error handling required
227
-
228
- ## Conclusion
229
-
230
- This solution addresses the core limitation while maintaining the streaming architecture's benefits. The continuous pagination approach ensures complete data delivery without breaking the real-time streaming model, providing a robust foundation for applications requiring both historical completeness and live updates.