Skip to content

fix(harmony): 修复首次加载标记重复消费导致误触发 rollback#586

Closed
liusanhong wants to merge 0 commit into
reactnativecn:masterfrom
liusanhong:master
Closed

fix(harmony): 修复首次加载标记重复消费导致误触发 rollback#586
liusanhong wants to merge 0 commit into
reactnativecn:masterfrom
liusanhong:master

Conversation

@liusanhong

@liusanhong liusanhong commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
  • 新增 consumeFirstLoadMarker,消费后清除标记,避免同一进程多次解析 bundleURL 误触发 rollback
  • 新增 ignoreRollback 标志位,对齐安卓 UpdateContext.java 同名逻辑
  • resolveLaunch 接入 ignoreRollback 参数,switchVersion 切换版本时重置该标志
  • clearFirstTime 与 persistState 维护 firstLoadMarked 标记同批原子落盘

Committed at: 2026-06-29 18:46
Committed by: liuqiang

Summary by CodeRabbit

  • New Features
    • Enhanced version detection by dynamically retrieving the current persisted native version at runtime, with a safe fallback when native support isn’t available.
  • Bug Fixes
    • Improved update checks, reporting, and diff patch downloads to consistently use the latest current-version value and better handle “already current” cases.
    • Strengthened preference initialization and flushing, including graceful fallback when synchronous flush isn’t supported.
  • Chores
    • Bumped the package version.
    • Updated dependency lock metadata and adjusted ignore rules for a local Harmony artifact.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a runtime getCurrentVersion() path across the native spec, core module, Harmony implementation, and client call sites. Harmony preference access is also updated, and the package version is bumped to 10.42.6.

Changes

Dynamic getCurrentVersion flow

Layer / File(s) Summary
Spec, core, and Harmony implementation
src/NativePushy.ts, src/core.ts, harmony/pushy/src/main/ets/PushyTurboModule.ts
Adds the getCurrentVersion contract, exports a core helper with native fallback, and implements the Harmony TurboModule method from context.
client.ts current-version call sites
src/client.ts
Replaces static currentVersion reads in reporting, update checks, download guards, and patch origin hash selection with getCurrentVersion().
Harmony preference access updates
harmony/pushy/src/main/ets/UpdateContext.ts
Reinitializes preferences before reads and writes, adds guarded cache removal during initialization, and changes flush fallback behavior when flushSync is unavailable.
Package metadata and lockfile
package.json, harmony/pushy/oh-package-lock.json5
Updates the package version and adds the @ohos/hypium@1.0.19 lockfile entry.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I sniff the path, then hop to see
A version fresh in runtime tea.
The markers rest, the reads stay neat,
While client hops on nimble feet.
🥕 Hooray for paths that stay so free!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: preventing repeated first-load marker consumption from incorrectly triggering rollback.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
harmony/pushy/src/main/ets/PushyTurboModule.ts (1)

78-85: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Consume the marker only after fallible constants work succeeds.

Line 78 deletes the first-load marker before clearRollbackMark() runs. If that later state operation throws, getConstants() will not return to JS, but the marker is already gone, so a retry reports isFirstTime: false.

Proposed fix
-    const isFirstTime = this.context.consumeFirstLoadMarker();
     const rolledBackVersion = this.context.rolledBackVersion();
     const uuid = this.context.getKv('uuid');
     const isUsingBundleUrl = this.context.getIsUsingBundleUrl();
 
     if (rolledBackVersion) {
       this.context.clearRollbackMark();
     }
+
+    const isFirstTime = this.context.consumeFirstLoadMarker();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harmony/pushy/src/main/ets/PushyTurboModule.ts` around lines 78 - 85, Move
the first-load marker consumption in PushyTurboModule.getConstants so it happens
only after the fallible state work succeeds, especially after
clearRollbackMark() in the rolledBackVersion branch. Use the existing symbols
consumeFirstLoadMarker(), clearRollbackMark(), and getConstants() to reorder the
logic so a throw leaves the marker intact for retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@harmony/pushy/src/main/ets/UpdateContext.ts`:
- Around line 29-31: Make `ignoreRollback` process-wide instead of being reset
per `UpdateContext` instance; the current `private ignoreRollback = false` in
`UpdateContext` lets a new context in the same process re-enable rollback
checks. Update the `UpdateContext` class so the flag is shared across instances
(or stored on a process-level singleton/static), and ensure `getBundleUrl()` and
the rollback path around `shouldRollback`/`firstTime` consult and update that
shared state consistently so a second resolution in the same process cannot
trigger rollback again.

---

Outside diff comments:
In `@harmony/pushy/src/main/ets/PushyTurboModule.ts`:
- Around line 78-85: Move the first-load marker consumption in
PushyTurboModule.getConstants so it happens only after the fallible state work
succeeds, especially after clearRollbackMark() in the rolledBackVersion branch.
Use the existing symbols consumeFirstLoadMarker(), clearRollbackMark(), and
getConstants() to reorder the logic so a throw leaves the marker intact for
retry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1944a829-970a-4b7c-aec7-56fad44cedaa

📥 Commits

Reviewing files that changed from the base of the PR and between 38717fa and 9e76322.

📒 Files selected for processing (2)
  • harmony/pushy/src/main/ets/PushyTurboModule.ts
  • harmony/pushy/src/main/ets/UpdateContext.ts

Comment thread harmony/pushy/src/main/ets/UpdateContext.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/client.ts (1)

478-479: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid calling getCurrentVersion() twice for the same check.

getCurrentVersion() is a synchronous native read; invoking it on both the comparison and the log line doubles the bridge call and risks the two reads diverging. Cache it once.

♻️ Proposed refactor
-    if (hash === getCurrentVersion()) {
-      log(`current hash ${getCurrentVersion()}, ignored`);
+    const current = getCurrentVersion();
+    if (hash === current) {
+      log(`current hash ${current}, ignored`);
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.ts` around lines 478 - 479, The version check in the `hash ===
getCurrentVersion()` path calls `getCurrentVersion()` twice, which repeats the
native read and can produce inconsistent values. Cache the result in a local
variable before the comparison in `src/client.ts`, then reuse that same value in
both the `if` condition and the `log` message so the check and output are based
on one read.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/client.ts`:
- Around line 478-479: The version check in the `hash === getCurrentVersion()`
path calls `getCurrentVersion()` twice, which repeats the native read and can
produce inconsistent values. Cache the result in a local variable before the
comparison in `src/client.ts`, then reuse that same value in both the `if`
condition and the `log` message so the check and output are based on one read.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61d1ff0e-b331-4a5b-93e1-7864d825eded

📥 Commits

Reviewing files that changed from the base of the PR and between 9e76322 and 3bd7d30.

📒 Files selected for processing (5)
  • harmony/pushy/src/main/ets/PushyTurboModule.ts
  • package.json
  • src/NativePushy.ts
  • src/client.ts
  • src/core.ts
✅ Files skipped from review due to trivial changes (1)
  • package.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.gitignore:
- Line 64: The commented-out ignore rule for harmony/pushy.har appears to be
unintentional and would allow a generated artifact to be tracked. Re-enable the
existing .gitignore entry unless there is a deliberate decision to version this
file, and if tracking is intended, confirm that the artifact is stable and
documented in the PR description. Use the harmony/pushy.har ignore rule in
.gitignore as the target to locate and restore.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf4b8b73-a583-49f2-bec7-718f69de6c0a

📥 Commits

Reviewing files that changed from the base of the PR and between 3bd7d30 and d473d2b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • .gitignore
  • harmony/pushy.har

Comment thread .gitignore Outdated

harmony/package
harmony/pushy.har
#harmony/pushy.har

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Verify this .gitignore change is intentional.

This change disables the ignore rule for harmony/pushy.har, but the PR objectives and summary don't mention tracking this artifact. HAR files are typically build outputs (like Android AARs). If this was commented out temporarily or unintentionally, re-enable the rule to prevent accidentally committing large binaries.

If the intent is to track a stable release artifact, document this in the PR description and ensure the file is meant to be version-controlled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.gitignore at line 64, The commented-out ignore rule for harmony/pushy.har
appears to be unintentional and would allow a generated artifact to be tracked.
Re-enable the existing .gitignore entry unless there is a deliberate decision to
version this file, and if tracking is intended, confirm that the artifact is
stable and documented in the PR description. Use the harmony/pushy.har ignore
rule in .gitignore as the target to locate and restore.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@harmony/pushy/src/main/ets/UpdateContext.ts`:
- Around line 157-160: The fallback in UpdateContext’s persistence path
incorrectly calls Preferences.flush() without awaiting it, so the save can
complete before the data is durable and rejected Promises bypass the existing
try/catch. Remove the flush() fallback from the code path in
persistState()/consumeFirstLoadMarker() handling and keep using the synchronous
write method already used for preferences updates, so firstLoadMarked and
currentVersion are only reported after the state is actually written.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f3b7e29-9538-48ba-a67d-8fd4f0079712

📥 Commits

Reviewing files that changed from the base of the PR and between d474911 and 8b1914f.

📒 Files selected for processing (4)
  • harmony/pushy/oh-package-lock.json5
  • harmony/pushy/src/main/ets/UpdateContext.ts
  • src/NativePushy.ts
  • src/core.ts
✅ Files skipped from review due to trivial changes (1)
  • harmony/pushy/oh-package-lock.json5
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core.ts
  • src/NativePushy.ts

Comment on lines +157 to +160
if (typeof this.preferences.flush === 'function') {
this.preferences.flush();
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In HarmonyOS / OpenHarmony Preferences API, what are the return type and persistence semantics of Preferences.flush()versusPreferences.flushSync()?

💡 Result:

In the HarmonyOS and OpenHarmony Preferences API, both flush and flushSync are used to persist cached data to a persistent file [1][2][3]. If no data has been modified or the new data matches the cached data, the system will not update the file [1][4][5]. The return types and execution semantics are as follows: flush Return Type: This method returns a Promise [1][4][5]. An alternative overload, flush(callback: AsyncCallback), is also available for callback-based asynchronous handling [1][2][6]. Persistence Semantics: It performs the operation asynchronously [1][2][3]. The data cached in memory is flushed to the persistent storage file without blocking the calling thread [1][7]. flushSync Return Type: This method returns void [1][4][5]. Persistence Semantics: It performs the operation synchronously [1][5]. The cached data is flushed to the persistent file immediately and directly within the calling thread [7]. Because this operation is synchronous, it is recommended to avoid calling it on the main (UI) thread to prevent potential performance degradation or UI lag [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="harmony/pushy/src/main/ets/UpdateContext.ts"
nl -ba "$FILE" | sed -n '1,240p'

Repository: reactnativecn/react-native-update

Length of output: 211


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="harmony/pushy/src/main/ets/UpdateContext.ts"

echo "== around flushPreferences =="
sed -n '130,190p' "$FILE" | cat -n

echo
echo "== callers of flushPreferences/persistState/consumeFirstLoadMarker =="
rg -n "flushPreferences|persistState\\(|consumeFirstLoadMarker\\(|getConstants\\(|getBundleUrl\\(" "$FILE" "harmony/pushy/src/main/ets/PushyTurboModule.ts"

Repository: reactnativecn/react-native-update

Length of output: 3529


Don't fall back to flush() here. Preferences.flush() is async, so this returns before the write is durable, and the try/catch won't catch a rejected Promise. That can let persistState() / consumeFirstLoadMarker() report success while firstLoadMarked / currentVersion are still stale on the next launch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harmony/pushy/src/main/ets/UpdateContext.ts` around lines 157 - 160, The
fallback in UpdateContext’s persistence path incorrectly calls
Preferences.flush() without awaiting it, so the save can complete before the
data is durable and rejected Promises bypass the existing try/catch. Remove the
flush() fallback from the code path in persistState()/consumeFirstLoadMarker()
handling and keep using the synchronous write method already used for
preferences updates, so firstLoadMarked and currentVersion are only reported
after the state is actually written.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant