Sunday, December 07, 2014

Amazon DynamoDB - Data Modeling and Scaling Best Practices | AWS re:Invent 2014

前情提要:
Amazon DynamoDB會將一個table切成好幾個partition,
而整個table的throughput會平均分配給各個partition.
Total provisioned throughput/partitions = throughput per partition
不過partition跟思念一樣都是很玄的東西,
Amazon DynamoDB的官方文件只輕描淡寫的說是依照hash key來決定item是屬於哪個partition,
我們無從得知我們的table實際上被切成幾個partition.
然而在今年的AWS re:Invent, Amazon DynamoDB: Data Modeling and Scaling Best Practices的講者終於揭開了partition的奧秘!
剛剛查了一下官方文件不知何時也更新囉!
Partition的個數跟table的資料量(大小)以及讀寫的provision throughput有關.
若是你的RCU和WCU都是1000,
( 1000 / 3000 ) + ( 1000 / 1000 ) = 1.333
那你的table就會需要兩個partition, 分別有500的RCU和WCU.

依照公式可以看出,
若是我們的資料量不大而且需要的讀寫量(單筆資料&頻率)不高,
其實table是不會被切成多個partition哦!.
所以不用擔心存取資料(key)不夠分散, 某些狀況下可以好好利用這點.

另外講者也提到一些實際應用上的小技巧,
針對熱門的read item像是拍賣主打的商品資料要cache來降低花費(減少需要的throughput)
針對熱門的write item像是投票系統的候選人則是要自行把候選人切成多個key讓寫入能夠分散
(投票的人數多過於候選人, 且票數常常是集中在固定幾個人身上)

不錯的影片, 有在用DynamoDB的人值得看看啦!

Sunday, July 13, 2014

西班牙葡萄牙我們來惹 (7) Sevilla城堡

自從看到HBO發佈Game of Thrones第五季將會有部分場景在西班牙拍攝的消息就感到非常興奮,
官方消息不僅透露西班牙將會作為馬泰爾家族的根據地冬恩,
更明確的指出Alcázar of Sevilla(Sevilla城堡)將會是水之花園的拍攝場所,
雖然這一系列的遊記已經拖稿快兩年, 聽到此一消息讓我又重獲動力啊!

Alcázar of Seville在旅遊書上的譯名相當混亂, 有的稱之為賽維亞皇宮, 也有的叫它做阿卡乍堡.
其實Alcázar就是城堡的意思, 而且它一開始是由摩爾人建造而成的城堡,
後來的改建及擴建則又受到文藝復興時期的藝術風格和新古典主義的影響.

我們由獅子門進入,



進門


摩爾人的建築真的很厲害, 光是看從外頭看城牆會覺得整個城堡氣勢恢弘,
主建物本身卻又不同, 格局是一個封閉的長方形, 有著許多許多房間,
拱廊圍繞著庭院, 庭院中間會有個水池, 感覺就是個能讓人心情放鬆的地方,
然而他們不僅考慮到機能性, 建築的裝飾上也充滿著各種不同的元素,
細節精美的程度讓人感到相當驚豔!
拱廊有著繁複的雕刻,



屋頂的金色裝飾讓人目眩神迷!


牆壁上也貼滿了瓷磚, 相同樣式的瓷磚重複排列形成了如萬花筒般的複雜變化,
瓷磚的圖案有基本的幾何形狀也有各種植物動物,
這個人面魚尾真是超可愛啊!


往上走到二樓,

在這裡甜小大發現只要戴上墨鏡我拍照就無敵了(是嗎XD)

隨後我們到了後方的花園閒晃,


離開城堡之後我們就展開充實的逛街行程啦!

小大版: 西班牙也有火龍果 Cactus Figs in Seville(有早午餐跟晚餐)

ref: Alcázar of Sevilla

西班牙葡萄牙我們來惹 (2) 太陽海岸 Costa del Sol: Nerja, Frigiliana, San Pedro
西班牙葡萄牙我們來惹 (3) 藍色小精靈村Juzcar以及鬥牛的故鄉Ronda
西班牙葡萄牙我們來惹 (4) 快閃直布羅陀Gibraltar以及雪莉酒的故鄉Jerez
西班牙葡萄牙我們來惹 (5) Sevilla主教堂, 希拉達塔, 西班牙廣場

Monday, June 30, 2014

呱呱蜜汁鴨胸


之前跟孔雀一起買了豪野鴨, 因為是難得的合購所以一口氣就買了四片鴨胸XD
本來想說可以多試試幾種煮法,
但是參考了蘿瑞娜的三步驟搞定懶人版蜜汁燒鴨食譜之後,就懶得再找別的來試了XD
因為這個版本真的是簡單又好吃啊

[主材料]
- 豪野鴨特選鴨胸 *1塊約300g
- 青蔥 *1支
[醬汁]
- 李錦記蜜汁烤肉醬
- 李錦記蠔油
- 米酒
- 水
- 蜂蜜

[步驟]
1. 鴨胸用米酒加鹽巴醃半小時
2. 鴨胸先擦乾, 皮朝下先用大火煎3分鐘轉中火煎7分鐘
3. 翻面煎8分鐘
* 煎鴨胸很會噴油, 最好用深一點的鍋子事後才不會難清理
* 這個時間我覺得蠻剛好的, 我只有第一次做的時候比較緊張從頭監控到尾
* 之後就都放著煎然後用手機定時就去做別的事情了
4. 翻面煎的時候另一個平底鍋煮醬汁, 比例可以參考蘿瑞娜的部落格
* 我自己是不太喜歡蜂蜜煮熟的味道, 後幾次做就都沒放了
* 比例的話我是蠻隨性的, 因為跟鴨肉一起煮的時間沒有很長, 基本上味道不會太重
* 如果真的太淡吃的時候就多沾點醬, 太鹹就多配點蔥囉XD
5. 翻面煎完將鴨胸放到醬汁鍋小火煮5-8分鐘
6. 之後關火放在鍋中休息5分鐘後切片, 跟牛排煎完要休息之後才能切片避免肉汁跑掉的道理應該是一樣的吧!
7. 蔥切絲, 將剩餘醬汁呈到小碟子後就可以擺盤啦!

Sunday, June 29, 2014

Gatling筆記: 完成自己的simulation

[基本單元]
1. scenaio: 描述使用情境, 一個simulation可以指定多個scenario
2. exec: 指定要做的事, 通常會是http request, 一個scenario裡面用exec串接多個動作
3. http: 設定request格式, 也可加入response檢查
4. httpConfig: 設定整個scenario共用的protocol(要打哪個domain, 要不要自動redirect等等)
5. users: 要用幾個user來跑
6. ramp: 設定每個user一開始起來跑的間隔
scn.users(10).ramp(10) // 10 users/10s = 1 user/s
scn.users(10).ramp(20) // 10 users/20s = 0.5 user/s = 1 user every 2s
scn.users(1000).ramp(100) // 1000 users/100s = 10 users/s

[HTTP]
1. GET: 以下是基本的HTTP GET範例
val httpConf = httpConfig.baseURL("http://my.website.tld")

val scn = scenario("My Scenario")
  .exec(
    http("My Request")
    .get("/my_path") // Will actually make a request on "http://my.website.tld/my_path"
  )
  .exec(
    http("My Other Request")
    .get("http://other.website.tld") // Will make a request on "http://other.website.tld"
  ...

setUp(scn.protocolConfig(httpConf)...)
2. 帶query string:
- 直接串在uri上
- queryParam(key: String, value: String)

3. 加header:
- 加一個: header(key: String, value: String)
- 加多個: header(Map[String, String]())

4.帶request body:
- 直接給字串: body(body: String)
- 從檔案讀: fileBody(fileName: String)
** 要注意檔案要放在galting目錄的這個路徑底下user-files/request-bodies/
- 從檔案讀且檔案內有變數可以替換: fileBody(templateFileName: String, valuesToReplace: Map[String, String])
** 同樣檔案要放在galting目錄的user-files/request-bodies/路徑底下, 且副檔名須為ssp
- 給byte array: byteArrayBody (byteArray : (Session) => Array[Byte])
- 另外還有POST特有的form以及multi-part格式

[Session]
session是一個Map, key為String, value可以是任意類型,
每個user在執行scenario時都有一個專屬的session可以暫存各種之後會用到的東西
1. 儲存:
- saveAs("myKey"), 通常是與Check合在一起用
- setAttribute("myKey", "myValue")
2. 取出:
- "${myKey}"
3. 如果對語法不熟悉或是發生什麼怪狀況需要debug時, 可以在用exec特別處理session
** 因為session是不能修改的, 所以當你動到session的內容時, 它其實是產生一個新的session物件給你
- getAttribute(key: String): Any = getTypedAttribute[Any](key)
- getTypedAttribute[X](key: String)
- getAttributeAsOption[T](key: String): Option[T]
- setAttributes(attributes: Map[String, Any])
- setAttribute(attributeKey: String, attributeValue: Any)
- removeAttribute(attributeKey: String)
- isAttributeDefined(attributeKey: String)
- getCounterValue(counterName: String)
- getTimerValue(timerName: String)
.exec(session =>
  session.setAttribute("foo", "bar")
  println(session)
  session
)

[Feeder]
用來餵參數用的好東西, 通常會是讀檔但也把JDBC或Redis當作來源,
1. 讀檔怎麼讀
- csv( filename: String ), 每個值是用,分隔
- ssv( filename: String ), 每個值是用;分隔
- tsv( filename: String ), 每個值是用TAB分隔
- 自訂分隔符號csv("user_credentials.csv", '#')
2. 每次呼叫feeder時, 會從來源取得一筆資料, 有幾種模式
- queue(預設): 照順序讀進來, 讀到底之後simulation會終止, 使用這個模式的時候要注意給的資料要夠多
- random: 隨機
- circular: 照順序讀進來, 讀到底之後會從頭讀
3. 如何存取
資料被feeder讀進來之後會被放在session裡, 如果是從檔案讀的話, key就是對應到檔案第一行的名稱
username,password
john,smith21
john,doe43
4. 使用自己的feeder
Feeder其實是一個簡單的class, 當你呼叫它的提供一個next函式時會回傳一個Map.
package object myfeeders {
  import com.excilys.ebi.gatling.core.feeder._

  val myAccountFeeder = new Feeder[String] {
    import org.joda.time.DateTime
    import scala.util.Random

    private val RNG = new Random

    // random number in between [a...b]
    private def randInt(a:Int, b:Int) = RNG.nextInt(b-a) + a

    private def daysOfMonth(year:Int, month:Int) = new DateTime(year, month, 1, 0, 0, 0, 000).dayOfMonth.getMaximumValue

    // always return true as this feeder can be polled infinitively
    override def hasNext = true

    override def next: Map[String, String] = {
      val email = scala.math.abs(java.util.UUID.randomUUID.getMostSignificantBits) + "_gatling@dontsend.com"
      val year = randInt(1945, 1994)
      val month = randInt(1, 12)
      val day = randInt(1, daysOfMonth(year, month))

      Map("contactEmail" -> email, 
        "birthdayYear" -> year.toString, 
        "birthdayMonth" -> month.toString, 
        "birthdayDay" -> day.toString)
    }
  }
}

[Check]
就是可以讓你檢查response是否與你預期相同,
檢查的範圍除了http status,還可以檢查payload格式是否正確, 甚至可以抓特定的值出來確認
1. status
- check(status.is(200))
- check(status.not(404), status.not(500)))
2. body
- check(regex("""..."""))
- check(xpath(".."))
- check(jsonPath(""))
3. 另外可以用find把你想要的值抓出來, 是需求決定是否要套上transform將值做些轉換或是用saveAs把它存在session裡

[範例]
最後的最後有一些流程控制的東西懶得寫了, 就直接看範例吧!
package collection

import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.http.Headers.Names._
import akka.util.duration._
import bootstrap._
import CLTHeaders._
import myfeeders._

class MyCollectionSimulation extends Simulation {
 val httpConf = httpConfig.baseURL("http://localhost:9000/")

  val get_chain = exec(http("get collection")
          .get("api/collection/${cid}")          
          .headers(auth_headers)
          .check(status.is(200)))

  val update_chain = exec(http("update collection")
          .post("api/")
          .fileBody("Update", Map("cid" -> "${cid}", "title" -> "${title}")).asJSON
          .headers(auth_headers)
          .check(status.is(200))) 

  val my_scenario = scenario("CRUD collection")
        .during(3 minutes) {
          feed(myAccountFeeder)
          .exec(http("create collection")
            .post("api/")
            .fileBody("CreateCollection.json").asJSON
            .headers(auth_headers)
            .check(status.is(200))
            .check(jsonPath("$.collection").saveAs("collection")))
          .exec(session=> {                   
            val tmp = session.getTypedAttribute[String]("collection").split("\"")
            val cid = tmp(1)                   
            session.removeAttribute("collection").setAttribute("cid", cid)
            })
          .repeat(2){
            get_chain      
          }
          .doIf(session=>session.getTypedAttribute[String]("cid").head.toInt>80) {
            update_chain
          }
          .exec(http("delete collection")
            .post("api/")
            .fileBody("DeleteCollection", Map("cid" -> "${cid}")).asJSON
            .headers(auth_headers)
            .check(status.is(200)))
          .exec(http("get collection")
            .get("api/collection/${cid}")          
            .headers(auth_headers)
            .check(status.is(400)))
        }

  setUp(create_scenario.users(100).ramp(10).protocolConfig(httpConf))      
}

上一集: Gatling, 威力強大卻也簡單上手的Http Server壓力測試工具
ref:
- HTTP reference
- Session
- Feeders
- Checks

Friday, June 27, 2014

Scala筆記: Gatling, 威力強大卻也簡單上手的Http Server壓力測試工具

[Gatling]
Gatling的底層是Akka搭配Netty, 會比狂開thread來提升rps的方法來的有效率.
它提供了簡單的DSL就算你不懂Akka不懂Netty不懂Scala, 也可以一眼看懂範例在做什麼.
搭配文件其實蠻容易依樣畫葫蘆寫出自己的測試程式.
測試結果也有很厲害的圖表可以參考(status, rps, latency的總合及分佈等等).

[安裝]
1. 先下載Gatling的package, 我目前是用1.5.5
2. 接下來檢查你的JDK版本, Gatling跟JDK版本對應可以參考這裡, 我用Mac, JDK版本是1.7.0_45
3. 然後只要把抓下來的package解開就可以囉!


[跑跑範例]
1. 執行gatling目錄bin底下的gatling.sh會出現兩組範例可以選
Choose a simulation number:
     [0] advanced.AdvancedExampleSimulation
     [1] basic.BasicExampleSimulation
2. 開始跑之後會定期有狀態回報, 要不然就是有Waring才會顯示.
3. 如果是初學其實可以把log等級調成INFO這樣會比較有參與感XD
4. 跑完之後, 結果會以網頁的方式呈現, 可以看到以下圖表. 另外也可以看個別request的狀況, 相當方便!





ref:
- gatling github wiki page

Sunday, June 08, 2014

西雅圖逛逛 (2) Ravenna Park, Washington Park Arboretum, Kerry Park

[Beach]
離開鮭魚階梯之後, 萱小姐提議順便去鄰近的海灘晃晃
但是因為穿了長褲又沒帶拖鞋, 就只坐在椅子上遠遠的看風景沒下去玩水囉!

晴朗的天空加上寬闊的海面真是百看不厭,
但是沙灘就輸台灣很多耶XD

[Ravenna Park]
接著來到萱小姐最喜歡的公園
入口附近有個大草坪, 很多人就一派悠閒的躺著曬太陽, 遛狗或是野餐

走進去之後發現這根本就是森林啊!
充滿芬多精的感覺好健康~
走一圈大概花了快一個小時, 加上調時差一直調不好我整個人累翻了, 所以就回萱小姐家睡午覺先~


[Washington Park Arboretum]
睡飽了再出發, 但是剩下的時間已經不夠划船就直接到華盛頓大學附近的棧道走走.
但是萬萬沒想到這季節水位高, 淹過了步道沒辦法通行,
地頭蛇萱小姐就當機立斷開車到對岸植物園北邊的濕地,

對岸也是沒法走不過這裡腹地比較大可以在這兒散散步



恰巧碰上一群年輕人(我已經不是了啊TAT)歡樂出遊請我幫忙照相,
想到大學時假日幾乎都也都關在寢室看電影跟日劇,
相較之下真是差太多啦 囧

[Kerry Park]
上飛機前的最後一站來到凱莉公園看夜景
剛好有情侶在這裡結婚感覺很很溫馨,
後來又有一群小屁孩從party bus(車子上就真的是醬寫)衝下來鬼吼鬼叫
小小的公園氣氛頓時變得很混搭.


- 西雅圖逛逛 (1) Pioneer Square, Occidental Park, Hiram M. Chittenden Locks and Fish Ladder

Saturday, June 07, 2014

西雅圖逛逛 (1) Pioneer Square, Occidental Park, Hiram M. Chittenden Locks and Fish Ladder

[Pioneer Square]
兩年前來西雅圖的時候這邊的辦公室還在圖書館附近,
這次來已經搬到Pioneer Square旁邊囉!
道路兩旁綠樹成蔭沒有高高的建築物給人壓迫感
挺喜歡這裡特有的悠閒氣氛,
有名的咖啡店Zeitgeist跟Umbria走幾步就到了.







[Hiram M. Chittenden Locks Fish Ladder]
結束出差行程後在西雅圖多留一天
感謝萱小姐好心收留還帶我到處吃吃逛逛
第一站就是到Ballard Locks Fish Ladder看鮭魚逆流而上,
看人生能不能因此獲得啟發XD
Hiram M. Chittenden(Ballard) Locks Fish Ladder其實是多個景點的集合,
首先, 這邊位於湖(Lake Washington)與海(Puget Sound)的交界,
漁船遊艇帆船等大大小小的船隻非常多,



如果船長的太高, 橋就得打開船才能通過.

這邊有船的電梯, 就是類似巴拿馬運河的機制,
湖跟海的水位高度不一樣, 船要出海的時候要先把閘門關起來,
然後把水位降到與海灣同高, 看起來就跟船隻坐電梯往下一樣感覺超酷,
還有划獨木舟的人也可以跟著出海噢~

除了讓船隻通過的閘門, 旁邊的區域則是讓鮭魚通過的地方
但是因為季節不對, 我今天一隻魚都沒看到(心碎)
倒是看到好幾隻肥滋滋的海豹快樂的在水壩面海的一側悠游
還有在岸邊等待狩獵的海鳥們


- 西雅圖逛逛 (2) Ravenna Park, Washington Park Arboretum, Kerry Park

Thursday, May 22, 2014

Snowboard再體驗, 迷人的小村莊 - 野澤溫泉

天氣漸漸熱起來, 讓我不禁懷念起年初在雪地裡快樂打滾的時光啊!
再撐半年雪季就來啦~ (握拳)

先來寫點跟滑雪無關的XD

去年在苗場住在王子飯店, 晚上就只能在旅館裡晃來晃去蠻空虛的
一方面也是因為摔太慘整個呈現殘廢的狀態連店都沒什麼逛,
只有藥妝店天天報到, 酸痛貼布不夠用啊XDD
相較之下, 野澤溫泉就有趣多了
不光是著名的13處的免費公湯, 溫泉街上還有各式各樣的商店
當地特產是一定有的, 溫泉玉米,包子,蘋果,醃漬醬菜等等好多好多
還有很多賣酒的商店, 除了當地特產的清酒(水尾好喝)也有葡萄酒(白酒很不錯)哦
最讓我瘋狂的是一家賣果醬跟的冰淇淋的店
冰淇淋真是好吃的不可思議(我選的是正常的巧克力跟奶茶,聽說清酒口味也很棒)


第一天滑雪結束後, 飢腸轆轆的我們經過大釜來到溫泉街
雪不停的下著, 泉水熱氣蒸騰, 霧濛濛的一片真美啊!

團員們立馬被玉米吸引, 點完老闆就把玉米放在籃子裡拿到旁邊的大釜煮哦!

回旅館的途中經過河原湯
我來泡的時候剛好都沒人超幸運的啊

第二天滑雪結束就沒下雪囉! 拍了一張清楚的茹釜(就在大釜旁邊而已)

天色漸暗街燈已亮起.

店家都會把把雪剷到邊邊比較安全喔!

我們來到野澤剛好是道祖神祭舉辦的前幾天, 隨處都可以感受到節慶的氣氛

要離開野澤的那天, 我起了個大早在村裡閒晃, 看到十王堂之湯但沒機會泡囉~

十王堂之湯旁邊可以煮溫泉蛋

再往前走一段, 路旁有道祖神祭的介紹

看到一個非常奇妙的東西, 回來一查覺得他應該是初燈籠,
家裡有長男誕生, 就會準備這個祈禱孩子能夠健康成長

布條上寫著滿滿的願望, 蠻像我們放天燈的感覺,
前面可以集拓印.

大湯商店街上有很多好東西(眼神閃爍)


最後的最後是我們住的旅館, 那魯灣的團都是住朝日屋

前廊

櫃檯的服務員人很親切,
我把梳子掉在樓上的溫泉(沒錯朝日屋裡面就有溫泉囉!)過了好久才想起來,
跟櫃檯人員一問馬上就找到了(感人)

都是日式榻榻米房間, 每天桌上都有小點心, 棉被超軟超好睡鑽進去就不想爬出來了
哦對了, 櫃子裡還有浴衣跟(超擋風)外套可以穿著去泡溫泉哦! 挺方便~


[野澤系列]
- Snowboard再體驗, 迷人的小村莊 - 野澤溫泉, 開滑囉! 大雪下不停~
ref:
- Go! 長野
- WWOOF Japan in 長野野沢温泉村 道祖神祭り
- WWOOF Japan in 長野野沢温泉村 道祖神祭りの御神木里引き
- WWOOF Japan in 長野野沢温泉村 外湯巡り~大湯
- WWOOF Japan in 長野野沢温泉村 外湯巡り~横落の湯、麻釜湯、上寺湯、新田の湯、松葉の湯、滝の湯
- WWOOF Japan in 長野野沢温泉村 外湯巡り~真湯、中尾の湯、河原湯、熊の手洗湯、秋葉の湯、十王堂の湯
- 朝日屋