Skip to content

Fix retained Life360 request tasks - #102

Open
Mason10198 wants to merge 1 commit into
pnbruckner:masterfrom
Mason10198:fix/retained-request-tasks
Open

Fix retained Life360 request tasks#102
Mason10198 wants to merge 1 commit into
pnbruckner:masterfrom
Mason10198:fix/retained-request-tasks

Conversation

@Mason10198

Copy link
Copy Markdown

Fixes #101.

What changed

Python 3.14 has an upstream asyncio.wait(..., FIRST_COMPLETED) retention bug when many requests repeatedly wait against the same long-lived task: python/cpython#152569

Life360 triggered that behavior by racing every client request against one account-lifetime failure task. This change keeps the shared failure event but replaces the shared task with a bounded watcher for each active request. The watcher is canceled and awaited when its request finishes.

The cancellation behavior is preserved when:

  • another request disables the account;
  • account failure and external cancellation happen together;
  • the underlying request catches CancelledError and returns normally.

Tests

  • Added a regression test proving completed outer request tasks, inner request tasks, and failure watchers are released.
  • Added tests for account-failure cancellation and external-cancellation races.
  • Verified the retention test fails with the original implementation and passes with this change.
  • Full suite on Python 3.14.5: 18 passed, 2 xfailed.
  • Full suite on Python 3.13.11: 18 passed, 2 xfailed.

I also tested the patch on Home Assistant Core 2026.8.3 with Life360 0.10.2. The original code retained 924 completed Life360 _request() tasks after about 5.5 minutes. With this patch, the same profiler check after six minutes found zero retained Life360 request tasks; all 10 entities remained available and memory stayed between 971.7 and 976.8 MiB with no swap use.

@megamilam

Copy link
Copy Markdown

Confirming this fix on a second platform. Thanks for running down the upstream CPython cause and writing the workaround — I could report the symptom, but the actual diagnosis and fix are yours.

Environment: Home Assistant Green (aarch64), HA Core 2026.8.3, Python 3.14.6, Life360 0.10.2, one account, five trackers, 3.83 GB RAM. Same Python 3.14 runtime implicated in python/cpython#152569.

Before the patch

profiler.dump_log_objects with type: Task:

  • 1,037 live Task objects, 1,033 of them completed CirclesMembersDataUpdateCoordinator._request() at coordinator.py:429, still holding their member result payloads.

profiler.start_log_objects at 300 s, two consecutive samples:

Task        +360    +362
coroutine   +350    +369
Context     +488    +250
dict     +14,061 +15,023

HA Core RSS went 467 MB → 2,536 MB in roughly nine hours (~65 MB/hour).

After applying this PR

Swapped only custom_components/life360/coordinator.py from this branch. Verified first that coordinator.py at tag 0.10.2 is byte-identical to master, so nothing unrelated came along with it.

  • Integration loads cleanly; all five trackers available and updating.

  • profiler.start_log_objects at 300 s, two consecutive post-startup samples:

    Task absent from the growth list
    coroutine absent from the growth list
    Context absent +50
    dict +415 +922

  • HA Core RSS held between 775 MB and 781 MB across 23 minutes. The same window before the patch would have added ~25 MB.

The residual dict growth and the SQLAlchemy objects in the second sample look like ordinary recorder churn on a freshly restarted instance, not retention.

Note on severity

Worth flagging given the hardware difference: your LXC has 4 GiB and took ~57 hours to exhaust. A Home Assistant Green has 3.83 GB usable, no upgrade path, and reached the ceiling in about a day — after which the kernel SIGKILLs whatever container is largest. On this instance that repeatedly killed an unrelated add-on with no obvious connection to Life360, which is what made it hard to attribute in the first place.

The Green is the appliance Home Assistant sells to non-technical users, so this likely bites hardest on the least-diagnosable setups.

Follow-up

This instance is now running patched under normal household load. I'll post a 24-hour soak result tomorrow — neither of our tests so far has run long enough to show durability, only that retention stops.

@megamilam

Copy link
Copy Markdown

31-hour soak result

Following up on my earlier confirmation. Same instance — Home Assistant Green (aarch64), HA Core 2026.8.3, Python 3.14.6, Life360 0.10.2 with this PR's coordinator.py, five trackers.

No restarts, no intervention, normal household load for 31 hours.

Task retention

profiler.start_log_objects at 300 s, run fresh at the 31-hour mark:

Task         96 total,  +1 over 5 minutes
coroutine    absent from the growth list
Context      absent from the growth list
dict         absent from the growth list

For comparison, the same instance before the patch held 246,124 live Tasks and was adding ~360 every 5 minutes. 96 total is normal for this instance.

Memory

Aug 31  00:41   HA Core RSS   775 MB   (just after patch + restart)
Aug 31  01:04                 781 MB
Sep  1  07:45                 968 MB   (31 hours later)

That is ~6 MB/hour, against ~65 MB/hour before the patch — roughly an 11× reduction.

The residual is not Life360. The only things growing in the profiler samples are SelectedOption, BaseZone, Zone, ZoneStatus, StandardProgram and similar — my Hydrawise irrigation integration polling — plus ordinary recorder and Bluetooth churn. Totals stay in the hundreds rather than accumulating, so it reads as churn rather than a second leak.

Practical outcome

I had a memory-guard automation in place that restarts HA Core when RAM crosses 85%, because before the patch this box was hitting the ceiling roughly daily and the kernel would SIGKILL whatever container was largest.

That automation has not fired once in 31 hours. Before the patch it would have fired at least once. Longest uninterrupted uptime this instance has had in weeks.

Happy to keep it running and report again at a week if that is useful.

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.

Memory leak: completed _request() tasks retained indefinitely (~1.2/sec, ~2 GB/day)

2 participants