Skip to content

ndb: replace force unwraps with safe optionals in NdbTxn#3562

Closed
alltheseas wants to merge 1 commit intodamus-io:masterfrom
alltheseas:fix/ndbtxn-force-unwraps
Closed

ndb: replace force unwraps with safe optionals in NdbTxn#3562
alltheseas wants to merge 1 commit intodamus-io:masterfrom
alltheseas:fix/ndbtxn-force-unwraps

Conversation

@alltheseas
Copy link
Collaborator

@alltheseas alltheseas commented Feb 1, 2026

Summary

Replace force unwraps (as!) with safe optional binding (as?) in NdbTxn and SafeNdbTxn transaction inheritance logic. This prevents crashes when thread-local storage is in an unexpected state.

Changes:

  • Move ref_count check into the if let conditional binding
  • Use already-bound txn_generation instead of re-reading with force unwrap
  • Simplify ref count increment to single expression

The fix ensures transaction inheritance gracefully falls back to creating a new transaction if thread-local state is incomplete, rather than crashing on missing values.

Checklist

Standard PR Checklist

  • I have read (or I am familiar with) the Contribution Guidelines
  • I have tested the changes in this PR
  • I have profiled the changes to ensure there are no performance regressions, or I do not need to profile the changes.
    • If not needed, provide reason: Changes only affect transaction initialization logic, no performance impact.
  • I have opened or referred to an existing github issue related to this change.
    • Related to crash analysis from TestFlight v1.16 (Build 1277)
  • My PR is either small, or I have split it into smaller logical commits that are easier to review
  • I have added the signoff line to all my commits. See Signing off your work
  • I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
  • I have added appropriate Closes: or Fixes: tags in the commit messages wherever applicable, or made sure those are not needed. See Submitting patches

Test report

Device: macOS Simulator (iPhone 17 Pro)

iOS: 18.0

Damus: master + this PR

Setup: Unit test suite via xcodebuild test

Steps:

  1. Run xcodebuild test -project damus.xcodeproj -scheme damus -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -only-testing:damusTests/NdbTests
  2. Verify test_inherited_transactions passes - confirms transaction inheritance with safe optional binding works

Results:

  • PASS
  • Executed 14 tests, with 0 failures (0 unexpected) in 1.415 seconds
  • All existing tests continue to pass

Testing directions for reviewers

  1. Unit tests: Run NdbTests target - test_inherited_transactions specifically tests the transaction inheritance path
  2. Code review: Verify the force unwraps at lines 47-48 (old) are replaced with safe optional binding in the if let chain

Other notes

The original code had:

if let active_txn = Thread.current.threadDictionary["ndb_txn"] as? ndb_txn,
   let txn_generation = Thread.current.threadDictionary["txn_generation"] as? Int,
   txn_generation == ndb.generation
{
    self.generation = Thread.current.threadDictionary["txn_generation"] as! Int  // Force unwrap!
    let ref_count = Thread.current.threadDictionary["ndb_txn_ref_count"] as! Int  // Force unwrap!
    ...
}

The fix moves ref_count into the conditional binding and reuses the already-bound txn_generation:

if let active_txn = Thread.current.threadDictionary["ndb_txn"] as? ndb_txn,
   let txn_generation = Thread.current.threadDictionary["txn_generation"] as? Int,
   let ref_count = Thread.current.threadDictionary["ndb_txn_ref_count"] as? Int,
   txn_generation == ndb.generation
{
    self.generation = txn_generation  // Already bound, no force unwrap
    Thread.current.threadDictionary["ndb_txn_ref_count"] = ref_count + 1
    ...
}

Signed-off-by: alltheseas

Summary by CodeRabbit

  • Refactor
    • Enhanced internal transaction handling stability with improved error-handling patterns.

✏️ Tip: You can customize this high-level summary in your review settings.

Replace force unwraps (`as!`) with safe optional binding (`as?`) in
NdbTxn and SafeNdbTxn transaction inheritance logic. This prevents
crashes when thread-local storage is in an unexpected state.

Changes:
- Move `ref_count` check into the `if let` conditional binding
- Use already-bound `txn_generation` instead of re-reading with force unwrap
- Simplify ref count increment to single expression

The fix ensures transaction inheritance gracefully falls back to
creating a new transaction if thread-local state is incomplete,
rather than crashing on missing values.

Changelog-Fixed: Fixed potential crash in NdbTxn when thread-local storage is incomplete

Signed-off-by: alltheseas
@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Walkthrough

Changes to transaction initialization in NdbTxn.swift replace force-unwrapping with optional binding for ref_count retrieval and align generation assignment across both NdbTxn.init and SafeNdbTxn.new, maintaining existing control flow while improving safety.

Changes

Cohort / File(s) Summary
Transaction Initialization Safety
nostrdb/NdbTxn.swift
Replaced force-unwrapping with optional binding for ref_count; uses txn_generation from outer scope for generation assignment. Pattern applied consistently in both NdbTxn.init and SafeNdbTxn.new. Ref_count incrementation and storage consolidated. No API changes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Poem

🐰 With optional binding bright,
We banish force-unwrap's plight,
Safe transactions now take flight,
Generation flows just right! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: replacing force unwraps with safe optional binding in NdbTxn, which is the primary focus of the changeset.
Description check ✅ Passed The PR description is comprehensive and follows the template structure, including a clear summary, completed standard PR checklist with justifications, detailed test report with steps and results, and thorough technical explanation with code examples.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alltheseas alltheseas requested review from danieldaquino and jb55 and removed request for jb55 February 2, 2026 14:40
@danieldaquino danieldaquino added the pr-short Pull request with less than 100 lines of code changed label Feb 7, 2026
@alltheseas
Copy link
Collaborator Author

Closing in favor of consolidated Linux-style topic branches. This work will be audited against AGENTS.md requirements and cherry-picked into ordered topic branches. See #3624 for tracking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-short Pull request with less than 100 lines of code changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants