Skip to content

fix(desktop): claim the instance before application startup - #2875

Merged
openai0229 merged 8 commits into
mainfrom
fix/desktop-single-instance-startup
Sep 7, 2026
Merged

openai0229 merged 8 commits into
mainfrom
fix/desktop-single-instance-startup

Conversation

@openai0229

@openai0229 openai0229 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Related issue

N/A - maintainer-requested improvement to Community desktop instance lifecycle.

Summary

Community currently checks for an existing instance while starting the window, after configuration and encryption initialization. Duplicate launches without arguments do not activate the existing window, and simultaneous file-open requests can overwrite the shared IPC file. Claim the instance at application entry and hand each launch request to the owner through an acknowledged local connection.

Keep the existing whole-file app.lock until process exit, including shutdown hooks. A launch lock serializes receiver initialization and endpoint publication. The owner listens on a random port bound only to 127.0.0.1; its endpoint contains the owner PID, process start time, and a random token. A duplicate sends one length-prefixed file/protocol argument or an empty activation request and exits only after the owner confirms receipt into its memory queue. Receiver operation is independent of frontend readiness or blocked window dispatch. Registration has one two-minute budget including launch-lock acquisition; each received frame has a two-second total budget, and forwarding has a five-second limit within the remaining registration budget. Ambiguous writes are never retried automatically.

Instance admission belongs to the application entry point; the window no longer registers the instance again. A received request remains owned until its window callback finishes on the Swing EDT. Exit confirmation is rejected if a launch arrived during confirmation or a window handoff remains pending. Initial startup can still be cancelled before readiness when no forwarded request is waiting. Once exit starts, new launchers wait for the old instance lock to be released before initializing services. Failed or rejected exit actions restore admission and dispatch, including late readiness and unread legacy requests. Invalid launch arguments retain the existing logged error outcome and cannot permanently block later launches or exit. CLI, headless services, and macOS native activation keep their existing runtime paths.

Legacy app.ipc file/protocol forwarding remains available for older binaries. Its reader retains timestamp-collision handling and retries transient failures independently of new requests. All current-version launchers use the acknowledged connection, so simultaneous requests and identical repeated arguments do not overwrite or coalesce.

Affected surfaces

  • Frontend / Web
  • Backend / API / Storage
  • Database plugin / Driver
  • JCEF / Desktop packaging
  • CI / Build / Release
  • Documentation only

Verification

  • Commands and results:
    • Full tools/JCEF suites ran in the local reactor package build with -Dmaven.test.skip=false -DskipTests=false -Dmaven.test.failure.ignore=false and explicit ai.chat2db.community.jcef.**.*Test / ai.chat2db.community.tools.**.*Test selectors: 176 tests, 175 passed, 1 environment-dependent terminal test skipped; affected modules packaged successfully.
    • SingleInstanceUtilTest: all 33 tests passed using separate JVMs, real OS locks and sockets. Coverage includes concurrent and repeated requests; pre-readiness and EDT-pending acknowledged requests during exit; stale close/restart/update confirmations; failed-exit and late-readiness recovery; cancellation of initial startup; real window-handler ordering with native operations replaced; unsupported/internal restart URIs; lock timeout/interruption; overlapping slow authentication/payload frames and healthy senders; crash recovery; and legacy compatibility. Test JVMs use isolated home directories and are joined before cleanup.
    • Independent transport probes: default lock timeout at 120009 ms, stalled ACK at 5042 ms, shared 2000 ms budget after 1300 ms of lock waiting at 2004 ms, blocked socket write at 5051 ms, and slow token/body cutoff at 2006/2010 ms with a successful overlapping launcher. Process-generation and publication recovery probe: 10 assertions passed.
    • mvn -B -o -f chat2db-community-server/pom.xml -pl :chat2db-community-start -am -Dmaven.test.skip=true -Dchat2db.finalName=chat2db-community package: executable backend packaged with tests skipped for this command.
    • git diff --check and workflow YAML parsing passed.
  • Manual verification: Windows native EXE/window focus and installer upgrade are not manually tested on this macOS host. The PR keeps the repository's existing CI configuration; the cross-process test class can also be run directly on Windows and Linux.
  • UI evidence: N/A - frontend files are unchanged; window activation is handled by Swing.

Risk and compatibility

  • Public API or stored data: no business API/data format changes. app.lock remains authoritative; app.launch.lock coordinates launch initialization, and app.ipc.endpoint stores process-bound connection metadata. These files live in the existing per-user state directory. Exit actions must report whether exit started so failed attempts can restore admission.
  • Database or driver compatibility: N/A.
  • Network, privacy, or security: the IPC listener uses a random loopback-only port, checks a random per-process token, bounds request size, and enforces a total frame deadline. Existing application/MCP ports are unchanged. No customer logs or data are included. Receipt ACK confirms ownership by the live process; it is not a renderer completion ACK or crash-durable storage.
  • Community / Local / Pro boundary: Community instance handling uses the existing runtime identity and state directory. No product-specific logic is introduced.
  • Backward compatibility: old and new binaries share the same whole-file lock. Legacy binaries keep their original single-file forwarding behavior; updated launchers communicate with updated owners through independent acknowledged connections. Application entry points own instance admission before window initialization.

Reviewer map

  • Start here: SingleInstanceUtil.registerInstance, forward, receive, dispatch, and guardExit; then application entry, frontend readiness binding, MainJFrame.handleLaunchRequest, and ApplicationExitCoordinator.
  • Failure condition: a second process must never initialize application services while the first holds the instance lock, including during shutdown.
  • Rollback or disable path: revert this change; do not delete an active instance's lock file.

Contributor declaration

  • I linked the Issue that defines this change. N/A - report supplied directly by maintainer.
  • I tested the affected behavior and reported the actual results above.
  • I did not include credentials, private data, or generated build output.
  • I disclosed substantial AI assistance below, or this PR contains no substantial AI-generated code.

AI assistance: Codex implemented and tested this change following the maintainer's diagnosis and requested design.

@openai0229
openai0229 requested a review from a team as a code owner September 7, 2026 03:45
@openai0229 openai0229 moved this to In Review in Chat2DB Community Sep 7, 2026
@openai0229
openai0229 merged commit 934aa21 into main Sep 7, 2026
15 of 16 checks passed
@openai0229
openai0229 deleted the fix/desktop-single-instance-startup branch September 7, 2026 12:48
@openai0229 openai0229 moved this from In Review to Done in Chat2DB Community Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant