mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 08:09:22 +00:00
### **PR Type** Bug fix, Enhancement ___ ### **Description** Fix HTTP/HTTP2 streaming in Safari Improve partition-based pagination logic Add region and cluster query parameters Refine aggregation and streaming pagination flows ___ ### **Changes walkthrough** 📝 <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Bug fix</strong></td><td><table> <tr> <td> <details> <summary><strong>ShortUrl.vue</strong><dd><code>Fix original URL redirection for deeper paths</code> </dd></summary> <hr> web/src/views/ShortUrl.vue - Updated handleOriginalUrl to join URL segments dynamically </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/7241/files#diff-b7ffcc5de24f9e1db5a3a8e78a686727360f9061ca6f9f8ae60456c3d603f9b7">+3/-2</a> </td> </tr> <tr> <td> <details> <summary><strong>useStreamingSearch.ts</strong><dd><code>Add Safari-compatible HTTP2 streaming</code> </dd></summary> <hr> web/src/composables/useStreamingSearch.ts <li>Manually read streams with TextDecoder for Safari<br> <li> Post decoded chunks to worker for processing<br> <li> Retain start and end stream control messages </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/7241/files#diff-8fce3e25a161cd458e734bd9b1f98aadefe3482da7b6529a5b1f4fa20ec115e2">+37/-4</a> </td> </tr> </table></td></tr><tr><td><strong>Enhancement</strong></td><td><table> <tr> <td> <details> <summary><strong>useLogs.ts</strong><dd><code>Enhance log pagination and cluster support</code> </dd></summary> <hr> web/src/composables/useLogs.ts <li>Inject regions and clusters into query and URL<br> <li> Fix partition pagination for zero or multiple partitions<br> <li> Add fetchAllParitions for complete partition fetch<br> <li> Apply dynamic histogram_interval to SQL queries </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/7241/files#diff-1040f61354f92711e0e77ed4e9bfa15586d45a94c7e2ab06cc37372068030e7b">+116/-23</a></td> </tr> <tr> <td> <details> <summary><strong>streamWorker.js</strong><dd><code>Refactor worker for chunk-based streaming</code> </dd></summary> <hr> web/src/workers/streamWorker.js <li>Remove transferable ReadableStream handling<br> <li> Introduce processChunk to buffer and parse SSE<br> <li> Consolidate message parsing and error handling </details> </td> <td><a href="https://github.com/openobserve/openobserve/pull/7241/files#diff-8534facfcca68c16701832f43a501abcdb92847115c46fced5d69792e5594dd9">+75/-94</a> </td> </tr> </table></td></tr></tr></tbody></table> ___ > <details> <summary> Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about PR-Agent usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>
133 lines
3.7 KiB
JavaScript
133 lines
3.7 KiB
JavaScript
// Worker to handle stream processing
|
|
// This offloads decoding and parsing from the main thread
|
|
let activeBuffers = {};
|
|
|
|
// Helper function to extract data from message line
|
|
function extractData(line) {
|
|
return line.startsWith('data:') ? line.slice(6) : line.slice(5);
|
|
}
|
|
|
|
// Handle messages from main thread
|
|
self.onmessage = async (event) => {
|
|
const { action, traceId, chunk } = event.data;
|
|
|
|
switch (action) {
|
|
case 'startStream':
|
|
// For Safari compatibility, we receive chunks instead of streams
|
|
activeBuffers[traceId] = '';
|
|
break;
|
|
|
|
case 'processChunk':
|
|
// Process individual chunk (Safari compatibility)
|
|
if (activeBuffers[traceId] !== undefined) {
|
|
processChunk(traceId, chunk);
|
|
}
|
|
break;
|
|
|
|
case 'endStream':
|
|
// Stream ended, send end notification
|
|
self.postMessage({
|
|
type: 'end',
|
|
traceId,
|
|
});
|
|
|
|
delete activeBuffers[traceId];
|
|
break;
|
|
|
|
case 'cancelStream':
|
|
delete activeBuffers[traceId];
|
|
break;
|
|
|
|
case 'closeAll':
|
|
activeBuffers = {};
|
|
break;
|
|
}
|
|
};
|
|
|
|
// Process a chunk for a given traceId
|
|
function processChunk(traceId, chunk) {
|
|
let buffer = activeBuffers[traceId] || '';
|
|
|
|
try {
|
|
// Add chunk to buffer
|
|
buffer += chunk;
|
|
|
|
// Process complete messages
|
|
const messages = buffer.split('\n\n');
|
|
// Keep the last potentially incomplete message in buffer
|
|
buffer = messages.pop() || '';
|
|
activeBuffers[traceId] = buffer;
|
|
|
|
const lines = messages.filter(line => line.trim());
|
|
|
|
// Process each complete line
|
|
for (let i = 0; i < lines.length; i++) {
|
|
try {
|
|
const msgLines = lines[i].split('\n');
|
|
// Check if this is an event line
|
|
|
|
if(msgLines.length > 1){
|
|
const eventType = msgLines[0].startsWith('event:') ? msgLines[0].slice(7).trim() : msgLines[0].slice(6).trim();
|
|
|
|
//TODO: Logic is duplicated for event:search_response and event:search_response_hits
|
|
// Create method to handle this
|
|
if (msgLines[1]?.startsWith('data:') || msgLines[1]?.startsWith('data: ')) {
|
|
const data = extractData(msgLines[1]);
|
|
|
|
try {
|
|
// Try to parse as JSON
|
|
const json = JSON.parse(data);
|
|
// Send message based on event type
|
|
self.postMessage({
|
|
type: eventType,
|
|
traceId,
|
|
data: json,
|
|
});
|
|
} catch (parseErr) {
|
|
// If JSON parsing fails, send raw data
|
|
self.postMessage({
|
|
type: 'error',
|
|
traceId,
|
|
data: { message: 'Error parsing data', error: parseErr.toString() },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (msgLines[0]?.startsWith('data:') || msgLines[0]?.startsWith('data: ')) {
|
|
const data = extractData(msgLines[0]);
|
|
try {
|
|
// Try to parse as JSON
|
|
const json = JSON.parse(data);
|
|
// Send message based on event type
|
|
self.postMessage({
|
|
type: 'data',
|
|
traceId,
|
|
data: json,
|
|
});
|
|
} catch (parseErr) {
|
|
// If JSON parsing fails, send raw data
|
|
self.postMessage({
|
|
type: 'data',
|
|
traceId,
|
|
data: data,
|
|
});
|
|
}
|
|
}
|
|
} catch (e) {
|
|
self.postMessage({
|
|
type: 'error',
|
|
traceId,
|
|
data: { message: 'Error processing message', error: e.toString() },
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
// Send error to main thread
|
|
self.postMessage({
|
|
type: 'error',
|
|
traceId,
|
|
data: { message: 'Stream processing error', error: error.toString() },
|
|
});
|
|
}
|
|
}
|