meadow-integration 1.0.5 → 1.0.6
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.
- package/.dockerignore +11 -0
- package/Docker-Build.sh +2 -0
- package/Docker-Compose.sh +2 -0
- package/Docker-Push.sh +2 -0
- package/Docker-Tag.sh +2 -0
- package/Dockerfile +28 -0
- package/Dockerfile_LUXURYCode +23 -0
- package/README.md +139 -25
- package/docker-compose.yml +16 -0
- package/docs/README.md +65 -18
- package/docs/_cover.md +3 -2
- package/docs/_sidebar.md +52 -7
- package/docs/_topbar.md +2 -0
- package/docs/api/clone-rest-client.md +278 -0
- package/docs/api/connection-manager.md +179 -0
- package/docs/api/guid-map.md +234 -0
- package/docs/api/integration-adapter.md +283 -0
- package/docs/api/operation.md +241 -0
- package/docs/api/sync-entity-initial.md +227 -0
- package/docs/api/sync-entity-ongoing.md +244 -0
- package/docs/api/sync.md +213 -0
- package/docs/api/tabular-check.md +213 -0
- package/docs/api/tabular-transform.md +316 -0
- package/docs/architecture.md +423 -0
- package/docs/cli/comprehensionarray.md +111 -0
- package/docs/cli/comprehensionintersect.md +132 -0
- package/docs/cli/csvcheck.md +111 -0
- package/docs/cli/csvtransform.md +170 -0
- package/docs/cli/data-clone.md +277 -0
- package/docs/cli/jsonarraytransform.md +166 -0
- package/docs/cli/load-comprehension.md +129 -0
- package/docs/cli/objectarraytocsv.md +159 -0
- package/docs/cli/overview.md +96 -0
- package/docs/cli/serve.md +102 -0
- package/docs/cli/tsvtransform.md +144 -0
- package/docs/data-clone/configuration.md +357 -0
- package/docs/data-clone/connection-manager.md +206 -0
- package/docs/data-clone/docker.md +290 -0
- package/docs/data-clone/overview.md +173 -0
- package/docs/data-clone/sync-modes.md +186 -0
- package/docs/implementation-reference.md +311 -0
- package/docs/overview.md +156 -0
- package/docs/quickstart.md +233 -0
- package/docs/rest/comprehension-push.md +209 -0
- package/docs/rest/comprehension.md +506 -0
- package/docs/rest/csv.md +255 -0
- package/docs/rest/entity-generation.md +158 -0
- package/docs/rest/json-array.md +243 -0
- package/docs/rest/overview.md +120 -0
- package/docs/rest/status.md +63 -0
- package/docs/rest/tsv.md +241 -0
- package/docs/retold-catalog.json +93 -3
- package/docs/retold-keyword-index.json +23683 -1901
- package/package.json +6 -3
- package/scripts/run.sh +18 -0
- package/source/Meadow-Integration.js +15 -1
- package/source/cli/Default-Meadow-Integration-Configuration.json +37 -2
- package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
- package/source/cli/commands/Meadow-Integration-Command-DataClone.js +284 -0
- package/source/services/clone/Meadow-Service-ConnectionManager.js +251 -0
- package/source/services/clone/Meadow-Service-Operation.js +196 -0
- package/source/services/clone/Meadow-Service-RestClient.js +364 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-Initial.js +367 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-Ongoing.js +457 -0
- package/source/services/clone/Meadow-Service-Sync.js +142 -0
- /package/docs/examples/bookstore/{mapping_books_Author.json → mapping_books_author.json} +0 -0
- /package/docs/examples/bookstore/{mapping_books_Book.json → mapping_books_book.json} +0 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Docker Deployment
|
|
2
|
+
|
|
3
|
+
The meadow-integration module includes Docker support for running data clone operations in containerized environments.
|
|
4
|
+
|
|
5
|
+
## Dockerfiles
|
|
6
|
+
|
|
7
|
+
### Production Dockerfile
|
|
8
|
+
|
|
9
|
+
The primary `Dockerfile` builds a production-ready image based on `node:20-bookworm`.
|
|
10
|
+
|
|
11
|
+
#### Build Stages
|
|
12
|
+
|
|
13
|
+
The Dockerfile uses a multi-stage build:
|
|
14
|
+
|
|
15
|
+
**Base stage**:
|
|
16
|
+
1. Starts from `node:20-bookworm`.
|
|
17
|
+
2. Installs system utilities: `curl`, `vim`, `nano`, `less`, `tmux`, `uuid-runtime`.
|
|
18
|
+
3. Installs `nodemon` globally.
|
|
19
|
+
4. Copies `package.json` and runs `npm install --omit=dev` (production dependencies only).
|
|
20
|
+
5. Copies `source/` and `scripts/` directories.
|
|
21
|
+
6. Cleans up development artifacts (`package-lock.json`, `.git`, `test`).
|
|
22
|
+
7. If a `Meadow-Config-Docker.json` file exists, it is moved to `source/cli/Default-Meadow-Integration-Configuration.json` to serve as the default configuration inside the container.
|
|
23
|
+
|
|
24
|
+
**Production stage**:
|
|
25
|
+
1. Extends the base stage.
|
|
26
|
+
2. Records the build timestamp in `build.date`.
|
|
27
|
+
3. Runs `scripts/run.sh` as the default command.
|
|
28
|
+
|
|
29
|
+
#### Building the Image
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
docker build -t retold/meadow-integration:latest .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or use the provided helper script:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
./Docker-Build.sh
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Custom Configuration at Build Time
|
|
42
|
+
|
|
43
|
+
To bake a default configuration into the image, create a `Meadow-Config-Docker.json` file in the project root before building:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"Source": {
|
|
48
|
+
"ServerURL": "https://api.production.example.com/1.0/",
|
|
49
|
+
"UserID": "sync_service",
|
|
50
|
+
"Password": "service_password"
|
|
51
|
+
},
|
|
52
|
+
"Destination": {
|
|
53
|
+
"Provider": "MySQL",
|
|
54
|
+
"MySQL": {
|
|
55
|
+
"server": "mysql-host",
|
|
56
|
+
"port": 3306,
|
|
57
|
+
"user": "clone_user",
|
|
58
|
+
"password": "clone_password",
|
|
59
|
+
"database": "production_clone",
|
|
60
|
+
"connectionLimit": 20
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"SchemaPath": "/service_root/schema/Model-Extended.json",
|
|
64
|
+
"Sync": {
|
|
65
|
+
"DefaultSyncMode": "Initial",
|
|
66
|
+
"PageSize": 100,
|
|
67
|
+
"SyncEntityList": []
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This file is automatically picked up during the build and used as the default configuration.
|
|
73
|
+
|
|
74
|
+
### Development Dockerfile (Dockerfile_LUXURYCode)
|
|
75
|
+
|
|
76
|
+
The `Dockerfile_LUXURYCode` builds a development image based on `codercom/code-server:latest`. It provides a browser-based VS Code environment for developing and debugging the meadow-integration module.
|
|
77
|
+
|
|
78
|
+
#### What It Includes
|
|
79
|
+
|
|
80
|
+
- **Base**: code-server (VS Code in the browser)
|
|
81
|
+
- **Runtime**: Node.js 20 via NVM
|
|
82
|
+
- **System tools**: vim, curl, tmux
|
|
83
|
+
- **VS Code extensions**:
|
|
84
|
+
- Mocha Test Adapter
|
|
85
|
+
- Test Explorer
|
|
86
|
+
- Indent Rainbow
|
|
87
|
+
- ESLint
|
|
88
|
+
- GitLens
|
|
89
|
+
|
|
90
|
+
#### Volumes
|
|
91
|
+
|
|
92
|
+
| Volume | Purpose |
|
|
93
|
+
|--------|---------|
|
|
94
|
+
| `/home/coder/.config` | code-server configuration |
|
|
95
|
+
| `/home/coder/.vscode` | VS Code settings |
|
|
96
|
+
| `/home/coder/meadow-integration` | Project source (mount your local checkout here) |
|
|
97
|
+
|
|
98
|
+
#### Building the Development Image
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
docker build -f Dockerfile_LUXURYCode -t retold/meadow-integration-dev:latest .
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Running the Development Image
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
docker run -d \
|
|
108
|
+
-p 8443:8080 \
|
|
109
|
+
-v "$(pwd):/home/coder/meadow-integration" \
|
|
110
|
+
retold/meadow-integration-dev:latest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Access the development environment at `http://localhost:8443`.
|
|
114
|
+
|
|
115
|
+
## Docker Compose
|
|
116
|
+
|
|
117
|
+
The `docker-compose.yml` file defines a service for running the data clone:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
version: '2'
|
|
121
|
+
|
|
122
|
+
services:
|
|
123
|
+
meadow-integration-clone:
|
|
124
|
+
image: retold/meadow-integration:latest
|
|
125
|
+
volumes:
|
|
126
|
+
- "${RETOLD_DIR:-./}:/service_root"
|
|
127
|
+
environment:
|
|
128
|
+
- RUN_LOCAL_DEV=true
|
|
129
|
+
networks:
|
|
130
|
+
- back
|
|
131
|
+
|
|
132
|
+
networks:
|
|
133
|
+
back:
|
|
134
|
+
external:
|
|
135
|
+
name: meadow_backend
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Running with Docker Compose
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
docker-compose up -d meadow-integration-clone
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Or use the provided helper script:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
./Docker-Compose.sh
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Environment Variables
|
|
151
|
+
|
|
152
|
+
| Variable | Default | Description |
|
|
153
|
+
|----------|---------|-------------|
|
|
154
|
+
| `RETOLD_DIR` | `./` | Host directory to mount as `/service_root` |
|
|
155
|
+
| `RUN_LOCAL_DEV` | - | Set to `true` for local development mode |
|
|
156
|
+
| `MEADOW_INTEGRATION_PORT` | `8086` | Port override for the REST server (when running the `serve` command) |
|
|
157
|
+
|
|
158
|
+
### Network
|
|
159
|
+
|
|
160
|
+
The compose file expects an external Docker network named `meadow_backend`. Create it before running:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
docker network create meadow_backend
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Volume Mounts for Configuration
|
|
167
|
+
|
|
168
|
+
### Mounting a Config File
|
|
169
|
+
|
|
170
|
+
To provide a `.meadow.config.json` at runtime without baking it into the image:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
docker run \
|
|
174
|
+
-v "/path/to/.meadow.config.json:/service_root/.meadow.config.json" \
|
|
175
|
+
-v "/path/to/schema:/service_root/schema" \
|
|
176
|
+
retold/meadow-integration:latest
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Mounting a Schema Directory
|
|
180
|
+
|
|
181
|
+
The schema file referenced by `SchemaPath` must be accessible inside the container. Mount it as a volume:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
docker run \
|
|
185
|
+
-v "/host/path/schema:/service_root/schema" \
|
|
186
|
+
retold/meadow-integration:latest
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Production Deployment Tips
|
|
190
|
+
|
|
191
|
+
### Running an Initial Clone
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
docker run --rm \
|
|
195
|
+
--network meadow_backend \
|
|
196
|
+
-v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
|
|
197
|
+
-v "/data/schema:/service_root/schema" \
|
|
198
|
+
retold/meadow-integration:latest \
|
|
199
|
+
node source/cli/Meadow-Integration-CLI-Run.js data-clone --sync_mode Initial
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Running an Ongoing Sync on a Schedule
|
|
203
|
+
|
|
204
|
+
Use cron or a container orchestrator to run ongoing syncs periodically:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
docker run --rm \
|
|
208
|
+
--network meadow_backend \
|
|
209
|
+
-v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
|
|
210
|
+
-v "/data/schema:/service_root/schema" \
|
|
211
|
+
retold/meadow-integration:latest \
|
|
212
|
+
node source/cli/Meadow-Integration-CLI-Run.js data-clone --sync_mode Ongoing
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Using the Post-Run Delay
|
|
216
|
+
|
|
217
|
+
The `--post_run_delay` flag keeps the container alive for a specified number of minutes after the sync completes. This is useful in orchestrated environments where you want to inspect logs before the container exits:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
docker run --rm \
|
|
221
|
+
--network meadow_backend \
|
|
222
|
+
-v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
|
|
223
|
+
-v "/data/schema:/service_root/schema" \
|
|
224
|
+
retold/meadow-integration:latest \
|
|
225
|
+
node source/cli/Meadow-Integration-CLI-Run.js data-clone --post_run_delay 5
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Running the REST Server in Docker
|
|
229
|
+
|
|
230
|
+
To run the integration REST API server inside Docker:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
docker run -d \
|
|
234
|
+
-p 8086:8086 \
|
|
235
|
+
--name meadow-integration-api \
|
|
236
|
+
retold/meadow-integration:latest \
|
|
237
|
+
node source/cli/Meadow-Integration-CLI-Run.js serve --port 8086
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Tagging and Pushing
|
|
241
|
+
|
|
242
|
+
Helper scripts are provided for image management:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Tag the image
|
|
246
|
+
./Docker-Tag.sh
|
|
247
|
+
|
|
248
|
+
# Push to a registry
|
|
249
|
+
./Docker-Push.sh
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Health Checks
|
|
253
|
+
|
|
254
|
+
When running the REST server, use the Status endpoint as a health check:
|
|
255
|
+
|
|
256
|
+
```yaml
|
|
257
|
+
services:
|
|
258
|
+
meadow-integration-api:
|
|
259
|
+
image: retold/meadow-integration:latest
|
|
260
|
+
command: node source/cli/Meadow-Integration-CLI-Run.js serve
|
|
261
|
+
ports:
|
|
262
|
+
- "8086:8086"
|
|
263
|
+
healthcheck:
|
|
264
|
+
test: ["CMD", "curl", "-f", "http://localhost:8086/1.0/Status"]
|
|
265
|
+
interval: 30s
|
|
266
|
+
timeout: 10s
|
|
267
|
+
retries: 3
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Database Connectivity
|
|
271
|
+
|
|
272
|
+
When running inside Docker, make sure the container can reach the database server. Common patterns:
|
|
273
|
+
|
|
274
|
+
- **Same Docker network**: Use the container name as the hostname (e.g., `mysql-server`).
|
|
275
|
+
- **Host machine**: Use `host.docker.internal` (Docker Desktop) or `172.17.0.1` (Linux).
|
|
276
|
+
- **External database**: Use the external hostname or IP.
|
|
277
|
+
|
|
278
|
+
Update the `Destination` configuration accordingly:
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"Destination": {
|
|
283
|
+
"Provider": "MySQL",
|
|
284
|
+
"MySQL": {
|
|
285
|
+
"server": "mysql-server",
|
|
286
|
+
"database": "production_clone"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Data Clone System Overview
|
|
2
|
+
|
|
3
|
+
The data clone system synchronizes data from a remote Meadow API server to a local relational database. It is designed for offline access, reporting, data warehousing, and building local replicas of Meadow-managed datasets.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
The data clone system connects to a source Meadow REST API, reads entity records using paginated queries, and writes them into a local MySQL or MSSQL database. Tables are auto-created from the Meadow schema, and indexes are built on GUID and Deleted columns for query performance.
|
|
8
|
+
|
|
9
|
+
## Why Use It
|
|
10
|
+
|
|
11
|
+
- **Offline access**: Work with your data without a persistent connection to the source API.
|
|
12
|
+
- **Reporting**: Run complex SQL queries against a local database without impacting the production API.
|
|
13
|
+
- **Data warehousing**: Aggregate data from Meadow APIs into a central database for analytics.
|
|
14
|
+
- **Development**: Maintain a local copy of production data for development and testing.
|
|
15
|
+
|
|
16
|
+
## Key Components
|
|
17
|
+
|
|
18
|
+
### ConnectionManager
|
|
19
|
+
|
|
20
|
+
Manages database connections for MySQL and MSSQL providers. Handles connection pooling, table creation, and index management.
|
|
21
|
+
|
|
22
|
+
See [connection-manager.md](connection-manager.md) for details.
|
|
23
|
+
|
|
24
|
+
### RestClient
|
|
25
|
+
|
|
26
|
+
A REST client service (`MeadowCloneRestClient`) that communicates with the source Meadow API. Handles authentication, session management, caching, and paginated entity retrieval via keep-alive HTTP connections.
|
|
27
|
+
|
|
28
|
+
Key capabilities:
|
|
29
|
+
- Session-based authentication (`authenticate` / `deauthenticate`)
|
|
30
|
+
- CRUD operations (`getEntity`, `createEntity`, `updateEntity`, `upsertEntity`, `deleteEntity`)
|
|
31
|
+
- Bulk retrieval with automatic pagination (`getEntitySet`)
|
|
32
|
+
- Built-in LRU object cache per entity type (30-second TTL, 10,000 entries)
|
|
33
|
+
|
|
34
|
+
### Sync
|
|
35
|
+
|
|
36
|
+
The `MeadowSync` service orchestrates the overall synchronization. It loads a Meadow schema, creates `SyncEntity` instances for each entity, and runs them in sequence.
|
|
37
|
+
|
|
38
|
+
### SyncEntity (Initial and Ongoing)
|
|
39
|
+
|
|
40
|
+
Two sync strategies are available:
|
|
41
|
+
|
|
42
|
+
- **MeadowSyncEntityInitial**: Full clone using ID-based pagination. Only creates new records.
|
|
43
|
+
- **MeadowSyncEntityOngoing**: Incremental sync using UpdateDate comparison. Creates new records and updates changed ones.
|
|
44
|
+
|
|
45
|
+
See [sync-modes.md](sync-modes.md) for a detailed comparison.
|
|
46
|
+
|
|
47
|
+
## Supported Providers
|
|
48
|
+
|
|
49
|
+
| Provider | Module | Default Port |
|
|
50
|
+
|----------|--------|-------------|
|
|
51
|
+
| MySQL | `meadow-connection-mysql` | 3306 |
|
|
52
|
+
| MSSQL | `meadow-connection-mssql` | 1433 |
|
|
53
|
+
|
|
54
|
+
## Sync Modes
|
|
55
|
+
|
|
56
|
+
| Mode | Strategy | Creates | Updates | Use Case |
|
|
57
|
+
|------|----------|---------|---------|----------|
|
|
58
|
+
| Initial | ID-based pagination from max local ID | Yes | No | First-time full clone |
|
|
59
|
+
| Ongoing | UpdateDate comparison across all records | Yes | Yes | Incremental sync after initial clone |
|
|
60
|
+
|
|
61
|
+
## Running a Data Clone
|
|
62
|
+
|
|
63
|
+
### Via CLI
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mdwint data-clone \
|
|
67
|
+
--api_server "https://api.example.com/1.0/" \
|
|
68
|
+
--api_username "admin" \
|
|
69
|
+
--api_password "secret" \
|
|
70
|
+
--db_host "127.0.0.1" \
|
|
71
|
+
--db_name "local_clone" \
|
|
72
|
+
--schema_path "./schema/Model-Extended.json" \
|
|
73
|
+
--sync_mode "Initial"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Via Configuration File
|
|
77
|
+
|
|
78
|
+
Create a `.meadow.config.json` file (see [configuration.md](configuration.md)) and run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
mdwint data-clone
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Programmatically
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
const meadowIntegration = require('meadow-integration');
|
|
88
|
+
const libFable = require('fable');
|
|
89
|
+
|
|
90
|
+
const fable = new libFable({});
|
|
91
|
+
|
|
92
|
+
// Register services
|
|
93
|
+
fable.serviceManager.addServiceType('MeadowCloneRestClient', meadowIntegration.CloneRestClient);
|
|
94
|
+
fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient', {
|
|
95
|
+
ServerURL: 'https://api.example.com/1.0/',
|
|
96
|
+
UserID: 'admin',
|
|
97
|
+
Password: 'secret'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
fable.serviceManager.addServiceType('MeadowConnectionManager', meadowIntegration.ConnectionManager);
|
|
101
|
+
fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager', {
|
|
102
|
+
Provider: 'MySQL',
|
|
103
|
+
MySQL: {
|
|
104
|
+
server: '127.0.0.1',
|
|
105
|
+
port: 3306,
|
|
106
|
+
user: 'root',
|
|
107
|
+
password: '',
|
|
108
|
+
database: 'local_clone',
|
|
109
|
+
connectionLimit: 20
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
fable.serviceManager.addServiceType('MeadowSync', meadowIntegration.Sync);
|
|
114
|
+
|
|
115
|
+
// Authenticate, connect, load schema, sync
|
|
116
|
+
fable.MeadowCloneRestClient.authenticate((pAuthError) => {
|
|
117
|
+
fable.MeadowConnectionManager.connect((pConnectError, pPool) => {
|
|
118
|
+
fable.serviceManager.instantiateServiceProvider('MeadowSync', {
|
|
119
|
+
ConnectionPool: pPool,
|
|
120
|
+
PageSize: 100
|
|
121
|
+
});
|
|
122
|
+
fable.MeadowSync.SyncMode = 'Initial';
|
|
123
|
+
const schema = require('./schema/Model-Extended.json');
|
|
124
|
+
fable.MeadowSync.loadMeadowSchema(schema, (pLoadError) => {
|
|
125
|
+
fable.MeadowSync.syncAll((pSyncError) => {
|
|
126
|
+
console.log('Sync complete!');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Architecture Diagram
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
+-------------------+ HTTPS/HTTP +-------------------+
|
|
137
|
+
| Source Meadow | <-------------------> | RestClient |
|
|
138
|
+
| API Server | GET /Entity/... | (MeadowClone |
|
|
139
|
+
+-------------------+ Session Auth | RestClient) |
|
|
140
|
+
+--------+----------+
|
|
141
|
+
|
|
|
142
|
+
v
|
|
143
|
+
+-------------------+
|
|
144
|
+
| MeadowSync |
|
|
145
|
+
| (Orchestrator) |
|
|
146
|
+
+--------+----------+
|
|
147
|
+
|
|
|
148
|
+
+------------+------------+
|
|
149
|
+
| |
|
|
150
|
+
+------+------+ +-------+-------+
|
|
151
|
+
| SyncEntity | | SyncEntity |
|
|
152
|
+
| Initial | | Ongoing |
|
|
153
|
+
+------+------+ +-------+-------+
|
|
154
|
+
| |
|
|
155
|
+
v v
|
|
156
|
+
+------+-------------------------+------+
|
|
157
|
+
| ConnectionManager |
|
|
158
|
+
| (MySQL / MSSQL) |
|
|
159
|
+
+---------------------------------------+
|
|
160
|
+
|
|
|
161
|
+
v
|
|
162
|
+
+-------------------+
|
|
163
|
+
| Local Database |
|
|
164
|
+
| (MySQL / MSSQL) |
|
|
165
|
+
+-------------------+
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Related Documentation
|
|
169
|
+
|
|
170
|
+
- [Connection Manager](connection-manager.md) -- Database connection setup and index creation
|
|
171
|
+
- [Sync Modes](sync-modes.md) -- Detailed comparison of Initial vs Ongoing sync
|
|
172
|
+
- [Configuration](configuration.md) -- Full `.meadow.config.json` reference
|
|
173
|
+
- [Docker Deployment](docker.md) -- Running data-clone in Docker
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Sync Modes
|
|
2
|
+
|
|
3
|
+
The data clone system supports two sync modes: **Initial** and **Ongoing**. Each mode uses a different strategy for determining which records to fetch and how to write them to the local database.
|
|
4
|
+
|
|
5
|
+
## Initial Sync
|
|
6
|
+
|
|
7
|
+
**Purpose**: Perform a full clone of data from the source API to the local database.
|
|
8
|
+
|
|
9
|
+
### Strategy
|
|
10
|
+
|
|
11
|
+
1. **Read local max ID**: Query the local database for the maximum value of the entity's `DefaultIdentifier` (primary key).
|
|
12
|
+
2. **Read local count**: Count the total records in the local database.
|
|
13
|
+
3. **Read server max ID**: Query the source API for the maximum ID via `GET /{Entity}/Max/{DefaultIdentifier}`.
|
|
14
|
+
4. **Read server count**: Query the source API for the total count via `GET /{Entity}s/Count`.
|
|
15
|
+
5. **Estimate work**: Calculate the estimated number of new records as `Server.RecordCount - Local.RecordCount`.
|
|
16
|
+
6. **Generate paginated URLs**: Build a list of URL partials using the filter `FBV~{ID}~GT~{LocalMaxID}~FSF~{ID}~ASC~ASC` with pagination offsets.
|
|
17
|
+
7. **Fetch and insert**: For each page of records:
|
|
18
|
+
- Check if the record exists locally by its ID.
|
|
19
|
+
- If the record does not exist, marshal and create it.
|
|
20
|
+
- If the record already exists, skip it.
|
|
21
|
+
|
|
22
|
+
### Key Characteristics
|
|
23
|
+
|
|
24
|
+
| Aspect | Behavior |
|
|
25
|
+
|--------|----------|
|
|
26
|
+
| Record creation | Yes |
|
|
27
|
+
| Record updates | No |
|
|
28
|
+
| Pagination | All pages generated upfront based on server count |
|
|
29
|
+
| Filter | Records with ID greater than local max |
|
|
30
|
+
| Sort | Ascending by DefaultIdentifier |
|
|
31
|
+
| Concurrency | 1 page at a time, 5 records in parallel per page |
|
|
32
|
+
| Progress tracking | Tracks estimated vs completed record count |
|
|
33
|
+
|
|
34
|
+
### When to Use
|
|
35
|
+
|
|
36
|
+
- First-time data cloning from a source API.
|
|
37
|
+
- Rebuilding a local database from scratch.
|
|
38
|
+
- When you only need new records and do not need to detect changes to existing records.
|
|
39
|
+
|
|
40
|
+
### Create Behavior
|
|
41
|
+
|
|
42
|
+
When creating records during Initial sync, the following flags are set on the Meadow query:
|
|
43
|
+
|
|
44
|
+
- `setDisableAutoIdentity(true)` -- Preserves the original ID from the source.
|
|
45
|
+
- `setDisableAutoDateStamp(true)` -- Preserves original CreateDate/UpdateDate values.
|
|
46
|
+
- `setDisableAutoUserStamp(true)` -- Preserves original CreatingIDUser/UpdatingIDUser values.
|
|
47
|
+
- `setDisableDeleteTracking(true)` -- Preserves the original Deleted flag.
|
|
48
|
+
- `AllowIdentityInsert = true` -- Allows inserting records with explicit ID values.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Ongoing Sync
|
|
53
|
+
|
|
54
|
+
**Purpose**: Incrementally sync new and changed records from the source API.
|
|
55
|
+
|
|
56
|
+
### Strategy
|
|
57
|
+
|
|
58
|
+
1. **Check for UpdateDate column**: Verify that the entity schema contains an `UpdateDate` column.
|
|
59
|
+
2. **Read local max ID**: Same as Initial.
|
|
60
|
+
3. **Read local max UpdateDate**: Query for the most recent `UpdateDate` in the local database.
|
|
61
|
+
4. **Read local count**: Same as Initial.
|
|
62
|
+
5. **Read server max ID**: Same as Initial.
|
|
63
|
+
6. **Read server max UpdateDate**: Query the source API via `GET /{Entity}/Max/UpdateDate`.
|
|
64
|
+
7. **Read server count**: Same as Initial.
|
|
65
|
+
8. **Estimate request count**: Calculate `Math.ceil(Server.RecordCount / PageSize)`.
|
|
66
|
+
9. **Recursive fetch**: Using a recursive `anticipate` pattern:
|
|
67
|
+
- Fetch a page of records sorted by ID ascending, starting from `LastRequestedID`.
|
|
68
|
+
- For each record:
|
|
69
|
+
- If it does not exist locally, create it.
|
|
70
|
+
- If it exists, compare `UpdateDate` values. If the difference exceeds 5 milliseconds, update the local record.
|
|
71
|
+
- If the dates are within 5ms, skip the record.
|
|
72
|
+
- After processing the page, recursively add the next page fetch to the anticipate chain.
|
|
73
|
+
- Continue until all estimated pages have been processed.
|
|
74
|
+
|
|
75
|
+
### Key Characteristics
|
|
76
|
+
|
|
77
|
+
| Aspect | Behavior |
|
|
78
|
+
|--------|----------|
|
|
79
|
+
| Record creation | Yes |
|
|
80
|
+
| Record updates | Yes |
|
|
81
|
+
| Pagination | Recursive, one page at a time |
|
|
82
|
+
| Filter | Records with ID greater than `LastRequestedID` (starts at 0) |
|
|
83
|
+
| Sort | Ascending by DefaultIdentifier |
|
|
84
|
+
| UpdateDate threshold | 5 milliseconds (changes smaller than 5ms are ignored) |
|
|
85
|
+
| Concurrency | Sequential pages, sequential records within each page |
|
|
86
|
+
| Progress tracking | Tracks request count against estimated total |
|
|
87
|
+
|
|
88
|
+
### Recursive Anticipate Pattern
|
|
89
|
+
|
|
90
|
+
The Ongoing sync uses a recursive pattern where each page fetch, upon completion, adds another page fetch to the `anticipate` queue. This ensures that the `LastRequestedID` advances as records are processed:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Page 1 (IDs 1-100) -> process -> update LastRequestedID to 100
|
|
94
|
+
-> add Page 2 (IDs > 100) to anticipate queue
|
|
95
|
+
Page 2 (IDs 101-200) -> process -> update LastRequestedID to 200
|
|
96
|
+
-> add Page 3 (IDs > 200) to anticipate queue
|
|
97
|
+
...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This approach handles cases where the server record count changes during the sync process.
|
|
101
|
+
|
|
102
|
+
### When to Use
|
|
103
|
+
|
|
104
|
+
- After an Initial sync has been completed.
|
|
105
|
+
- When running periodic sync jobs to keep the local database up to date.
|
|
106
|
+
- When you need to detect and apply changes to existing records.
|
|
107
|
+
|
|
108
|
+
### Create and Update Behavior
|
|
109
|
+
|
|
110
|
+
**Creating** new records uses the same flags as Initial sync (disabled auto-identity, auto-date, auto-user, and delete tracking with `AllowIdentityInsert`).
|
|
111
|
+
|
|
112
|
+
**Updating** existing records also uses the same disabled flags to preserve the original metadata from the source.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Comparison Table
|
|
117
|
+
|
|
118
|
+
| Feature | Initial | Ongoing |
|
|
119
|
+
|---------|---------|---------|
|
|
120
|
+
| Creates new records | Yes | Yes |
|
|
121
|
+
| Updates existing records | No | Yes |
|
|
122
|
+
| Starting point | Local max ID | ID 0 (scans all records) |
|
|
123
|
+
| Pagination strategy | Pre-computed URL list | Recursive anticipate |
|
|
124
|
+
| UpdateDate comparison | No | Yes (5ms threshold) |
|
|
125
|
+
| Schema requirement | DefaultIdentifier | DefaultIdentifier, UpdateDate |
|
|
126
|
+
| Typical use | First-time clone | Incremental updates |
|
|
127
|
+
| Performance | Fast (skips existing) | Thorough (checks every record) |
|
|
128
|
+
|
|
129
|
+
## Setting the Sync Mode
|
|
130
|
+
|
|
131
|
+
### Via CLI
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
mdwint data-clone --sync_mode Initial
|
|
135
|
+
mdwint data-clone --sync_mode Ongoing
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Via Configuration
|
|
139
|
+
|
|
140
|
+
In `.meadow.config.json`:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"Sync": {
|
|
145
|
+
"DefaultSyncMode": "Initial"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Programmatically
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
fable.MeadowSync.SyncMode = 'Ongoing';
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The sync mode must be set **before** calling `loadMeadowSchema()`, because the mode determines which `SyncEntity` class (Initial or Ongoing) is instantiated for each entity.
|
|
157
|
+
|
|
158
|
+
## Record Marshaling
|
|
159
|
+
|
|
160
|
+
Both sync modes use the same `marshalRecord` method to prepare source records for local database insertion:
|
|
161
|
+
|
|
162
|
+
- Columns defined in the entity schema are mapped from the source record.
|
|
163
|
+
- `null` and `undefined` values are skipped.
|
|
164
|
+
- Object values are JSON-stringified.
|
|
165
|
+
- `DateTime` values are reformatted to `YYYY-MM-DD HH:mm:ss.SSS` in UTC.
|
|
166
|
+
- Empty string values are skipped for non-DateTime fields.
|
|
167
|
+
- Columns ending in `JSON` are auto-populated from matching object properties (e.g., `ConfigJSON` is populated from `Config` if `Config` is an object).
|
|
168
|
+
|
|
169
|
+
## Table and Index Creation
|
|
170
|
+
|
|
171
|
+
Both sync modes automatically:
|
|
172
|
+
|
|
173
|
+
1. Create the database table if it does not exist (via Meadow provider's `createTable`).
|
|
174
|
+
2. Create a unique index on the GUID column (if present).
|
|
175
|
+
3. Create a non-unique index on the Deleted column (if present).
|
|
176
|
+
|
|
177
|
+
This happens during the `initialize()` phase before any records are synced.
|
|
178
|
+
|
|
179
|
+
## Progress Tracking
|
|
180
|
+
|
|
181
|
+
Both modes use the `MeadowOperation` utility class for progress tracking:
|
|
182
|
+
|
|
183
|
+
- **Initial**: Tracks `FullSync-{TableName}` with estimated record count.
|
|
184
|
+
- **Ongoing**: Tracks `UpdateSync-{TableName}` with estimated request count.
|
|
185
|
+
|
|
186
|
+
Progress includes: percent complete, average operation time, and estimated completion time.
|