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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +14 -8
- data/lib/mbuzz/middleware/tracking.rb +12 -35
- data/lib/mbuzz/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 03aa54a68ab1b6e6313d69c910c88aec7241e19d65b72b9cee37a6e187454692
|
|
4
|
+
data.tar.gz: 3ddce95a2f8795635278785f403daf6a41e15bc31fc01c3990989c20911ebc21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
199
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
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:
|
|
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