mbuzz 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81970794a962b276527d5aa05efb59703e1e3507598218c66f3eb3c238a96b6a
4
- data.tar.gz: 391e3993a7aa60f3ad5fe50dfeff1394c9f3282538d3074de12f7a7a284d8e15
3
+ metadata.gz: 03aa54a68ab1b6e6313d69c910c88aec7241e19d65b72b9cee37a6e187454692
4
+ data.tar.gz: 3ddce95a2f8795635278785f403daf6a41e15bc31fc01c3990989c20911ebc21
5
5
  SHA512:
6
- metadata.gz: 2187c1959502e1dcc183e6f3fd2d77b46b90e87427bb79078c04cac35715891ec89cf274514cf8e8f327de2d8176e0d381875c961b9f61b37fee18f313c8ff9e
7
- data.tar.gz: eb34d5989f34c7e83d5315c57000dff733af12a26494255817a414002d0448a1d8ee8f71143a94348f1b5118df743acf97cdeffd2347584e0cbe561b73b396e3
6
+ metadata.gz: 4ba8606ecbeec460a9145937b8d123c93c455476d92795e9ddf2b291082e57cdce4818cd709a427ed28d125e0ee6766110f0425039c43bfba75328f5da6f756f
7
+ data.tar.gz: 8d9233587278aaccb19fd5f124e486e1385b626b2eb4db3d7125ab0e64d6b1500117a33fe89e1fad84c846e6a292e6183760013ecb33e16d44082036d2c5a299
data/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.10.0] - 2026-09-08
9
+
10
+ ### BREAKING — the page snippet is now required
11
+
12
+ - **A page response no longer sets the visitor cookie.** Only `POST /_mbuzz/session` mints, and the inline snippet in the README's "Full-page caching" section is what calls it. **Upgrading without adding that snippet stops tracking entirely** — no cookie is ever minted, so no visitor exists and every event is dropped.
13
+
14
+ ### Fixed
15
+
16
+ - **A cached page no longer hands every visitor the same id.** The page response carried a `Set-Cookie`, and a full-page cache stored it and replayed it to everyone — so unrelated people merged into a single journey. Corruption rather than loss: every row still exists, each attributed to the wrong person, and nothing looks missing. The page path now uses only the cookie the browser already holds; the uncached session endpoint is the sole place a visitor is minted.
17
+
18
+ Found by the cache harness at `sdk_integration_tests/scenarios/page_cache_test.rb`, and it could not have been found any other way: in isolation "reuse the cookie the browser presents" is correct — it is what lets a returning visitor keep their id. The bug exists only once a cache has handed one cookie to many people. The WordPress plugin reached the same conclusion first (`CookieBootstrap::CONTEXT_PAGE`).
19
+
8
20
  ## [0.9.0] - 2026-09-02
9
21
 
10
22
  ### Added
data/README.md CHANGED
@@ -193,16 +193,22 @@ use Mbuzz::Middleware::Tracking
193
193
  run MyApp
194
194
  ```
195
195
 
196
- ## Full-page caching
196
+ ## Full-page caching — the snippet below is REQUIRED
197
197
 
198
- If pages are served from a full-page cache (Cloudflare, Varnish, nginx, Rack::Cache, a CDN),
199
- the cache answers the request **without entering the Rack stack**, so the tracking middleware
200
- never runs, no visitor cookie is set, and every later event is dropped for having no one to
201
- attribute it to. The page renders perfectly and nothing is logged — the failure is silent.
198
+ **Add this to your layout or nothing is tracked.** Since 0.10.0 a page response never sets the
199
+ visitor cookie: only `POST /_mbuzz/session` mints, and the snippet is what calls it.
202
200
 
203
- `Mbuzz::Middleware::SessionEndpoint` fixes this. It answers `POST /_mbuzz/session`, a path
204
- caches don't store, and the **server** sets the cookie on that response. Rails mounts it for
205
- you; Rack and Sinatra apps add it ahead of `Tracking` as shown above.
201
+ A page response can be stored by a full-page cache (Cloudflare, Varnish, nginx, Rack::Cache, a
202
+ CDN) and replayed to every visitor. A `Set-Cookie` sitting in that cache hands everyone the
203
+ *first* visitor's id, so unrelated people merge into one journey corruption rather than loss,
204
+ since every row exists and is simply attributed to the wrong person. Minting only on a response
205
+ no cache stores is the only way to prevent it.
206
+
207
+ The same endpoint solves the original problem too: a cached page never enters the Rack stack,
208
+ so the tracking middleware cannot run — but this one request always reaches it.
209
+
210
+ `Mbuzz::Middleware::SessionEndpoint` answers `POST /_mbuzz/session`. Rails mounts it for you;
211
+ Rack and Sinatra apps add it ahead of `Tracking` as shown above.
206
212
 
207
213
  Then call it once per page, from your layout:
208
214
 
@@ -15,6 +15,16 @@ module Mbuzz
15
15
  return @app.call(env) if skip_request?(env)
16
16
 
17
17
  request = Rack::Request.new(env)
18
+
19
+ # Only a visitor the browser already holds. This response may be stored
20
+ # by a full-page cache and replayed to everyone, so minting here would
21
+ # put a Set-Cookie in that cache and hand every later visitor the same
22
+ # id — unrelated people merged into one journey. Corruption, not loss:
23
+ # every row still exists, each attributed to the wrong person, and
24
+ # nothing looks missing. A first-time visitor is established a moment
25
+ # later by Middleware::SessionEndpoint, whose POST no cache stores.
26
+ return @app.call(env) unless visitor_id_from_cookie(request)
27
+
18
28
  context = build_request_context(request)
19
29
 
20
30
  env[ENV_VISITOR_ID_KEY] = context[:visitor_id]
@@ -26,9 +36,7 @@ module Mbuzz
26
36
  create_session_async(context, request) if should_create_session?(env)
27
37
 
28
38
  RequestContext.with_context(request: request) do
29
- status, headers, body = @app.call(env)
30
- set_visitor_cookie(headers, context, request)
31
- [status, headers, body]
39
+ @app.call(env)
32
40
  ensure
33
41
  reset_current_attributes
34
42
  end
@@ -87,7 +95,7 @@ module Mbuzz
87
95
  user_agent = request.user_agent.to_s
88
96
 
89
97
  {
90
- visitor_id: resolve_visitor_id(request),
98
+ visitor_id: visitor_id_from_cookie(request),
91
99
  session_id: SecureRandom.uuid,
92
100
  user_id: user_id_from_session(request),
93
101
  url: request.url,
@@ -98,10 +106,6 @@ module Mbuzz
98
106
  }.freeze
99
107
  end
100
108
 
101
- def resolve_visitor_id(request)
102
- visitor_id_from_cookie(request) || Visitor::Identifier.generate
103
- end
104
-
105
109
  def visitor_id_from_cookie(request)
106
110
  request.cookies[VISITOR_COOKIE_NAME]
107
111
  end
@@ -139,33 +143,6 @@ module Mbuzz
139
143
  Rails.logger.error("[Mbuzz] #{message}")
140
144
  end
141
145
 
142
- # Cookie setting - visitor identity only (sessions are server-side)
143
-
144
- def set_visitor_cookie(headers, context, request)
145
- Rack::Utils.set_cookie_header!(
146
- headers,
147
- VISITOR_COOKIE_NAME,
148
- visitor_cookie_options(context, request)
149
- )
150
- end
151
-
152
- def visitor_cookie_options(context, request)
153
- base_cookie_options(request).merge(
154
- value: context[:visitor_id],
155
- max_age: VISITOR_COOKIE_MAX_AGE
156
- )
157
- end
158
-
159
- def base_cookie_options(request)
160
- options = {
161
- path: VISITOR_COOKIE_PATH,
162
- httponly: true,
163
- same_site: VISITOR_COOKIE_SAME_SITE
164
- }
165
- options[:secure] = true if request.ssl?
166
- options
167
- end
168
-
169
146
  # Store context in CurrentAttributes for background job propagation
170
147
  def store_in_current_attributes(context, request)
171
148
  return unless defined?(Mbuzz::Current)
data/lib/mbuzz/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mbuzz
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbuzz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mbuzz team