接續 Day 16
這裡 Avro 把 encoding 跟 decoding 動作分成 Writer (寫入者) 和 Reader (讀取者),也就是寫入時會有 寫入者的 schema,讀取時也會有讀取者的 schema,因為應用軟體會進化、改變的關係, 2 邊的 schema 有時會不同,所以 Avro 是怎麼做到並存呢?
當 Avro 讀取資料時,它會同時看寫入者跟讀取者的 schema,然後從寫入者的 schema 翻譯資料到讀取者的 schema 裡,如下圖說明:
這裡可以看到,不會因為 2 邊欄位的順序不同造成問題,Avro 在解析時會一個欄位名接著一個欄位名解析,若寫入者 schema 有該欄而讀取者 schema 沒有,則 Avro 會忽略,若讀取者有新欄位而寫入者沒有,則會以讀取者 schema 所設定的方式給定預設值,整成成表格後如下:
寫入者的 schema | 讀取者的 schema | App Action |
---|---|---|
Y | N | 忽略 |
N | Y | 用讀取者 schema 定義的來給預設值 |
向後兼容代表你寫入者的 schema 是新的,而讀取者的 schema 是舊的,反過來說,
向前兼容代表你寫入者的 schema 是舊的,而讀取者的 schema 是新的,
所以為了保持一致性,你可能只能 新增 或 刪除 帶有 預設值 的欄位,舉例來說,如果你 新增 一欄位是沒有預設值的,新讀取者將無法讀取舊寫入者的資料,這就破壞了向前兼容,再繼續說若你 刪除 了一個沒有預設值的欄位,則舊的讀取者將無法讀取新寫入者寫入的資料,這就破壞了向後兼容。
在某些程式語言,null 是可被允許的預設值,但它不是一個 Avro 用的欄位型態,你可以用另一種 Avro 支援的方式: union type 來讓預設值設為 null;舉例來說,有一欄位型態為 union { null, long, string}
,它說明了該欄位可以是 null、long 或 string,此時你就可以把預設值設定為 null,只要它是在 union 中的某個分支型態之一就 OK,
因此,Avro 沒有像 Thrift 和 Protocol Buffers 那樣的 required 和 optional 的標註,取而代之就是用 union 加預設值來實現。
改變欄位資料型態是沒問題的,Avro 會幫我們轉換型態,
改變欄位名稱也是沒問題但有點 tricky,讀取者的 schema 可以為改變後欄位名取 alias name (別名),之後它就可以對舊寫入者資料用 alias name 來解析,所以向後兼容沒問題,但向前兼容就 GG 了 (舊讀取者遇到不認識的 schema 會忽略),同理新增 union 裡頭的型態也是一樣的。
明天會講 Encoding and Evolution 的最後一段。