Have drop() name the event a lost link warrants, and name requestOnCurrentLink() for what it does
Mirror / push (push) Successful in 6s
Test / test (18) (pull_request) Successful in 31s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Test / lint (pull_request) Successful in 26s
Test / test (20) (pull_request) Successful in 31s
Test / test (22) (pull_request) Successful in 31s
Mirror / push (push) Successful in 6s
Test / test (18) (pull_request) Successful in 31s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Test / lint (pull_request) Successful in 26s
Test / test (20) (pull_request) Successful in 31s
Test / test (22) (pull_request) Successful in 31s
This commit is contained in:
+4
-4
@@ -107,14 +107,14 @@ export class LinkLife {
|
||||
this.release({});
|
||||
}
|
||||
|
||||
/** The attached link is gone. False means there was none to lose. */
|
||||
drop(): boolean {
|
||||
if (!this.isAttached()) return false;
|
||||
/** The attached link is gone: the event that says so, or undefined when there was none to lose. */
|
||||
drop(): 'close' | 'disconnected' | undefined {
|
||||
if (!this.isAttached()) return undefined;
|
||||
|
||||
this.phase = 'down';
|
||||
this.drops++;
|
||||
|
||||
return true;
|
||||
return this.retrying() ? 'disconnected' : 'close';
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
|
||||
@@ -97,7 +97,7 @@ export class OutgoingRequests {
|
||||
if (bindCommands.includes(input.cmdName)) {
|
||||
const shut = this.link.refusal();
|
||||
|
||||
return shut ? { err: shut } : this.requestPastDrainGateAndWindow(input, options);
|
||||
return shut ? { err: shut } : this.requestOnCurrentLink(input, options);
|
||||
}
|
||||
|
||||
const waitForLink = this.link.hold(options.signal);
|
||||
@@ -118,7 +118,7 @@ export class OutgoingRequests {
|
||||
}
|
||||
|
||||
/** Straight onto the current link, for what has to go out either way. */
|
||||
async requestPastDrainGateAndWindow(
|
||||
async requestOnCurrentLink(
|
||||
input: PduObjectInput,
|
||||
options: SendOptions = {},
|
||||
): Promise<Result<{ pduObj: PduObject }>> {
|
||||
|
||||
+5
-5
@@ -237,7 +237,7 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
const drained = await this.drain(undefined);
|
||||
const wasOpen = this.link.isAttached();
|
||||
const sent = wasOpen
|
||||
? await this.outgoing.requestPastDrainGateAndWindow({ cmdName: 'unbind' })
|
||||
? await this.outgoing.requestOnCurrentLink({ cmdName: 'unbind' })
|
||||
: { err: new Error('Session is closed') };
|
||||
const closedOnUnbind = wasOpen && !this.link.isAttached();
|
||||
|
||||
@@ -376,17 +376,17 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
}
|
||||
|
||||
private teardown(): void {
|
||||
// Read once: clear() reports lost segments, and a listener could stop the session between reads.
|
||||
const retrying = this.link.retrying();
|
||||
const lost = this.link.drop();
|
||||
|
||||
if (!this.link.drop()) return;
|
||||
if (!lost) return;
|
||||
|
||||
this.outgoing.linkLost();
|
||||
this.timers.clear();
|
||||
this.incoming.clear();
|
||||
this.sock.destroy();
|
||||
|
||||
if (retrying) this.emit('disconnected');
|
||||
// Not re-read: clear() reports lost segments, and a listener can stop the session in between.
|
||||
if (lost === 'disconnected') this.emit('disconnected');
|
||||
else this.emitClose();
|
||||
}
|
||||
|
||||
|
||||
@@ -1460,19 +1460,20 @@ describe('LinkLife', () => {
|
||||
assert.match(link.refusal()?.message ?? '', /closed/, 'stopped while down');
|
||||
link.end();
|
||||
assert.equal(link.awaitsNextLink(), false, 'ended');
|
||||
assert.equal(new LinkLife({ log: silentLog, reconnects: false, timeout: 100 }).drop(), true);
|
||||
});
|
||||
|
||||
test('drops an attached link once, and counts each drop', () => {
|
||||
test('drops an attached link once, counts each drop, and names the event it warrants', () => {
|
||||
const link = new LinkLife({ log: silentLog, reconnects: true, timeout: 100 });
|
||||
const generation = link.generation();
|
||||
|
||||
assert.equal(link.drop(), true);
|
||||
assert.equal(link.drop(), false, 'already down');
|
||||
assert.equal(link.drop(), 'disconnected');
|
||||
assert.equal(link.drop(), undefined, 'already down');
|
||||
assert.equal(link.generation(), generation + 1);
|
||||
link.attach();
|
||||
assert.equal(link.drop(), true, 'a new link drops again');
|
||||
link.stop();
|
||||
assert.equal(link.drop(), 'close', 'a new link drops again, with none to follow it');
|
||||
assert.equal(link.generation(), generation + 2);
|
||||
assert.equal(new LinkLife({ log: silentLog, reconnects: false, timeout: 100 }).drop(), 'close');
|
||||
});
|
||||
|
||||
test('releases a held request with the reason once the link ends', async () => {
|
||||
|
||||
@@ -201,13 +201,22 @@ next work ([decision](docs/decisions.md#internals-and-tests)).
|
||||
|
||||
A second four-seat run on 2026-09-28, after #35–#40, read 6, 6, 7 and 6 again, Locality 5, 5, 6
|
||||
and 5. Every seat ranked the session's lifecycle hardest and least wanted to modify it. A third,
|
||||
after #46, read 6, 6, 7 and 7, Locality 5, 5, 6 and 6; the link's liveness now has one owner.
|
||||
after #46, read 6, 6, 7 and 7, Locality 5, 5, 6 and 6. A fourth, after the link's liveness got one
|
||||
owner in #48, read 6, 6, 6 and 6, Locality 5 from every seat: all four still ranked `Session.teardown()`
|
||||
hardest, and the held-message flow across `incoming-requests.ts`, `held-messages.ts`, `sms.ts` and
|
||||
`Session`'s rejection handler second.
|
||||
|
||||
- [ ] **Lift Locality to 7, and confirm it with a scoring run.** A run reading 7.0 or above also
|
||||
retires the #30 and #46 decision.
|
||||
|
||||
### Correctness
|
||||
|
||||
- [ ] **Register a multipart send's receipt merge before its segments go out.** `Session.sendSms()`
|
||||
calls `dlrMerger.expect()` only once `submitSms()` resolves, after the last segment's response,
|
||||
so a receipt for an early segment that arrives first is logged at `debug` as naming no merge,
|
||||
and the group then waits out `dlrMergeTimeout` with no `messageDlr`. Likeliest with a fast SMSC
|
||||
or more segments than `maxOutstanding`. Goal 2. From the 2026-09-28 scoring run on #48.
|
||||
|
||||
- [ ] **Settle what a repeated tag not marked `multiple` reads as, and pin it in a test.** A vendor
|
||||
tag or a known single-value tag a peer sends twice keeps the last occurrence and drops the
|
||||
rest silently, which goal 3 argues against; listing it would change every such tag's shape.
|
||||
|
||||
Reference in New Issue
Block a user