Skip to content

Commit 60b536c

Browse files
committed
remove bitly/attributions specific examples
1 parent e7614dd commit 60b536c

4 files changed

Lines changed: 60 additions & 64 deletions

File tree

CMV_SUPPORT.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# CMV (Continuous Materialized View) Support for little_bigtable
22

3-
## Context
3+
## Overview
44

5-
The attributions app needs a CMV to re-key `attributions_conversion_events` data
6-
by `provider_id#client_id` for efficient shop-level queries. Production Bigtable
7-
handles this automatically, but the local emulator needs to support it for dev/testing.
5+
Bigtable CMVs allow you to re-key table data for efficient queries on alternate key orderings.
6+
Production Bigtable handles CMV maintenance automatically, but the local emulator needs explicit
7+
support for development and testing.
88

99
## Key Finding
1010

@@ -25,18 +25,18 @@ Define CMVs in a JSON file:
2525
```json
2626
[
2727
{
28-
"source_table": "attributions_conversion_events",
29-
"view_id": "attributions_conversion_events_by_client",
28+
"source_table": "events",
29+
"view_id": "events_by_account",
3030
"key_separator": "#",
3131
"key_mapping": [3, 4, 1, 2, 0],
32-
"include_families": ["cr", "d", "m"],
32+
"include_families": ["cf1", "cf2"],
3333
"append_source_key": true
3434
}
3535
]
3636
```
3737

3838
- `source_table`: the table that feeds the CMV
39-
- `view_id`: the CMV name (used as the table name for reads)
39+
- `view_id`: the materialized view ID (used as the table name for reads)
4040
- `key_separator`: delimiter in the composite row key
4141
- `key_mapping`: 0-based indices into `SPLIT(source_key, separator)` in the new key order
4242
- `include_families`: column families to carry (empty = all)
@@ -47,7 +47,7 @@ Define CMVs in a JSON file:
4747
```bash
4848
little_bigtable --host 0.0.0.0 --port 8300 \
4949
--db-file /data/little_bigtable/little_bigtable.db \
50-
--cmv-config /path/to/attributions.json
50+
--cmv-config ./cmv_configs/example.json
5151
```
5252

5353
### 3. Write-time Sync
@@ -70,16 +70,16 @@ derives the CMV key and deletes the corresponding CMV row.
7070
Since the CMV shadow table is a regular table, reads use the standard approach:
7171

7272
```go
73-
table := client.Open("attributions_conversion_events_by_client")
74-
row, err := table.ReadRow(ctx, "shopify#myshop.myshopify.com#...")
73+
table := client.Open("events_by_account")
74+
row, err := table.ReadRow(ctx, "region_a#account_123#...")
7575
```
7676

7777
## What's Changed
7878

7979
### New Files
8080
- `bttest/cmv.go` — CMV config types, JSON loader, SQL parser, key transformation logic
8181
- `bttest/cmv_test.go` — Tests for key transformation, write sync, delete propagation
82-
- `cmv_configs/attributions.json` — Example config for the attributions app
82+
- `cmv_configs/` — Example config files
8383

8484
### Modified Files
8585
- `little_bigtable.go` — Added `--cmv-config` flag, version bump to 0.2.0
@@ -98,21 +98,21 @@ row, err := table.ReadRow(ctx, "shopify#myshop.myshopify.com#...")
9898
- **Backfill**: If data exists in the source table before the CMV is registered,
9999
it won't be retroactively copied.
100100

101-
## Example: Attributions Key Transformation
101+
## Example: Key Transformation
102102

103-
Source row key format:
103+
Source row key format (5 components):
104104
```
105-
bitlink_id#revTs#conversion_type#provider_id#client_id#attribution_source_id
105+
item_id#timestamp#type#region#account_id
106106
```
107107

108108
With `key_mapping: [3, 4, 1, 2, 0]` and `append_source_key: true`, a source key:
109109
```
110-
bit.ly/123#9999999#lead#shopify#myshop#01HWXYZ
110+
item-abc#9999999#type-x#region-a#account-42
111111
```
112112
Becomes CMV key:
113113
```
114-
shopify#myshop#9999999#lead#bit.ly/123#bit.ly/123#9999999#lead#shopify#myshop#01HWXYZ
114+
region-a#account-42#9999999#type-x#item-abc#item-abc#9999999#type-x#region-a#account-42
115115
```
116116

117-
This matches the production CMV's `ORDER BY provider_id, client_id, rev_ts,
118-
event_category, bitlink_id, source_key`.
117+
The first 5 components are the re-ordered key (per `key_mapping`); the remainder is the
118+
full original source key appended due to `append_source_key: true`.

bttest/cmv_test.go

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,23 @@ func newTestServerWithCMV(t *testing.T, configs []CMVConfig) *server {
3232
}
3333

3434
func TestCMVTransformKey(t *testing.T) {
35-
// Mirrors the production CMV config: key_mapping [3,4,1,2,0] + append_source_key.
36-
// SELECT SPLIT(_key,'#')[SAFE_OFFSET(3)] AS provider_id, ..., _key AS source_key
37-
// ORDER BY provider_id, client_id, rev_ts, event_category, bitlink_id, source_key
38-
// CMV key = provider_id#client_id#rev_ts#event_category#bitlink_id#<full_source_key>
35+
// key_mapping [3,4,1,2,0] + append_source_key:
36+
// CMV key = parts[3]#parts[4]#parts[1]#parts[2]#parts[0]#<full_source_key>
3937
inst := &cmvInstance{
4038
config: CMVConfig{
41-
SourceTable: "attributions_conversion_events",
42-
ViewID: "attributions_conversion_events_by_client",
39+
SourceTable: "events",
40+
ViewID: "events_by_account",
4341
KeySeparator: "#",
4442
KeyMapping: []int{3, 4, 1, 2, 0},
4543
AppendSourceKey: true,
4644
},
4745
}
4846

49-
sourceKey := "bitlink123#9999999999#order#shopify#myshop.myshopify.com#01HWXYZ"
47+
sourceKey := "item-abc#9999999#type-x#region-a#account-42#src-01"
5048
got := inst.transformKey(sourceKey)
51-
// parts: [0]bitlink123 [1]9999999999 [2]order [3]shopify [4]myshop.myshopify.com [5]01HWXYZ
49+
// parts: [0]item-abc [1]9999999 [2]type-x [3]region-a [4]account-42 [5]src-01
5250
// mapped: parts[3]#parts[4]#parts[1]#parts[2]#parts[0] + full source key
53-
want := "shopify#myshop.myshopify.com#9999999999#order#bitlink123#bitlink123#9999999999#order#shopify#myshop.myshopify.com#01HWXYZ"
51+
want := "region-a#account-42#9999999#type-x#item-abc#item-abc#9999999#type-x#region-a#account-42#src-01"
5452
assert.Equal(t, want, got)
5553
}
5654

@@ -264,11 +262,11 @@ func TestLoadCMVConfigs(t *testing.T) {
264262

265263
content := `[
266264
{
267-
"source_table": "attributions_conversion_events",
268-
"view_id": "attributions_conversion_events_by_client",
265+
"source_table": "events",
266+
"view_id": "events_by_account",
269267
"key_separator": "#",
270268
"key_mapping": [3, 4, 1, 2, 0],
271-
"include_families": ["cr", "d", "m"],
269+
"include_families": ["cf1", "cf2"],
272270
"append_source_key": true
273271
}
274272
]`
@@ -279,40 +277,38 @@ func TestLoadCMVConfigs(t *testing.T) {
279277
configs, err := LoadCMVConfigs(tmpFile.Name())
280278
require.NoError(t, err)
281279
require.Len(t, configs, 1)
282-
assert.Equal(t, "attributions_conversion_events", configs[0].SourceTable)
283-
assert.Equal(t, "attributions_conversion_events_by_client", configs[0].ViewID)
280+
assert.Equal(t, "events", configs[0].SourceTable)
281+
assert.Equal(t, "events_by_account", configs[0].ViewID)
284282
assert.Equal(t, "#", configs[0].KeySeparator)
285283
assert.Equal(t, []int{3, 4, 1, 2, 0}, configs[0].KeyMapping)
286-
assert.Equal(t, []string{"cr", "d", "m"}, configs[0].IncludeFamilies)
284+
assert.Equal(t, []string{"cf1", "cf2"}, configs[0].IncludeFamilies)
287285
assert.True(t, configs[0].AppendSourceKey)
288286
}
289287

290288
func TestParseCMVConfigFromSQL(t *testing.T) {
291-
// Mirrors the production Terraform CMV SQL exactly: no CAST, _key aliased as
292-
// source_key, attribution_source_id is embedded in source_key not a separate component.
289+
// Standard Bigtable CMV SQL pattern: plain SPLIT (no CAST), _key aliased,
290+
// alias appears in ORDER BY to set AppendSourceKey = true.
293291
query := `SELECT
294-
SPLIT(_key, '#')[SAFE_OFFSET(3)] AS provider_id,
295-
SPLIT(_key, '#')[SAFE_OFFSET(4)] AS client_id,
296-
SPLIT(_key, '#')[SAFE_OFFSET(1)] AS rev_ts,
297-
SPLIT(_key, '#')[SAFE_OFFSET(2)] AS event_category,
298-
SPLIT(_key, '#')[SAFE_OFFSET(0)] AS bitlink_id,
299-
_key AS source_key,
300-
cr AS cr,
301-
d AS d,
302-
m AS m
303-
FROM ` + "`attributions_conversion_events`" + `
304-
ORDER BY provider_id, client_id, rev_ts, event_category, bitlink_id, source_key`
305-
306-
cfg, err := ParseCMVConfigFromSQL("attributions_conversion_events_by_client", query)
292+
SPLIT(_key, '#')[SAFE_OFFSET(3)] AS region,
293+
SPLIT(_key, '#')[SAFE_OFFSET(4)] AS account_id,
294+
SPLIT(_key, '#')[SAFE_OFFSET(1)] AS timestamp,
295+
SPLIT(_key, '#')[SAFE_OFFSET(2)] AS type,
296+
SPLIT(_key, '#')[SAFE_OFFSET(0)] AS item_id,
297+
_key AS src_key,
298+
cf1 AS cf1,
299+
cf2 AS cf2
300+
FROM ` + "`events`" + `
301+
ORDER BY region, account_id, timestamp, type, item_id, src_key`
302+
303+
cfg, err := ParseCMVConfigFromSQL("events_by_account", query)
307304
require.NoError(t, err)
308-
assert.Equal(t, "attributions_conversion_events", cfg.SourceTable)
309-
assert.Equal(t, "attributions_conversion_events_by_client", cfg.ViewID)
305+
assert.Equal(t, "events", cfg.SourceTable)
306+
assert.Equal(t, "events_by_account", cfg.ViewID)
310307
assert.Equal(t, "#", cfg.KeySeparator)
311308
assert.Equal(t, []int{3, 4, 1, 2, 0}, cfg.KeyMapping)
312309
assert.True(t, cfg.AppendSourceKey)
313-
assert.Contains(t, cfg.IncludeFamilies, "cr")
314-
assert.Contains(t, cfg.IncludeFamilies, "d")
315-
assert.Contains(t, cfg.IncludeFamilies, "m")
310+
assert.Contains(t, cfg.IncludeFamilies, "cf1")
311+
assert.Contains(t, cfg.IncludeFamilies, "cf2")
316312
}
317313

318314
func TestCMVDropRowRangePrefix(t *testing.T) {

cmv_configs/attributions.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

cmv_configs/example.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
{
3+
"source_table": "events",
4+
"view_id": "events_by_account",
5+
"key_separator": "#",
6+
"key_mapping": [3, 4, 1, 2, 0],
7+
"include_families": ["cf1", "cf2"],
8+
"append_source_key": true
9+
}
10+
]

0 commit comments

Comments
 (0)