architecture.md rev1

This commit is contained in:
2026-02-14 13:04:28 +07:00
parent d9fd7a61bb
commit f0df169689
3 changed files with 673 additions and 299 deletions

View File

@@ -4,6 +4,32 @@
This document describes the implementation of the high-performance, bi-directional data bridge between Julia and JavaScript services using NATS (Core & JetStream), implementing the Claim-Check pattern for large payloads.
### Multi-Payload Support
The implementation uses a **standardized list-of-tuples format** for all payload operations. **Even when sending a single payload, the user must wrap it in a list.**
**API Standard:**
```julia
# Input format for smartsend (always a list of tuples)
[(dataname1, data1), (dataname2, data2), ...]
# Output format for smartreceive (always returns a list of tuples)
[(dataname1, data1), (dataname2, data2), ...]
```
**Examples:**
```julia
# Single payload - still wrapped in a list
smartsend("/test", [(dataname1, data1)], ...)
# Multiple payloads in one message
smartsend("/test", [(dataname1, data1), (dataname2, data2)], ...)
# Receive always returns a list
payloads = smartreceive(msg, ...)
# payloads = [(dataname1, data1), (dataname2, data2), ...]
```
## Architecture
The implementation follows the Claim-Check pattern:
@@ -107,20 +133,66 @@ node test/scenario3_julia_to_julia.js
## Usage
### Scenario 0: Basic Multi-Payload Example
#### Julia (Sender)
```julia
using NATSBridge
# Send multiple payloads in one message
smartsend(
"/test",
[("dataname1", data1), ("dataname2", data2)],
nats_url="nats://localhost:4222",
fileserver_url="http://localhost:8080/upload",
metadata=Dict("custom_key" => "custom_value")
)
# Even single payload must be wrapped in a list
smartsend("/test", [("single_data", mydata)])
```
#### Julia (Receiver)
```julia
using NATSBridge
# Receive returns a list of payloads
payloads = smartreceive(msg, "http://localhost:8080/upload")
# payloads = [(dataname1, data1), (dataname2, data2), ...]
```
### Scenario 1: Command & Control (Small JSON)
#### JavaScript (Sender)
```javascript
const { SmartSend } = require('./js_bridge');
const config = {
step_size: 0.01,
iterations: 1000
};
// Single payload wrapped in a list
const config = [{
dataname: "config",
data: { step_size: 0.01, iterations: 1000 },
type: "json"
}];
await SmartSend("control", config, "json", {
correlationId: "unique-id"
});
// Multiple payloads
const configs = [
{
dataname: "config1",
data: { step_size: 0.01 },
type: "json"
},
{
dataname: "config2",
data: { iterations: 1000 },
type: "json"
}
];
await SmartSend("control", configs, "json");
```
#### Julia (Receiver)
@@ -157,8 +229,8 @@ df = DataFrame(
category = rand(["A", "B", "C"], 10_000_000)
)
# Send via SmartSend with type="table"
await SmartSend("analysis_results", df, "table");
# Send via SmartSend - wrapped in a list
await SmartSend("analysis_results", [("table_data", df)], "table");
```
#### JavaScript (Receiver)
@@ -177,8 +249,12 @@ const table = result.data;
```javascript
const { SmartSend } = require('./js_bridge');
// Capture binary chunk
const binaryData = await navigator.mediaDevices.getUserMedia({ binary: true });
// Binary data wrapped in a list
const binaryData = [{
dataname: "audio_chunk",
data: binaryBuffer,
type: "binary"
}];
await SmartSend("binary_input", binaryData, "binary", {
metadata: {
@@ -208,16 +284,13 @@ end
#### Julia (Producer)
```julia
using NATS
using NATSBridge
function publish_health_status(nats)
jetstream = JetStream(nats, "health_updates")
while true
status = Dict("cpu" => rand(), "memory" => rand())
publish(jetstream, "health", status)
sleep(5) # Every 5 seconds
end
function publish_health_status(nats_url)
# Send status wrapped in a list
status = Dict("cpu" => rand(), "memory" => rand())
smartsend("health", [("status", status)], "json", nats_url=nats_url)
sleep(5) # Every 5 seconds
end
```
@@ -238,7 +311,8 @@ const consumer = await js.pullSubscribe("health", {
// Process historical and real-time messages
for await (const msg of consumer) {
const result = await SmartReceive(msg);
// Process the data
// result.data contains the list of payloads
// result.envelope contains the message envelope
msg.ack();
}
```
@@ -257,16 +331,39 @@ for await (const msg of consumer) {
```json
{
"correlation_id": "uuid-v4-string",
"type": "json|table|binary",
"transport": "direct|link",
"payload": "base64-encoded-string", // Only if transport=direct
"url": "http://fileserver/path/to/data", // Only if transport=link
"correlationId": "uuid-v4-string",
"msgId": "uuid-v4-string",
"timestamp": "2024-01-15T10:30:00Z",
"sendTo": "topic/subject",
"msgPurpose": "ACK | NACK | updateStatus | shutdown | chat",
"senderName": "agent-wine-web-frontend",
"senderId": "uuid4",
"receiverName": "agent-backend",
"receiverId": "uuid4",
"replyTo": "topic",
"replyToMsgId": "uuid4",
"BrokerURL": "nats://localhost:4222",
"metadata": {
"content_type": "application/octet-stream",
"content_length": 123456,
"format": "arrow_ipc_stream"
}
"content_length": 123456
},
"payloads": [
{
"id": "uuid4",
"dataname": "login_image",
"type": "image",
"transport": "direct",
"encoding": "base64",
"size": 15433,
"data": "base64-encoded-string",
"metadata": {
"checksum": "sha256_hash"
}
}
]
}
```