findinpath commented on code in PR #11041:
URL: https://github.com/apache/iceberg/pull/11041#discussion_r4539283728


##########
format/view-spec.md:
##########
@@ -322,3 +432,182 @@ 
s3://bucket/warehouse/default.db/event_agg/metadata/00002-(uuid).metadata.json
   } ]
 }
 ```
+
+### Materialized View Example
+
+Imagine the following operation, which creates a materialized view that 
precomputes daily event counts:
+
+```sql
+USE prod.default
+```
+
+```sql
+CREATE MATERIALIZED VIEW event_agg_mv (
+    event_count COMMENT 'Count of events',
+    event_date)
+COMMENT 'Precomputed daily event counts'
+AS
+SELECT
+    COUNT(1), CAST(event_ts AS DATE)
+FROM events
+GROUP BY 2
+```
+
+The writer creates the storage table `event_agg_mv__storage` and then commits 
the materialized view metadata, which looks as follows:
+
+```
+s3://bucket/warehouse/default.db/event_agg_mv/metadata/00001-(uuid).metadata.json
+```
+
+```json
+{
+  "view-uuid": "b2a12651-3038-4a72-8a31-5027ab84da35",
+  "format-version" : 1,
+  "location" : "s3://bucket/warehouse/default.db/event_agg_mv",
+  "current-version-id" : 1,
+  "properties" : {
+    "comment" : "Precomputed daily event counts"
+  },
+  "versions" : [ {
+    "version-id" : 1,
+    "timestamp-ms" : 1573518431292,
+    "schema-id" : 1,
+    "default-catalog" : "prod",
+    "default-namespace" : [ "default" ],
+    "summary" : {
+      "engine-name" : "Spark",
+      "engine-version" : "3.4.1"
+    },
+    "representations" : [ {
+      "type" : "sql",
+      "sql" : "SELECT\n    COUNT(1), CAST(event_ts AS DATE)\nFROM 
events\nGROUP BY 2",
+      "dialect" : "spark"
+    } ],
+    "storage-table" : {
+      "namespace" : [ "default" ],
+      "name" : "event_agg_mv__storage"
+    }
+  } ],
+  "schemas": [ {
+    "schema-id": 1,
+    "type" : "struct",
+    "fields" : [ {
+      "id" : 1,
+      "name" : "event_count",
+      "required" : false,
+      "type" : "int",
+      "doc" : "Count of events"
+    }, {
+      "id" : 2,
+      "name" : "event_date",
+      "required" : false,
+      "type" : "date"
+    } ]
+  } ],
+  "version-log" : [ {
+    "timestamp-ms" : 1573518431292,
+    "version-id" : 1
+  } ]
+}
+```
+
+After a refresh operation, the storage table's snapshot summary contains the 
`refresh-state` property.
+The following is an example of the `refresh-state` JSON value stored in the 
snapshot summary of the storage table:
+
+```json
+{
+  "view-version-id" : 1,
+  "refresh-start-timestamp-ms" : 1573518435000,
+  "source-states" : [ {
+    "type" : "table",
+    "catalog" : "prod",
+    "namespace" : [ "default" ],
+    "name" : "events",
+    "uuid" : "d4a10b5c-1e8a-4b72-9d67-3f4a8c9e1b2d",
+    "snapshot-id" : 6148331192489823102,
+    "ref" : "main"
+  } ]
+}
+```
+
+## Appendix B: Example freshness criteria and recorded dependencies
+
+Each engine decides when it can read a materialized view's storage table 
instead of evaluating the view query. This appendix calls that decision the 
engine's freshness criteria. The spec does not define freshness criteria, but 
they determine what a writer records in the refresh state. During a refresh, 
the engine applies its criteria to each materialized view the query reads and 
records the dependencies that a reader applying the same criteria needs to 
check. Each example below states its freshness criteria and the refresh state 
that follows from them.
+
+Both examples refresh `event_agg_30_day_mv`, which reads the materialized view 
`event_agg_mv` from [Appendix A](#materialized-view-example). `event_agg_mv` in 
turn reads the Iceberg table `events`:
+
+```sql
+USE prod.default
+```
+
+```sql
+CREATE MATERIALIZED VIEW event_agg_mv (
+    event_count COMMENT 'Count of events',
+    event_date)
+COMMENT 'Precomputed daily event counts'
+AS
+SELECT
+    COUNT(1), CAST(event_ts AS DATE)
+FROM events
+GROUP BY 2
+```
+
+```sql
+CREATE MATERIALIZED VIEW event_agg_30_day_mv (
+    event_count_30_day COMMENT 'Count of events in the 30 days up to the date',
+    event_date)
+COMMENT 'Precomputed 30-day event counts'
+AS
+SELECT
+    SUM(event_count) OVER (
+        ORDER BY event_date
+        RANGE BETWEEN INTERVAL 29 DAYS PRECEDING AND CURRENT ROW),
+    event_date
+FROM event_agg_mv
+```
+
+### Example 1: Fresh if refreshed recently
+
+Freshness criteria: an MV is fresh if its last refresh started within a 
user-defined staleness window, regardless of changes to its sources. PostgreSQL 
materialized views follow this approach.
+
+Recorded state: the criteria never inspect sources, so `source-states` is 
empty.
+
+```json
+{
+  "view-version-id": 1,
+  "refresh-start-timestamp-ms": 1736899200000,
+  "source-states": []
+}
+```
+
+### Example 2: Fresh if dependencies are unchanged
+
+Freshness criteria: an MV is fresh if none of its dependencies changed since 
its last refresh. A reader serves a fresh MV from its storage table and 
evaluates a stale one, applying the same test to each nested MV. 
Proof-of-concept implementations in Spark and Trino follow this approach.

Review Comment:
   IIUC, the scenario exposed here is 
   
   ```
   CREATE MATERIALIZED VIEW event_agg_mv 
   ...
   CREATE MATERIALIZED VIEW event_agg_30_day_mv 
   ...
   
   REFRESH MATERIALIZED VIEW event_agg_30_day_mv
   ```
   
   In this case, the event_agg_30_day_mv is "stale" because it hasn't been 
refreshed yet.
   
   
   Things would be different if we'd be dealing with 
   
   
   ```
   CREATE MATERIALIZED VIEW event_agg_mv 
   ...
   CREATE MATERIALIZED VIEW event_agg_30_day_mv 
   ...
   
   REFRESH MATERIALIZED VIEW event_agg_mv
   REFRESH MATERIALIZED VIEW event_agg_30_day_mv
   ```
   
   Maybe it would be worthwhile to mention this situation as well for clarity 
sake when does a storage table land in the source states.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to