You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: auth/profiles.mdx
+64-2Lines changed: 64 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -127,6 +127,68 @@ _ = kernelBrowser
127
127
```
128
128
</CodeGroup>
129
129
130
+
### Prevent concurrent profile writes
131
+
132
+
Saving replaces the profile's entire stored browser state; it doesn't merge cookies or local storage from multiple sessions. If more than one browser uses the same profile with `save_changes: true`, the browser that ends last overwrites the profile with its state.
133
+
134
+
Before starting a writer, call `GET /browsers?status=active&query=<profileId>` and check `profile_save_changes` to find active sessions that can save to that profile. The `query` parameter can match fields other than the profile ID, so filter the results by the exact `profile.id` too.
135
+
136
+
<CodeGroup>
137
+
```typescript TypeScript
138
+
const profileId =kernelBrowser.profile!.id;
139
+
const activeWriters = [];
140
+
141
+
forawait (const browser ofkernel.browsers.list({
142
+
status: 'active',
143
+
query: profileId,
144
+
})) {
145
+
if (browser.profile?.id===profileId&&browser.profile_save_changes) {
146
+
activeWriters.push(browser);
147
+
}
148
+
}
149
+
150
+
if (activeWriters.length>0) {
151
+
thrownewError(`Profile already has an active writer: ${activeWriters[0].session_id}`);
152
+
}
153
+
```
154
+
155
+
```python Python
156
+
profile_id = kernel_browser.profile.id
157
+
active_writers = [
158
+
browser
159
+
for browser in kernel.browsers.list(status="active", query=profile_id)
160
+
if browser.profile
161
+
and browser.profile.id == profile_id
162
+
and browser.profile_save_changes
163
+
]
164
+
165
+
if active_writers:
166
+
raiseRuntimeError(
167
+
f"Profile already has an active writer: {active_writers[0].session_id}"
if browser.Profile.ID == profileID && browser.ProfileSaveChanges {
181
+
panic(fmt.Sprintf("profile already has an active writer: %s", browser.SessionID))
182
+
}
183
+
}
184
+
iferr:= pager.Err(); err != nil {
185
+
panic(err)
186
+
}
187
+
```
188
+
</CodeGroup>
189
+
190
+
This check and browser creation are separate requests. If multiple workers can start sessions concurrently, use your own lock or lease around both operations so two workers can't pass the check at the same time.
191
+
130
192
## 3. Use the browser, then close it to persist the state
131
193
132
194
After using a browser with `save_changes: true`, closing the browser will save cookies and local storage into the profile.
@@ -513,6 +575,6 @@ _ = browser
513
575
- A profile's `name` must be unique within your [project](/info/projects). The same name can be reused across different projects in the same org.
514
576
- Profiles store cookies and local storage. Start the session with `save_changes: true` to write changes back when the browser is closed.
515
577
- To keep a profile immutable for a run, omit `save_changes` (default) when creating the browser.
516
-
- Multiple browsers in parallel can use the same profile, but only one browser should write (`save_changes: true`) to it at a time. Parallel browsers with `save_changes: true` may cause profile corruption and unpredictable behavior.
517
-
-`save_changes` applies to a profile attached to a single browser — either at creation (`kernel.browsers.create()`) or loaded afterward with `kernel.browsers.update()`. A profile set on a [browser pool's](/browsers/pools) config is loaded read-only and never persisted; `save_changes` sent on a pool's profile is silently ignored. To persist per-user state through a pool, attach the profile after acquiring the browser and release with `reuse: false` — see [Per-user profiles with pools](/browsers/pools#per-user-profiles-with-browser-pools).
578
+
- Multiple browsers in parallel can use the same profile, but only one browser can safely write (`save_changes: true`) to it at a time. Each save overwrites the whole profile, so the browser that ends last wins.
579
+
-`save_changes` applies to a profile attached to a single browser — either at creation (`kernel.browsers.create()`) or loaded afterward with `kernel.browsers.update()`. A profile set on a [browser pool's](/browsers/pools) config is loaded read-only and never persisted; `save_changes` sent on a pool's profile is silently ignored. To persist per-user state through a pool, attach the profile with `save_changes: true`after acquiring the browser and release with `reuse: false` — see [Per-user profiles with pools](/browsers/pools#per-user-profiles-with-browser-pools).
518
580
- Profile data is encrypted end to end using a per-organization key.
Copy file name to clipboardExpand all lines: browsers/pools.mdx
+63-2Lines changed: 63 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ A few constraints to weigh before moving a workload onto a browser pool:
29
29
30
30
-**No GPU browsers.** GPU-accelerated browsers are on-demand only. Use `browsers.create()` for WebGL, video, or canvas-heavy work.
31
31
-**One fixed configuration per browser pool**, with `start_url` the only setting you can override per acquisition — see [Create a browser pool](#create-a-browser-pool).
32
-
-**Profiles load read-only**, and a browser pool holds one at a time — see [Profiles with browser pools](#profiles-with-browser-pools) for how to persist state per user.
32
+
-**A profile set on the browser pool loads read-only**, and a browser pool holds one at a time — see [Per-user profiles with browser pools](#per-user-profiles-with-browser-pools) for how to persist state per user.
33
33
-**Browser pool capacity counts against your [concurrency limit](/info/pricing#concurrency-limits)** whether or not its browsers are acquired, though idle pooled browsers aren't billed.
34
34
-**Plan-gated.** Browser pools are available on the Start-Up and Enterprise plans.
35
35
@@ -208,7 +208,68 @@ A profile attached to the pool is loaded **read-only**. Every browser in the poo
208
208
209
209
### Per-user profiles with browser pools
210
210
211
-
Because that profile is shared and read-only, it can't hold per-user login state for many users at once. To serve many users from one browser pool, create it with no profile — stealth, proxies, extensions, and viewport still live on the pool — then attach each user's profile to the browser *after* you acquire it, and release with `reuse: false` so the browser is destroyed. Destroying it both persists that user's profile changes and keeps their state from reaching the next acquirer.
211
+
Because that profile is shared and read-only, it can't hold per-user login state for many users at once. To serve many users from one browser pool, create it with no profile — stealth, proxies, extensions, and viewport still live on the pool — then attach each user's profile to the browser *after* you acquire it, and release with `reuse: false` so the browser is destroyed. Destroying it keeps that user's state from reaching the next acquirer.
212
+
213
+
The read-only rule covers the pool's own profile, not one attached after acquiring: that profile belongs to the browser, so `save_changes` applies as it does on any other browser. Pass `save_changes: true` when you attach it — it defaults to `false`, and without it the browser is destroyed on release without writing the user's session back.
0 commit comments