2014年8月17日 星期日

Google Cloud Datastore with Go 學習筆記 - Queries

Google Cloud Datastore with Go 學習筆記 - Queries

類似關聯式資料庫,應用成是同樣也可以針對 entity 進行查詢 & 排序…等操作。

  • 原本的 where 過濾條件,在 Datastore 中變成了 filters 的方式,而 filter 可用在 property 的 value、keys、或是 ancestor 上。

  • 排序則是使用 sort orders 來進行。

  • 最後的回傳資料可以是完整的 entity 集合,也可以是包含部分屬性的 entity 集合,甚至可以只是 entity keys。

而標準的查詢包含了以下資訊:

  • entity kind
  • 0 或多個 filter 條件
  • 0 或多個用來排序結果的 sort orders 條件

為了確保執行的效能 & 節省記憶體,建議盡量每次查詢都限定回傳的資料筆數。

Query structure

每個 query 會包含有 entity kind、零或多個 filter、以及零或多個 sort order

Filters

filter 可以用在 property / key / ancestor 三種資訊上。

Property Filter

以下直接用範例來說明:

type Person struct {
    Name      string
    City      string
    BirthYear int
    Height    int
}

func handler_BasicQuery(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    //查詢 Height 屬性大於 170 的資料
    //根據 Height 屬性進行 desc 排序
    q := datastore.NewQuery("Person").Filter("Height > ", 170).Order("-Height")
    var people []Person
    q.GetAll(c, &people)
    for p := range people {
        fmt.Fprintf(w, `Name=%q, City=%q, BirthYear=%d, Height=%d\n`, people[p].Name, people[p].City, people[p].BirthYear, people[p].Height)
    }
}

Key Filter

若要使用 entity key 來進行 filter,必須使用特別的關鍵字 _key_

以下舉個例:

q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)

key 值在 datastore 中是如何排序的呢? 是依照以下的順序來排:

  1. Ancestor path
  2. Entity kind
  3. Identifier (Numeric ID 排在自訂的 key name 之前)

Ancestor Filter

指定 ancestor 也是種 filter 的方式,以下舉個簡單的例子:

q := datastore.NewQuery("Person").Ancestor(ancestorKey)

ancestorKey 若是 nil 會產生錯誤,不會回傳 root entities 喔!

Sort orders

要進行排序需要指定 property & 排序方式(asc or desc),以下範例說明:

//Name asc
q := datastore.NewQuery("Person").Order("Name")

//Height desc
q := datastore.NewQuery("Person").Order("-Height")

//多個條件排序
q := datastore.NewQuery("Person").Order("Name").Order("-Height")

Special query types

Kindless queries

若查詢中沒有指定 kind & ancestor,則表示是針對此應用程式在 Datastore 中所有的 entity 進行查詢。

有些特別的資訊(例如:statistics entities & Blobstore metadata entities)就必須要透過這種方式搜尋。

而不指定 kind & ancestor 的搜尋方式也是有限制的,就是不能使用 property 進行搜尋 or 將資料排序,只能使用 _key_ 關鍵字來針對 entity key 進行搜尋。

Keys-only queries

透過 KeysOnly() 方法取得 entity 的 key 值集合,舉個簡單範例:

q := datastore.NewQuery("Person").KeysOnly()

搜尋限制

在 Datastore 上進行搜尋是有些限制的,說明如下:

  • 在搜尋中沒有包含指定 property 的 entity 都將被忽略
    同樣 kind 底下的 entity 的 property 內容是可以不同的,因此在 filter or sort 時若指定的 property 有可能不會存在於所有的 entity 中,這時沒有該 property 的 entity 都將被忽略。

  • “不等於”的 filter 最多只能用在一個 property 上

//合法搜尋,兩個 filter 都用在相同的 property 上
q := datastore.NewQuery("Person").
        Filter("BirthYear >=", minBirthYear).
        Filter("BirthYear <=", maxBirthYear)

//不合法的搜尋,兩個 filter 分別用在不同的 property 上
q := datastore.NewQuery("Person").
        Filter("BirthYear >=", minBirthYear).
        Filter("Height <=", maxHeight)

//跟"="混用的話,沒有 property 數量限制(但不等於的 filter 還是只能用在同一個 property 上)
q := datastore.NewQuery("Person").
        Filter("Name =", targetName).
        Filter("City =", targetCity).
        Filter("BirthYear >=", minBirthYear).
        Filter("BirthYear <=", maxBirthYear)
  • “不等於” filter 所指定的 property 必須先進行排序
//合法搜尋,BirthYear 為第一個排序的 property
q := datastore.NewQuery("Person").
        Filter("BirthYear >=", minBirthYear).
        Order("BirthYear").
        Order("Name")

//不合法搜尋,BirthYear 必須放置於第一個 property 進行排序
q := datastore.NewQuery("Person").
        Filter("BirthYear >=", minBirthYear).
        Order("Name")

//不合法搜尋,Name 為第一個排序的 property (必須是 BirthYear)
q := datastore.NewQuery("Person").
        Filter("BirthYear >=", minBirthYear).
        Order("Name").
        Order("BirthYear")
  • 含有多個 value 的 property 在 filter or sort 時可能會有出乎意料之外的結果
    這個部分等我真的遇到再來寫…..

  • 在 transaction 中的 query,必須包含 ancestor filter
    因為在 transaction 中的 query 必須屬於相同的 entity group,因此必須帶上 ancestor filter 資訊。

取得查詢結果

取得查詢結果有幾種方式,介紹如下:

使用 Run method

透過 Run method 可以得到一個 Iterator,並搭配 Next method 可以一個一個的取得搜尋結果,範例如下:

q := datastore.NewQuery("Person")
t := q.Run(c)
for {
        var p Person
        k, err := t.Next(&p)
        if err == datastore.Done {
                break // No further entities match the query.
        }
        if err != nil {
                c.Errorf("fetching next Person: %v", err)
                break
        }
        // Do something with Person p and Key k
}

使用 GetAll method

這個方式可以將搜尋結果一次取回來,可參考之前的範例!

使用 peojection query

可用來取得 entity 中部分的 property 的資訊。

使用 keys-only query

透過 KeyOnly method 僅取得 entity key。

使用 Limit & Offset (達到資料分頁目的)

可透過 Limit & Offset 兩個 method 來取得特定範圍的 entity 資料,達到資料分頁效果,範例如下:

//取得第 6~10 筆的資料
q := datastore.NewQuery("Person").Order("-Height").Limit(5).Offset(5)

比較需要注意的是, 雖然 Offset 可以忽略前幾筆資料,但其實資料還是有回傳的(這些都是要收費的),因此若是要 僅回傳 特定筆數資料,就必須使用 query cursor 來處理。

Query cursors

透過 query cursor 就可以取得特定範圍的資料而沒有 Offset 的問題。

當執行了一個取得資料的動作後,應用程式可以取得一個 cursor(base64 編碼過的字串),用來表是上一次取得結果的索引位置,如此一來就可以用 cursor 來取得下一批的資料。

除了可以透過 cursor 指定取得資料的起始點之外,還可以透過指定 end cursor 來限制回傳資料的範圍。

使用限制

cursor 在使用上有些限制存在:

  • cursor 僅有在相同的應用程式內,並使用相同的 query(相同的 kind & ancestor & property filter) 時才有效。

  • 若用在有多個值的 property 上時,cursor 的運作可能會不如預期。

  • 新的 App Engine 功能釋出時,可能會造成某些 cursor 不再有效,此時 Datastore 會回傳 error。

2014年8月13日 星期三

Google Cloud Datastore with Go 學習筆記 - Overview

Google Cloud Datastore with Go 學習筆記 - Overview

與傳統關聯式資料庫的差異

與傳統資料庫比較,Google Cloud Datastore(以下簡稱 Datastore) 架構於分散式系統上,擁有高度的可延展性,在裡面存放的資料以 Entity 的形式存在。
以下有幾項重要的不同:

  • Datastore 是以可延展為前提所設計,在繁忙工作時依然有高效能的表現

    • 寫入 Datastore 的資料會被自動分散到不同的機器上。
    • 讀取 Datastore 的資料時,假設查詢相同數量的資料(例如:50 個 Entity),不論是向擁有 100 個 Entityt 的 Dataset 查詢,或是向有 100 萬個 Entity 的 Dataset 查詢,都會是一樣的快速。
  • 因為所有的查詢都必須有預先定義好的 index 來處理,因此查詢語法的變化就無法像傳統關聯式資料庫一樣多變,以下類型的查詢就不支援:

    • Join
    • 多欄位的 not equal 篩選
    • 子查詢
  • 不同於關聯式資料庫,Table 中的每一筆資料的型態都要是相同的;在 Datastore 中,即使相同 kind 的 Entity 也不一定有要相同的屬性

Entity

存放於 Datastore 內的資料以物件(object)的形式存在,這些物件稱為 Entity

Entity 內可以有一或多個屬性,這些屬性稱為 Property

每個 Property 都會有其對應的 Value,這些 value 可以是各式不同的型態,包括整數、浮點數、字串
、日期、二進位資料 …. 等。

Datastore entity 是 schemaless 的,若是要確保每個 entity 都會有相同的 property set,就必須透過程式自行控制。

Kind / Key / Identifier

每個 entity 都屬於特定的 kind,目的是可以在之後進行有分類的查詢,例如:位於人事系統中的職員資料,可能就會屬於 Employee 這個 kind。

每個 entity 都有專屬且唯一的 key,而 key 是由以下項目所組成:

  • entity 所屬的 kind
  • Identifier,可能是一個 key name 字串,或是整數 ID
  • 若是以階層式存在的 entity,還會包含 ancestor path 資訊

因為 identifier 是屬於 entity key 的一部分,因此在 entity 建立時就會指派且無法變更,可以有兩種方式指派:

  1. 程式中直接指定 key name 字串給 entity
  2. 讓 Datastore 自動指定整數 ID 給 entity (有兩種產生整數 ID 的 policy,可參考文章1文章2)

Ancestor path

就像檔案系統一樣,entity 也可以以階層式的方式存放。(entity 中還包含了 child entity)

沒有 parent 的 entity 稱為 root entity

entity 之間的父子關係若是已經給定了就無法再更改,而 Datastore 不會指派相同的整數 ID 給同樣 parent 的 entity 或是兩個 root entity。

由於 entity 有父子關係,因此要完整表示一個 child entity 的 key,就必須包含類似以下的 ancestor path 資訊:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

若是 root entity,ancestor path 資訊就大概會像下面這樣:

[Person:GreatGrandpa]

Query & Index

若想要對 Datastore 進行查詢,可以包含以下資訊:

  • 指定要查詢的 kind 資訊之外
  • 根據查詢需求指定 filter,而 filter 資訊可以是 entity 的 key / value / ancestor … 等資訊
  • 用來根據 property 來排序查詢結果用的 sort orders 資訊

查詢結果可以是完整的 entity 資訊,或是僅有部分 property 的 entity,也可以只有 entity key。

為了確保查詢效能,建議每個查詢都包含指定有限數量的 entity,避免一次回傳太多資料而影響查詢效能。

若是要確保回傳的資料達到 strong consistent 的狀態,在規劃資料存放的架構時,就要指定好相關的 entity 都放置於相同的 entity group 中,否則 Datastore 只能確保回傳的資料是 eventually consistent

App Engine 會預先在 entity 的每個 property 上定義 index,若要定義更複雜的 index 資訊,則可以修改 index.yaml 這個檔案。

Transaction

相同於傳統的關聯式資料庫,每一個交易可以包含多個 insert / update / delete 的操作,這些操作最多可以同時分布到 5 個不同的 entity group。

Datastore 使用 [optimistic concurrency] 的機制來管理 transaction,當多個交易同時針對相同的 entity group 進行修改時,只有第一個 transaction 的 commit 會成功,其餘會失敗,而失敗的 transaction 可以針對第一個 transaction 已經修改好的資料重新嘗試變更操作。也因為這種機制,就限制了對於相同的 entity group 可以同時進行的 transaction 數量。

同一個 transaction 中若是對多個 entity group 進行操作,就稱為 cross-group (XG) transaction,最多可以同時對 5 個 entity group 進行操作。

在 cross-group transaction 終無法執行 non-ancestor query,因為此類型的查詢可能會包含部分之前被 commit 後的交易結果。

而且在 cross-group transaction 中,即使只有讀取 entity group 的資料而未修改,但若有其他 transaction 同時存取相同的 entity group,也是會有衝突錯誤發生的。

Quotas and limits

使用 Google Cloud Datastore,有幾種不同的 quota 定義必須了解:

  • Datastore API Calls quota
    每次呼叫 Datastore API 的次數屬於此類 (有些 library 的一次呼叫會包含多次的 Datastore API Call)。

  • Data Send to Datastore API quota
    透過應用程式傳來的資料屬於此類。

  • Data Received from Datastore API quota
    應用程式接收到來自 Datastore 的資料屬於此類。

  • Stored Data quota
    存於 Datastore 的資料(包含 entity 的 property & key,甚至 index 都算)數量屬於此類。若要了解這些資料在 Datastore 中詳細的存放方式,可以參考 How Entities and Indexes Are Stored 這篇文章。

參考資料

  1. Go Datastore API - Go — Google Developers

2014年8月12日 星期二

[VMware] 解決 consolidation 時發生 file locked 錯誤的狀況

[VMware] 解決 consolidation 時發生 file locked 錯誤的狀況

今天早上到公司發現 vSphere Web Client 中有提示某台 VM 有 warning,進到 Summary tab 看了一下,出現了訊息如下:

Virtual machine Consolidation Needed status
Virtual machine disks consolidation is needed.

vSphere Web Client 中提示 VM 有狀況,檢視後顯示 "Virtual machine Consolidation Needed status. Virtual machine disks consolidation is needed"


恩,一看就大概可以知道是 snapshot 時出了點狀況,所以 disk 需要執行 consolidation 的動作,於是很直覺的按照下圖執行:

VM >> All vCenter Actions >> Snapshots >> Consolidate

執行 snapshot consolidation @ VM

結果出現了以下錯誤訊息:

Consolidate virtual machine disk files
[VM Name]
Unable to access file since it is locked

Consolidate Error


後來發現原來是 VDP 搞的禍,詢問原廠人員後,他們提到有時候 VDP 在備份的時候會搭配 snapshop 的機制來進行,但有時候會發生秀逗…..

處理的方式很簡單,因為 VDP 在處理 snapshot 時出了 trouble,因此在 VDP 的 VM 內就會殘留有問題的 snapshot 資訊,可透過以下方式尋找:

VM >> Edit virtual machine settings >> 尋找有問題的 Hard disk(此處為帶有 warning VM name 的 vmdk 檔案) >> Remove

(注意! 不要勾選 Delete files from datastore)

從 VDP 中移除有問題的 VMDK

當有問題的 vmdk 被移除後,最後再重新做一次 snapshot consolidate,就可以解決此 warning 囉!

2014年8月8日 星期五

讓 Sublime Text 3 支援 Auto Complete for Go & Google App Engine Library

讓 Sublime Text 3 支援 Auto Complete for Go & Google App Engine Library

首先在 Ubuntu 下安裝 Sublime Text,可以參考以下這篇:

在 Ubuntu Linux 下安裝及使用 Sublime Text 2 / 3 - 玩物尚誌


接著讓 Sublime Text 支援 Auto Complete for Go,可以參考以下這篇:

基礎知識- 在Ubuntu 14.04 中配置Sublime Text 3 的Golang 開發環境- GoLove - 博客園


最後讓 Sublime Text 支援 Google App Engine Library 的 Auto Complete,步驟如下:

1、設定 GoSublime

開啟 Preferences > Package Settings > GoSublime > Settings-User,輸入以下內容:

{
    "use_legacy_imports": true, 
    "installsuffix": "appengine",
    "env": {
        "GOPATH": "$GOPATH",
		"GOROOT": "$HOME/google-cloud-sdk/platform/google_appengine/goroot"
    }
}

其中 $HOME/google-cloud-sdk/platform/google_appengine/goroot 是 Google Cloud SDK 在我的電腦上安裝後的路徑,必須根據自己的環境去修改。

此時重新開啟 Sublime Text,還無法自動抓取到 Google App Engine Library 的定義,因為在 $GOROOT 中的 /pkg 目錄中,library 位於 /[GOOS][GOARCH]_appengine 目錄中(我的例子是 ~/google-cloud-sdk/platform/google_appengine/goroot/pkg/linux_amd64_appengine),但預設抓取的是 /[GOOS][GOARCH] 路徑。
因此下指令建立 symbolic link:

cd ~/google-cloud-sdk/platform/google_appengine/goroot/pkg
ln -s ~/google-cloud-sdk/platform/google_appengine/goroot/pkg/linux_amd64_appengine ./linux_amd64

最後重新開啟 Sublime Text,就會發現 Auto Complete for Google App Engine Library 已經可以正常使用啦!!


參考文章

  1. 在 Ubuntu Linux 下安裝及使用 Sublime Text 2 / 3 - 玩物尚誌
  2. 基礎知識- 在Ubuntu 14.04 中配置Sublime Text 3 的Golang 開發環境- GoLove - 博客園
  3. Code Completion and Google App Engine (Bug in GoSublime or GoCode?) · Issue #418 · DisposaBoy/GoSublime · GitHub