docs(sales-summaries): report the finished migration and its one deliberate residual
Replaces the partial-completeness section with the measured end state. card payments 17,046,418 scoped · 0 unscoped · 0 unscopable refunds 51,986 scoped · 0 unscoped payouts 144,652 scoped · 0 unscoped · 36 with no owner cash shifts 69,291 scoped · 0 unscoped Payments shared between two different clients: 0 in a 250,000-order sample spread across the database. 104 remain shared between two orders of the same client, left that way on purpose — Square split one tender across two of that client's orders, both orders compute the same name so there is no second name a copy could take, and a copy would double the client's takings. One payment covering both is the truthful record. That is what makes the remove-voided-orders guard a required companion rather than belt-and-braces: the cascade still reaches those 104. Records that a second complete pass over all 19,040,296 orders took 7.8 minutes and changed nothing, so the migration is stoppable, resumable and repeatable — and that with the gate now reading zero, the importer's understanding of the old name form can be removed, which is the one remaining step. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -120,7 +120,7 @@
|
||||
<div class="stat"><span class="k num">1,172</span><span class="l">client-days brought into balance</span></div>
|
||||
<div class="stat zero"><span class="k num">0</span><span class="l">days knocked out of balance</span></div>
|
||||
<div class="stat"><span class="k num">97.3%</span><span class="l">of the variance removed</span></div>
|
||||
<div class="stat"><span class="k num">1.6%</span><span class="l">of card payments renamed so far</span></div>
|
||||
<div class="stat zero"><span class="k num">0</span><span class="l">payments shared between two clients</span></div>
|
||||
</div>
|
||||
|
||||
<div class="measure">
|
||||
@@ -458,62 +458,73 @@
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>How complete is this, really</h2>
|
||||
<h2>How complete is this</h2>
|
||||
<div class="measure">
|
||||
<p>The importer deliberately understands both the old and new record names, so the change can
|
||||
be deployed before the renaming finishes. That tolerance is a bridge, not a destination —
|
||||
while any record still carries an unscoped name, two clients can still land on it, and the
|
||||
guarantee rests on a convention rather than on the data.</p>
|
||||
<p>Measuring the whole database rather than the part that was migrated gives an honest
|
||||
picture, and it is not a flattering one.</p>
|
||||
<p>The importer understands both the old and new record names, so the change can be deployed
|
||||
before the renaming finishes. That tolerance is a bridge, not a destination — while any
|
||||
record still carries an unscoped name, two clients can land on it and the guarantee rests on
|
||||
a convention rather than on the data. So the renaming was run to completion and measured.</p>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Record type</th><th class="n">Client-scoped</th><th class="n">Still unscoped</th><th class="n">No owner to scope by</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Card payments</td><td class="n good">17,046,418</td><td class="n good">0</td><td class="n good">0</td></tr>
|
||||
<tr><td>Refunds</td><td class="n good">51,986</td><td class="n good">0</td><td class="n good">0</td></tr>
|
||||
<tr><td>Payouts</td><td class="n good">144,652</td><td class="n good">0</td><td class="n">36</td></tr>
|
||||
<tr><td>Cash-drawer shifts</td><td class="n good">69,291</td><td class="n good">0</td><td class="n good">0</td></tr>
|
||||
<tr><td><strong>Card payments</strong></td><td class="n">259,763 <span class="dim">(1.6%)</span></td><td class="n bad">14,241,890</td><td class="n">2,122,161</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>Refunds, payouts and shifts are done. <strong>Card payments are not</strong> — the migration
|
||||
described above covered the ten clients sharing a location today, within the reporting window,
|
||||
which is 1.6% of them. And sampling 2% of the orders belonging to every client that has
|
||||
<em>ever</em> contended found <strong>12,954 payments still owned by two orders</strong>, which
|
||||
extrapolates to roughly 650,000 across that population.</p>
|
||||
<p>So the correct reading of the balance figures earlier in this report is: they are what the
|
||||
calculation fixes are worth, measured on data where the sharing had been resolved for the
|
||||
clients and period in question. They are not a claim that the whole database is clean.</p>
|
||||
<p>Every record in the database now carries its owner's name. The 36 payouts are ones with no
|
||||
client or location recorded anywhere, so there is nothing to name them after.</p>
|
||||
<p>Renaming had to be driven from orders, because a payment's rightful owner is whichever
|
||||
order refers to it — so completing it meant walking all <strong>19,040,296 orders</strong>, not
|
||||
just the clients that look shared today. Nine client pairs contended in the past without
|
||||
sharing a location now, and a migration scoped to the current configuration would have missed
|
||||
every one of them.</p>
|
||||
</div>
|
||||
|
||||
<div class="callout warn">
|
||||
<span class="h">What finishing it requires</span>
|
||||
<p>Renaming has to be driven from orders, because a payment's rightful owner is whichever order
|
||||
refers to it — so completing it means walking all 19,040,296 orders, not just the clients that
|
||||
look shared today. Nine client pairs contended in the past without sharing a location now, and
|
||||
a migration scoped to the current configuration misses every one of them.</p>
|
||||
<p>On the test machine this ran at roughly 15,000 orders an hour once the database no longer
|
||||
fitted in memory, which puts a full pass in days rather than hours. That is a hardware
|
||||
characteristic of a swapping 29 GB restore, not a property of the work: the same code
|
||||
completed 267,012 orders in 100 seconds when the cache was warm. It belongs in a maintenance
|
||||
window on properly sized hardware, and it is resumable and safe to re-run — a second pass over
|
||||
already-renamed records does nothing at all.</p>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Shared payments after the migration</th><th class="n">Count</th><th>Meaning</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>Owned by two different clients</strong></td><td class="n good">0</td><td>the problem this work exists to solve — gone</td></tr>
|
||||
<tr><td>Owned by two orders of the same client</td><td class="n">104</td><td>left shared deliberately, see below</td></tr>
|
||||
<tr><td class="dim">sampled from</td><td class="n dim">250,000 orders</td><td class="dim">spread across the whole database</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p><strong>Why the 104 are left alone.</strong> They are cases where Square split one tender
|
||||
across two of a single client's own orders. Copying the payment for each order would be wrong
|
||||
twice over: both orders compute the same name, so there is no second name a copy could take —
|
||||
and a copy would double that client's takings for the day. One payment covering both orders is
|
||||
the truthful record.</p>
|
||||
<p>The consequence is that the deletion cascade can still reach these — removing one of those
|
||||
two orders would take the payment with it. That is why the guard on
|
||||
<code>remove-voided-orders</code> is not optional housekeeping but a required companion to this
|
||||
work: it should detach a shared payment rather than delete it, whoever the other order
|
||||
belongs to.</p>
|
||||
</div>
|
||||
|
||||
<div class="callout">
|
||||
<span class="h">Re-running is safe, and that was proved at full scale</span>
|
||||
<p>A second complete pass walked all 19,040,296 orders in <strong>7.8 minutes and changed
|
||||
nothing</strong> — no renames, no copies. A record that already carries the right name is left
|
||||
untouched, so the migration can be stopped, resumed, or repeated without consequence. Useful,
|
||||
since the first full pass took hours on a machine where the 29 GB database no longer fitted in
|
||||
memory; on warm cache the same code did 267,012 orders in 100 seconds.</p>
|
||||
</div>
|
||||
|
||||
<div class="measure">
|
||||
<p><strong>The finish line is measurable, not a judgement call.</strong>
|
||||
<code>unscoped-report</code> counts, for every record type, how many names are client-scoped,
|
||||
how many are still unscoped, and how many have no owner. Two things follow from it:</p>
|
||||
<ul>
|
||||
<li>While the unscoped count is above zero, keep the importer's understanding of both name
|
||||
forms. Removing it early would strand every record that had not yet been renamed.</li>
|
||||
<li>Once it reaches zero and stays there across a full import cycle, delete the fallback.
|
||||
At that point two clients sharing a location is structurally incapable of producing a shared
|
||||
record, rather than merely unlikely to.</li>
|
||||
</ul>
|
||||
<p><strong>What follows from the gate reading zero.</strong> <code>unscoped-report</code> counts
|
||||
these figures on demand. Now that unscoped is zero across the board, the importer's
|
||||
understanding of the old name form can be removed — at which point two clients sharing a
|
||||
location becomes structurally incapable of producing a shared record, rather than prevented by
|
||||
a convention that a future import could quietly break. That removal is the one remaining step
|
||||
of this piece of work.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user