読者です 読者をやめる 読者になる 読者になる

JSUG勉強会 2017年その4 ~ Springを使ったバッチアプリケーション特集 に行ってきた

JSUG勉強会 2017年その4 に行ってきました。簡単に所感をまとめます。

jsug.doorkeeper.jp

今回のテーマは Spring を使ったバッチアプリケーション特集です。

バッチは地味だが役に立つ

  • 処理モデル
    • タスクレットモデル
      • 1ステップに対して1タスクレットの構成
      • シンプル
      • 処理をまとめたい場合など
    • チャンクモデル
    • 処理モデルはジョブごとに選択する
  • 起動方法
    • 同期実行
      • スクリプトなどからジョブを叩く
      • 複数のジョブを組み合わせるケースなど
    • 非同期実行
      • Webコンテナ経由でジョブを叩く
      • バッチ実行に即時性が求められるケースなど (ユーザ操作とか)
  • データ入出力
    • ファイル/DB
    • Bean定義の中でファイル読み込みやDB書き込みの定義が書ける
    • MyBatis
  • フロー制御
    • Job と Step でフローを制御する
    • シーケンシャルフロー
      • 後続ステップを指定してステップを繋げていく
      • リターンコードで分岐を定義できる
    • step の同時実行 (並列処理/多重処理) が可能
  • ジョブ管理機能
    • 障害発生時のリスタート制御
    • 二重起動防止
  • Webコンテナによる非同期実行の課題
  • ファイルアクセスにおける課題
    • Spring Batch では1行を1レコードとして扱う
    • マルチバイト文字が正しく処理できない
    • CSVエスケープ処理してない (クォートで囲った中のカンマが区切りとして見なされる)
  • TERASOLUNA Batch Framework

  • 業務DBを別途用意する場合、2フェーズコミットが必要になるか

    • ジョブの実行結果は JobRepository に入っているのでどこまで処理できたかは分かるはず
    • 2フェーズコミットにしても不整合が発生する可能性はある
    • 2フェーズコミットは使わず、不整合が発生した場合の運用対処を用意するのもアリ

www.slideshare.net

  • JJUG CCC 2017 Spring の再演
  • 現在のレポートシステムは第3世代
    • RabbitMQ + Spring Cloud Stream で構成されている
    • 第2世代が Spring Batch アプリだったので Spring Boot アプリに移行し易かった
    • Spring Cloud では Spring Boot アプリであることが前提
  • Spring Cloud Stream
    • 各サービスがマイクロサービス
      • サービス間はメッセージで連携する
    • Publisher / Subscriber
  • ポーリング型からイベントドリブンにすることでスループットが向上
  • Source (Publisher) -> RabbitMQ -> Sink (Subscriber)
    • output と input 両方で同じ destination を指定する
      • ただし、このままだとスケールしたすべての Sink にジョブが送られてしまう
    • consumer groups でグループ化する
      • グループ内の1つの Sink にジョブが送られるようになる
  • 大量のジョブを処理すると、Job管理テーブルに同時に大量のINSERTが発生する
    • RDBがハングした結果、RabbitMQにメッセージが戻ってくる
      • 空いてる Subscriber に再送する
    • デフォルト3回リトライしてだめだったらメッセージが消える
      • とりあえず上限値を5に上げる
    • Dead Letter Queue
      • 5回リトライしてだめならエラー処理用のサーバにメッセージを送る
      • Dead Letter Queue からメッセージを再送信する
  • API, Batch の Spring Boot 化
    • API : war -> jar (with Jetty)
    • Batch : 1アプリを1jarにする (機能単位でjarにする)
      • 常駐バッチは spring.batch.job.enabled=false にする (起動時にジョブを実行しない)
    • Spring Boot アプリを Web アプリとして起動しない
      • ポートの管理が大変
      • 1サーバに複数プロセスとする
      • spring.main.web-environment=false を設定する
        • Spring Boot 2.0 以降は非推奨
        • 代わりに WebApplicationType を使う

TERASOLUNA は、フレームワーク本体だけでなくドキュメントも充実してるので、とても助かります。これまで Tasklet 構成のバッチしか作ったことがないので、機会があればチャンクモデルも使ってみたいです。Spring Batch は、その処理モデルに合わせて設計するのがキモな気がする。(特にチャンクモデルの場合)

Spring 徹底入門の Spring Batch 編をダウンロードしたけど、まだちゃんと読めてないので勉強しよう。

これまで、Spring Cloud を使ったことがないので、JSUG の勉強会で Spring Cloud 関連のテーマになると理解が追い付かない…。

フレームワークや開発ツールを取り入れるときに気を付けたいこと

数年ほど前から JavaScript フレームワークの話題をよく見かけるようになり、同時に各所で適用事例が挙がるようになりました。

ところが、最近になって、JavaScript フレームワークをやめてサーバーサイドレンダリングに移行した、みたいな話をいくつか見かけることがあります。

なんとなく、揺り戻し みたいな現象が起きているように思えます。

なぜそういうことが起きるのか、このあたりの考えを簡単にまとめてみます。もちろん、すべてのケースに当てはまるわけではないですが、身近な現場で似たような状況を見かけたもので…。

まず、何かしらのフレームワーク (アプリケーションフレームワークや開発手法など) や開発ツールを取り入れるとき、それによって解決したい問題や課題はなにか を明確にする必要があると考えています。

で、冒頭で挙げた近年の JavaScript フレームワーク事情においてしばしば見かけるように思えるんですが、新しい技術である、流行りの技術であるという単純な理由で「とりあえずこれを使ってみるか…」みたいな判断を下しがちになっているような気がしています。もちろん、流行りに追従して新しい技術をおさえることは大切ですし、世間でいろいろ取り沙汰される故、焦りを覚えることもあると思います。私もそうです。

ただ、ここでよくないと思うのは、いわゆる 手段が目的になっている ような状況です。

しかし、新しいことを取り入れた直後というのは、割と上手くいっているように見えるものです。不思議なことですが。ところが、解決するべき問題や課題を明確にしていないと、いずれは頓挫して上手くいかない状況に陥り、方針を見直す必要が出てきたりします。これが揺り戻しの正体ではないかと考えています。(多くの場合、方針を見直したところで実行に移せないことがあるかもしれませんけど…)

解決したい問題や課題はなにか、不安なことはなにか、どうやったらその不安を取り除けるか、をあらかじめ明確にしておくと、どんなフレームワークや開発ツールが適しているのかが判断し易くなると思います。もしかしたら、全体適用ではなく部分適用で十分かもしれないし、いくつかの手法を組み合わせて取り入れることでより効果を生むかもしれません。

また、このようなプロセスを経て改善したものは、メンテし易く、長い期間にわたって効果を発揮するのではないかと思います。

このように、手段ではなく目的を中心に据えて、解決すべき問題や課題を解決することによって不安を取り除くことが、フレームワークや開発ツールを取り入れることの意義である、というのが最近の私の考え、というか認識です。当たり前のことなんだと思いますが、自戒を込めて。

Eclipse の Team Synchronizing パースペクティブが開かない

Eclipse でプロジェクトのメニューから Team > Synchronize with Repository とすると、今まで Team Synchronizing パースペクティブが開いていたが、新しい開発環境を構築したら、ふとした拍子にパースペクティブが開かなくなってしまった。そんなときのメモ。

※ Mars.2 Release (4.5.2)

  1. Window > Preferences > Team
  2. Open the associated perspective when a synchronize operation completesAlwaysPrompt をチェックする

Never がチェックされているとパースペクティブではなくビューとして開く。

おそらく、最初にプロンプトが出たときに間違えて Remember my decision をチェックして No をクリックしたと思われる。そうすると Never で設定される模様。まぁ、ビューでもいいんだけど、パースペクティブの方が見慣れているので。

GitHubとクラスメソッドの勉強会に行ってきた #github_method

GitHubとクラスメソッドの勉強会に行ってきました。簡単に所感をまとめます。

classmethod.connpass.com

今回のテーマは「GitHub x AWS の最新 DevOps 事情」です。

  • DevOps はインフラ自動化やリリース自動化など、ひとによって捉え方は違う
  • 価値を無駄なく遅滞なく届けることが重要
  • かつてはスコープに対してスケジュールやリソースを決めていた
  • 近年はこの関係が逆転して、スケジュールとリソースがある上でスコープを決める
  • VCSによるコラボレーションをやりやすくする
    • 中央集権型から分散型へ
  • CI は必須のベストプラクティス
  • Continuous Delivery : 継続的にデリバリーする
  • Continuous Deployment : 継続的にデプロイする
  • GitHub の開発者は世界中にいる
    • それでも1日のデプロイは100回に及ぶことも
    • それを支えるのは GitHub FlowHubot (ChatOps)
  • master を常にデプロイ可能に保つ (本番とイコール)
    • 本番にデプロイして問題がないものを master にマージする
      1. master から feature を作ってコミットを加える
      2. PR を作って開発者みんなでレビューする
      3. レビューで問題がなければ PR ブランチを本番にデプロイする
      4. 本番で問題がなければ master にマージする
    • 本番にデプロイした PR ブランチに問題があれば master をデプロイする
  • master にマージしてからデプロイすると問題があった場合に master が不安定な状態になってしまう
  • 1回本番にデプロイされると、問題ないことが保証されるまでは次以降のデプロイはロックされてキューイングされる
  • Chat は Shared Console である
    • オープンなコンソール (みんなで見ることができる)
    • 誰がやっても同じ結果になる
    • ゆくゆくは AI が絡んでくるかな?

GitHub Flow は2011年頃に出てきたものですが、中のひとたちは割と早い段階で今の運用に変えていたようです。当時、日本にはそのあたりの情報があまりなかったため、池田さん自身もチーム開発実践入門を執筆した段階ではご存知なかったらしいです。
また、1回でデプロイされる変更ボリュームは数十行程度。敢えて小さくしている。1日のトータルは3000行くらいだが、一気にデプロイするのではなく、30行を100回に分けてデプロイするような感じ。とのこと。

www.slideshare.net

  • DevOps の目的はビジネスに使う時間を増やすこと
    • 決まりきった運用や手運用を自動化する
    • 障害対応の時間を減らす
  • CodeDeploy ではデプロイとプロビジョニングを混ぜないように注意
    • プロビジョニングは OS やミドルウェアのための設定
    • プロビジョニングとデプロイではライフサイクルが違う
    • CodeDeploy ではプロビジョニングはしない
  • AWS を使うならマネージドサービスを使うことを第一に検討する
    • 安定性やサービス連携、運用負荷を減らす観点から
    • 標準のサービスでは要件に合わない場合には自前で作る
  • GitHub Enterprise を使う場合は標準で提供している設定や機能を使う
    • アップデートで /data/user 以外が上書きされるため
    • 運用負荷を減らす

AWS については圧倒的経験不足のため、CodeStar をはじめ、多くの DevOps 関連のサービスが提供されていることを初めて知りました…。

正規表現メモ

CODEPREP で正規表現を学びつつ、練習問題の3パターンをメモる。

codeprep.jp

電話番号にマッチさせる

  • 電話番号は3ブロックの数字の並びで構成される
  • 各ブロックの桁数は 3桁、3桁 or 4桁、4桁
  • 各ブロックは - または ( or ) で区切られる
  • 先頭のブロックの数字は0で始まる
  • 先頭または末尾に空白や数字以外があることは許容しない
/^0\d{2}[-(]\d{3,4}[-)]\d{4}$/

URLからホスト名を抽出する

  • URLは http:// または https:// で始まる
  • ホスト名の後ろに / がない場合もある
/https?.{3}([^/]*)/

http:// または https:// で始まるということは、http の後ろに s があってもなくてもよいということと同義である。正規表現では https? で表す。

ホスト名は、http(s):// 以降で最初に現れる / より前の部分を抽出する。つまり、http(s):// 以降で / 以外の文字が続く限りキャプチャする。正規表現では ([^/]*) で表す。

HTML要素の属性値を抽出する

  • a 要素から href 属性の値をキャプチャする
  • = の前後には空白が入ることがある
  • 属性値は " または ' で括られる
  • href 以外の属性が定義されることがあり、順序は不定である
/<a\s*.*href\s*=\s*(["'])(.*?)\1.*/

空白があるかないかは、正規表現では \s* で表す。

シングルクォートとダブルクォートの両方に対応する場合は ["'] で表す。また、後ろのクォートは前のクォートと同一である必要があるため、前のクォートをキャプチャして \1 で後方参照する。キャプチャを複数定義するため、抽出結果は配列の2番目(\2)を取る。

href 以外の属性が存在し得るため、同じクォートが複数回現れることがある。キャプチャは (.*?) で最短マッチにする。

Cookpad Tech Kitchen #7 に行ってきた #cookpad_tech_kitchen

Cookpad Tech Kitchen #7 に行ってきました。簡単に所感をまとめます。

cookpad.connpass.com

今回のテーマは『理想の開発現場の「ふつう」のお話』です。資料はこちら。

  • 開発者もテストエンジニアも品質を向上するという共通の目的を持つ
    • テストの現場の理想形のひとつ
  • 日々の小さな会話がよいチームを作るのではないか
    • フレーズ→振る舞い→価値観
  • 「うまくいったらどうなるの?」
    • ゴールまでの仮説を立てる
    • うまく迷える (ゴールまでのステップが分かっていれば迷っても安心)
  • 「なんでやるんだっけ?」
    • 作業の目的や作業自体の意味を考える
    • やりすぎを抑える
  • 「やりたくないの?」

    • 「やりたいくない」がチームに認められる
    • やりたくないことをチームで共有する
  • 「なんでやるの?」という問答は1対1かもしれない

    • その場にいるみんなが考えるきっかけになる
    • それぞれの認識にズレがあるのかないのかを知ることができる

「なんでやるの?」はやりすぎを抑えられる、というのはよく理解できます。何かに没頭したり集中するあまり物事の優先度の意識が薄れて、つい本質から外れたり、ときには手段が目的になっていたりすることがあります。そういうときに「なぜやるのか?」を自身に問いかけることで、目的を再認識して軌道修正をかけることができると思います。

今回、「明日から使える」というのが登壇者のテーマだったようです。で、実際にすぐに現場に適用できるかどうかというと、当然現場の状況にもよりますが、大半は難しいかも、というのが正直な印象です。

どなたかの質問でも話が挙がりましたが、今回の話のような内容がチームのメンバーに対して効果を発揮するのは、ある程度完成されているチームに限るような気がします。

事業会社では必然的に似たような目的意識を持ったメンバーが集まるのかもしれませんが、受託開発を行う会社では多様なメンバーでチームが構成されることが多いかと思います。(偏見かもしれませんが…)
当然、メンバー全員が当事者意識を持っているかというと決してそうではないだろうし、そのようなチームに対してどのような振る舞いがよい作用をもたらすか、ということを一様に述べることはなかなか難しいのではないかと思います。そのようなチームでは、まずは目的意識を共有できるようにチームの「風土作り」から始める必要があると考えます。しかし、今回はそのあたりは前提から外れているように感じました。

理想の開発現場の「ふつう」のお話というテーマでしたが、そこで「ふつう」に行われていることは、多くの「理想とは程遠い開発現場」にとっては他でもない『理想』そのものであるわけです。

ただまぁ、こういう話をきちんと自分の中で腹落ちさせてチームに共有するところから始めることが、よいチームの風土作りの第一歩になるんだろうと思います。そういう意味でいろいろな気付きが得られたとてもいい機会でした。

Haskell を使ってみる 7 (ガード)

前回の続き

Haskell を使ってみる 6 (パターンマッチ) - kntmr-blog

ガード

ガードは引数の値が満たす性質で処理を分岐させるときに使う。パイプ文字 (|) と条件式と関数本体を組み合わせて記述する。(パターンは引数の構造で条件を分岐させるもの)

-- 階乗を求める関数
fact :: Integer -> Integer
fact n
    | n < 0 = error "error!!"
    | n == 0 = 1
    | otherwise = n * fact(n - 1)

*Main> fact 3
6

where

where キーワードは計算結果を変数に束縛するときに使う。where で定義した変数のスコープはその関数内のみ。

-- where キーワードを使わない場合
totalCheck :: Integer -> Integer -> String
totalCheck x y
    | x + y < 10 = "less than 10"
    | x + y < 100 = "less than 100"
    | otherwise = "a large number"

-- where キーワードを使う場合
totalCheck' :: Integer -> Integer -> String
totalCheck' x y
    | total < 10 = "less than 10"
    | total < 100 = "less than 100"
    | otherwise = "a large number"
    where total = x + y

where キーワードの中でパターンマッチを使うことができる。

initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
    where (f:_) = firstname
          (l:_) = lastname

let

let 式では関数のどこでも変数を束縛することができる。let 式自身が式であり、変数の束縛は局所的でガード間で共有されない。(where キーワードは関数の終わりで変数を束縛する)

let で変数に束縛し、続く in に式を記述する。

-- ローカルスコープに関数を作ることができる
Prelude> [let square x = x * x in (square 2, square 3, square 4)]
[(4,9,16)]

-- セミコロン区切りで複数の変数に束縛できる
Prelude> (let a = 10; b = 20; c = 30 in a * b * c, let foo = "Hello"; bar = "Haskell" in foo ++ " " ++ bar)
(6000,"Hello Haskell")

-- let 式とパターンマッチでタプルを要素に分解して変数に束縛できる
Prelude> (let (a, b, c) = (1, 2, 3) in a + b + c) * 100
600

リスト内包表記と let 式の組み合わせ。述語のように使っているが、フィルタしているのではなく計算結果を変数に束縛している。

-- リストから値を受け取り、計算結果を area に束縛する
calcCircleArea :: [Double] -> [Double]
calcCircleArea xs = [area | r <- xs, let area = r * r * 3.14]

case

case 式ではコード中のどこでもパターンマッチを使うことができる。変数で指定した値に基づいてコードブロックを評価する。case 式は関数の引数に対するパターンマッチと似ている。(実際、case 式の糖衣構文になっている)

case 式に合致するパターンが見つからない場合はランタイムエラーが発生する。

-- 関数の引数に対するパターンマッチ
head' :: [a] -> a
head' [] = error "empty list"
head' (x:_) = x

-- case 式
head'' :: [a] -> a
head'' xs = case xs of [] -> error "empty list"
                       (x:_) -> x

引数によるパターンマッチは関数定義のときしか使えない。case 式では、式の途中でパターンマッチを使うことができる。

checkList :: [a] -> String
checkList list = "This list is "
               ++ case list of [] -> "empty."
                               [x] -> "a singleton list."
                               xs -> "a longer list."

-- case 式の代わりに where を使う場合
checkList' :: [a] -> String
checkList' list = "This list is " ++ what list
    where what [] = "empty."
          what [x] = "a singleton list."
          what xs = "a longer list."

今回はガードのあれこれについて。