toga-ai 1.0.118 → 1.0.119
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.
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
| [_underscore Framework Architecture](architecture.md) | `_underscore` is the shared PHP backend framework for **all 2.0 applications**. | _underscore/_underscore.php, _underscore/Loader.php, _underscore/Framework.php, _underscore/Model.php, _underscore/Database.php, _underscore/Query.php, _underscore/Route.php, _underscore/Component.php |
|
|
6
6
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
7
7
|
| [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
|
|
8
|
+
| [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
|
|
8
9
|
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
|
|
9
10
|
| [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql |
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Per-Client Database Connections & the Local Logs Trap
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-18
|
|
10
|
+
owners: ["dfranks"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Database.php
|
|
13
|
+
- _underscore/ApiRequest.php
|
|
14
|
+
- _underscore/Model/Client/Logs/Api.php
|
|
15
|
+
related:
|
|
16
|
+
- ../architecture.md
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Summary
|
|
20
|
+
|
|
21
|
+
When `_underscore` serves a request for a client it opens **three distinct per-client
|
|
22
|
+
database connections**, not one. Each is an alias whose real schema name comes from Core's
|
|
23
|
+
`Databases` table for that client. Missing any one of them locally makes `mysqli_connect`
|
|
24
|
+
throw `Unknown database` and the whole request returns **HTTP 500 / EO-1** — even when the
|
|
25
|
+
actual work succeeded. The most common victim is the **logs** connection, because API
|
|
26
|
+
transaction logging is on by default.
|
|
27
|
+
|
|
28
|
+
## Key files / entry points
|
|
29
|
+
|
|
30
|
+
- **`_underscore/Database.php`** — `connect()` (line ~410) resolves a connection *alias*
|
|
31
|
+
(`Client`, `ClientLogs`, `Archive`) to the concrete schema via the Core join that selects
|
|
32
|
+
`ClientDatabase.name`, `LogDatabase.name`, `ArchiveDatabase.name` (lines ~64–137). The alias
|
|
33
|
+
`_underscore::DB_CLIENT_LOGS` maps to the per-client `Logs_<Id>` schema.
|
|
34
|
+
- **`_underscore/ApiRequest.php`** (line ~240) — writes each API transaction to the logs DB
|
|
35
|
+
via `_Model_Client_Logs_Api` (falls back to `_Model_Core_Logs_Api` only when the client-logs
|
|
36
|
+
register points at the same DB as core-logs). This is what fires the logs connection.
|
|
37
|
+
- **`_underscore/Model/Client/Logs/Api.php`** — `const DATABASE = _underscore::DB_CLIENT_LOGS;
|
|
38
|
+
const TABLE = 'Api';` — the model behind the logged row.
|
|
39
|
+
|
|
40
|
+
## How it works
|
|
41
|
+
|
|
42
|
+
The three per-client aliases and their concrete local schemas:
|
|
43
|
+
|
|
44
|
+
| Alias | Schema (per client) | Holds |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `Client` (data) | `Client_<Id>` | operational data |
|
|
47
|
+
| `ClientLogs` (`DB_CLIENT_LOGS`) | `Logs_<Id>` | **API transaction logging** |
|
|
48
|
+
| `Archive` | `Archive_<Id>` | historical snapshots |
|
|
49
|
+
|
|
50
|
+
`<Id>` is the client's `clientIdentifier` from `Core.Clients` (e.g. `Growrk` → `Client_Growrk`,
|
|
51
|
+
`Logs_Growrk`, `Archive_Growrk`). A `Logs_<Id>` schema has 5 tables:
|
|
52
|
+
`Api, CustomRecordField, Error, Record, RecordField`.
|
|
53
|
+
|
|
54
|
+
## Data model
|
|
55
|
+
|
|
56
|
+
`Logs_<Id>.Api` columns: `id, uuid, dtStamp, transactionId, apiId, userId, isAuthRequest,
|
|
57
|
+
direction, source, sourceIp, instanceId, executionTimeSeconds, method, hostname, route,
|
|
58
|
+
responseCode, queryString, requestHeaders, requestPayload, responseHeaders, responsePayload`.
|
|
59
|
+
Engine InnoDB, `utf8mb4_unicode_ci`.
|
|
60
|
+
|
|
61
|
+
## Client variations
|
|
62
|
+
|
|
63
|
+
None — uniform across clients; only the `<Id>` suffix differs.
|
|
64
|
+
|
|
65
|
+
## Gotchas / known issues
|
|
66
|
+
|
|
67
|
+
- **The "logs DB write trap" (local dev).** A laptop usually imports only `Client_<Id>`, not
|
|
68
|
+
`Logs_<Id>`/`Archive_<Id>`. Because logging is **on by default**, the first request 500s on
|
|
69
|
+
`Unknown database 'logs_<id>'` from `ApiRequest.php`'s logging write — *after* the real work
|
|
70
|
+
ran — so it reads like a mysterious outage, not a missing schema. The trace shows
|
|
71
|
+
`_Database::connect("ClientLogs","READ")` → `mysqli_connect(..., "Logs_<Id>")`.
|
|
72
|
+
- **Fix once and for all locally:** for every client you run locally, create all three schemas.
|
|
73
|
+
For logs, structure-only is enough (you never need prod log rows locally):
|
|
74
|
+
`mysqldump -h reader1.logs.database.togahub.com -u admin -p<pw> --no-data --no-tablespaces
|
|
75
|
+
--set-gtid-purged=OFF --column-statistics=0 Logs_<Id> | mysql -u root Logs_<Id>` (create the
|
|
76
|
+
empty DB first). Data DB comes from `reader1.client.database.togahub.com` (see the local-DB
|
|
77
|
+
refresh notes). This is a **local-environment** gap, not a code bug — do not "fix" it by
|
|
78
|
+
disabling logging in shared code.
|
|
79
|
+
- Related 1.0 analogue: the legacy `App_` worker has the same hazard writing to `Logs.API`
|
|
80
|
+
(`db_logs`) — the laptop trap there is documented separately in the worker NetSuite bootstrap
|
|
81
|
+
notes.
|
|
82
|
+
|
|
83
|
+
## Change history
|
|
84
|
+
|
|
85
|
+
- 2026-06-18 — Documented after the `Logs_Growrk`/`Logs_Aig` local 500s while testing the
|
|
86
|
+
NetSuite TOGa Supply wrappers; created the missing local log schemas structure-only. (dfranks)
|
|
87
|
+
|
|
88
|
+
## Related docs
|
|
89
|
+
|
|
90
|
+
- [_underscore architecture](../architecture.md)
|
|
91
|
+
- [NetSuite → TOGa Supply per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) — the work that surfaced this.
|
package/knowledge/INDEX.md
CHANGED
|
@@ -14,7 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
14
14
|
|
|
15
15
|
## 2.0 framework
|
|
16
16
|
|
|
17
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
17
|
+
- **_underscore** (_Underscore) _(framework core)_ — 8 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
18
18
|
- **worker2** (Worker) — 8 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
19
19
|
- **api2** (API) — 3 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
20
20
|
- **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
package/package.json
CHANGED