astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

groonga冗長構成で運用改善した話

はじめまして。インフラ・開発部のtorigakiと申します。 入社して初めての投稿となります。よろしくお願いします。

弊社は検索エンジンとしてgroongaを使用しているのですが、今回はgroongaの運用改善をした話を書きたいと思います。

groongaの運用構成

groongaにはElasticSearchのように複数台でクラスタを構成する機能がないため、弊社では2台のgroongaサーバーを立ててマスター・スレーブの冗長構成をとっています。

しかしながら、MySQLのようにレプリケーション機能がないため、マスター・スレーブそれぞれに対してインサート処理を実施していました。 しかし、この方法ではマスター・スレーブ間でデータの整合性がとれなくなる可能性があります。

そこで、インサート後にマスターのデータをスレーブにリストアすることによりこの問題を解決させました。

以下に具体的な方法を記載してきます。

改善前のインサートとバックアップ取得の構成

改善前の構成は以下になります。

f:id:astamuse:20180327143656p:plain

  • マスターにデータをインサート後、スレーブにデータをインサート(インサートは週1回)。
  • バックアップの取得
  • マスターのgroongaプロセスを停止。(マスターを停止してもアプリからの接続はスレーブで賄うことができるので問題ありません)
  • gsutilコマンドでgroongaのデータファイルをアップロード。
  • アップロード完了後groongaプロセスを起動。

改善後の構成

f:id:astamuse:20180327143806p:plain

  • スレーブのデータはバックアップからリストアするためスレーブへの更新処理は停止。
  • GCSからスレーブにファイルコピー(リストア)する。
  • 各処理の処理工程をリアルタイムで確認するため、各処理の完了時にSlackに通知する。

リストア処理概要

  1. スレーブのgroongaプロセスを停止。
  2. 既存データファイルを削除(gsutil rsyncではファイル上書きされないため、データファイルは一旦削除します)。
  3. gsutilコマンドでデータファイルをダウンロード。
  4. ダウンロード完了後groongaプロセスを起動。

データチェック

  • スレーブのgroonga起動後にマスターとのデータ整合性をチェックします。
  • チェック方法は各テーブルのレコード数を取得しマスターとスレーブで一致するか確認をします。

全体の処理の流れ

  1. マスター機のデータ更新
  2. マスター機のgroongaプロセス停止
  3. マスター機のデータファイルをアップロード
  4. マスター機のgroongaプロセス起動
  5. スレーブ機のgroongaプロセス停止
  6. スレーブ機のデータ削除
  7. スレーブ機にデータファイルをダウンロード
  8. スレーブ機のgroongaプロセス起動
  9. マスターとスレーブのデータ整合性チェック

上記処理を順番に実行するバッチスクリプトを作成し、cronで定期実行するようにしました。

バッチスクリプトで工夫したこと

バッチスクリプトはシェルで書きました。 以下にスクリプトで工夫した点について記載していきます。

Slack通知スクリプト

Slackに通知するスクリプトはこちらのサイトを参考にさせていただきました。

エラー処理

エラー発生時にSlackにエラーコード込で通知されるように以下のように書きました。

# slack通知用スクリプト
SLACK_NOTI_PATH="bin/webhooks.sh"

# エラー処理
error() {
   echo ''$1'の実行結果はエラーコード('$2')でした。' | ${SLACK_NOTI_PATH}
   echo 'groongaバッチ処理: 異常終了' | ${SLACK_NOTI_PATH}
   exit 1
}

# backup
backup.sh || error 'backup.sh' $?

上記の場合、backup.sh でエラーが発生するとその旨をSlackに通知してexit 1 でスクリプトを停止させます。

gsutilのオプション

rsync に「-m」 オプションをつけることにより、並列に同期をさせるようにします。 サーバー負荷は上がりますが、ファイルのアップロード・ダウンロードが高速なります。 (ファイルのアップロード・ダウンロードじはgroongaプロセスを停止しているので、サーバー負荷が上がっても問題ありません)

gsutil -m rsync -r ${DIR} gs://${GCS_BACKET_NAME}/${HOSTNAME}/${DATE}/

データ整合性チェック

以下コマンドでマスター・スレーブ両方のgroonga各テーブルのレコード数を取得し、比較することによって同一データであるとことをチェックします。

 # テーブル一覧取得
for table1 in `curl -s http://${HOST}:${port}/d/table_list | jq -r -c '.[1][][1]' | sed -e '1,1d'`
do
      # カウント取得
      COUNT1=`curl -s  http://${HOST}:${port}/d/select.json?table=${table1}\&limit=0 | jq '.[1][][0][]'`
      echo ${port} ":" ${table} ":" ${COUNT} >> /tmp/${HOST}_count.txt
done

データはJSON形式で取得できるのでjqコマンドで加工し、取得した結果をファイル出力して最後にdiffコマンドで差分がないことを確認しています。

まとめ

今回の改善により、マスター・スレーブ間でデータの不整合が出ることを防ぐことができました。

また、スレーブへのデータ更新につきましても、リストア処理がインサート処理よりも高速なため改善することができました。

あと、各処理の完了時にSlackに通知するようにしたため、サーバーにログインして確認するという手間が省け、かつ処理異常時にも即座に気づくことができるようにもなりました。

弊社では引き続きエンジニア・デザイナーを募集中ですので、ご興味のある方は下からご応募いただければと思います。

Play Framework 2.6でFilterに触れてみる

f:id:astamuse:20170815181256p:plain

こんにちは、開発部のyanagita@リモートワーク中です。
宮崎はだんだん暖かくなり、もうすこしで桜が咲き出しそうな雰囲気が漂ってます。
桜といえば春、春といえば出会いの季節です。
アスタミューゼでは多くの方との出会いを心待ちにしています。

前回は弊社のWeb Framework asta4dのscala対応について書きましたが、今回は元に戻ってPlay Frameworkに関する事でFilterについて書きたいと思います。

前提

今回もPlay Framework 2.6のScala版で説明します。Javaの方はJavaに読み替えて頂ければとm(_ _)m

Play Frameworkの標準Filterと設定

Play Framework 2.6では前バージョンの2.5から新しいFilterが一つ追加になり、5つのFilter機能が標準で使用可能になっています。

  1. Gzip encoding filter
  2. Security headers filter
  3. CORS filter
  4. CSRF filter
  5. Allowed hosts filter
  6. Redirect HTTPS filter ← 2.6から追加

このうち、Security header filter、CSRF filter、AllowedHostFilterはデフォルトで適用されるFilterになります。
もし、残りのFilterも適用したい場合はapplication.confにFilterの追加定義を行います。

# conf/application.conf
# Gzip filterを追加する例
play.filters.enabled += "play.filters.gzip.GzipFilter"

逆にFilterを外す場合

# conf/application.conf
#CORS filterを削除する例
play.filters.disabled += "play.filters.csrf.CSRFFilter"

次に各Filterのについて触れます。

Gzip encoding filter

Gzip encoding filterは、レスポンスデータのgzip圧縮をサポートするFilterです。
詳細な設定はapplication.confで行います。

# conf/application.conf
play.filters.gzip.contentType {

    # Gzip圧縮を行なうContent-Typeを設定(デフォルト値:空)
    whiteList = [ "text/*", "application/javascript", "application/json" ]

    # Gzip圧縮を行わないContent-Typeを設定(デフォルト値:空)
    blackList = []
}

whiteList、blackListが共に空の場合、全レスポンスがgzip圧縮の対象となります。
whiteListのみ設定した場合、設定したContent-Typeのレスポンスのみがgzip圧縮の対象となります。
blackListのみ設定した場合、設定したContent-Type以外のレスポンスがgzip圧縮の対象となります。 whiteList、blackList共に設定した場合、whiteListが優先されblackListは適用されないのでご注意下さい。

Security headers filter

セキュリティに関するHeaderパラメータを自動設定します。
各セキュリティパラメータの設定は下記の通りです。

# conf/application.conf
play.filters.headers {

    # X-Frame-Options設定。nullの場合は指定なし(デフォルト値:"DENY")
    frameOptions = "DENY"

    # X-XSS-Protection設定。nullの場合は指定なし(デフォルト値:"1; mode=block")
    xssProtection = "1; mode=block"

    # X-Content-Type-Options設定。nullの場合は指定なし(デフォルト値:"nosniff")
    contentTypeOptions = "nosniff"

    # X-Permitted-Cross-Domain-Policies設定。nullの場合は指定なし(デフォルト値:"master-only")
    permittedCrossDomainPolicies = "master-only"

    # Content-Security-Policy設定。nullの場合は指定なし(デフォルト値:"default-src 'self")
    contentSecurityPolicy = "default-src 'self'"

    # Referrer-Policy設定。nullの場合は指定なし(デフォルト値:"origin-when-cross-origin, strict-origin-when-cross-origin")
    referrerPolicy = "origin-when-cross-origin, strict-origin-when-cross-origin"
}

CORS filter

CORS(Cross-Origin Resource Sharing)は、ドメインの異なるWebアプリケーション間のリクエスト制御設定を行います。
こちらもapplication.confで設定を行います。

# conf/application.conf
play.filters.cors {

    # CORSを適用するルートパス(デフォルト値:/)
    pathPrefixes = ["/"]

    # アクセス許可するドメイン。nullの場合は制限無し(デフォルト値:null)
    # 指定する場合は、["astamuse.com", ...]
    allowedOrigins = null

    # preflightリクエストで許可するメソッド。nullの場合は制限なし(デフォルト値:null)
    # 指定する場合は、["GET", "POST"]
    allowedHttpMethods = null

    # preflightリクエストで許可するHttpヘッダ。nullの場合は制限なし(デフォルト値:null)
    # 指定する場合は、["Accept"]
    allowedHttpHeaders = null

    # 許可する独自のHttpヘッダ。nullの場合は制限なし(デフォルト値:null)
    exposedHeaders = []

    # Credentials情報の使用の有無(デフォルト値:true)
    supportsCredentials = true

    # CORSメタデータのキャッシュ有効期間(デフォルト値:1 hour)
    # 設定値はduration
    preflightMaxAge = 1 hour

    # pathPrefixesに該当しないパスへのアクセス許可有無(デフォルト値:false)
    serveForbiddenOrigins = false
  }

CSRF filter

CSRF (cross-site request forgeries) は、 2.6からデフォルトFilterに追加されたので2.5以前から移行される場合は対応が必要になります。
CSRFのチェックはForm内にtoken情報を含む必要があります。一般的なtoken情報の設定方法は下記の2パータンがあります。

  • action属性のパスにtoken情報を付加するケース

template.scala.html

@import helper._

@form(CSRF(routes.SampleController.index())) {
    ...
}

HTML

<form method="POST" action="/index?csrfToken=1234567890abcdef">
   ...
</form>

 
* formタグ内にパラメータを付加するケース

template.scala.html

@import helper._

@form(routes.SampleController.index()) {
   @CSRF.formField
    ...
}

HTML

<form method="POST" action="/index">
  <input type="hidden" name="csrfToken" value="1234567890abcdef"/>
   ...
</form>

Allowed hosts filter

有効host以外からのアクセスを遮断するFilterです。遮断時はHTTP codeは400を返します。
有効hostの設定もapplication.confで行います。

# conf/application.conf
play.filters.hosts {
    # 有効なホスト名を設定(デフォルト値:localhost, .local)
    allowed = ["localhost", ".local"]
  }

Allowed hosts filterはデフォルト設定のFilterになります。confファイルを環境別に用意している場合に、有効hostの設定が漏れていると環境を変えた時にアクセスできない問題が発生するので注意が必要です。
開発中などホスト制限を行わない場合は、"."を設定することでホスト制限なくアクセスできます。
サブドメインも含めてた制限を行なう場合は、"." + ホスト名で行えます。
(".astamuse.com"と設定した場合、"astamuse.com"、"sub.astamuse.com"が該当します。)
ポートが変更されている場合は、ポート番号を含んで設定します。
(localhostで8080ポートの場合、"localhost:8080")

Redirect HTTPS filter

httpでのアクセスをhttpsにリダイレクトするFilterです。リダイレクト時のHTTP codeはデフォルトで308になります。
308であればリダイレクト時のリクエストメソッドが変更されないので予期しないリクエストケースはなくなりますが古いブラウザでは動かない欠点があります。
別のcodeへの変更は下記の通り設定します。

# conf/application.conf(デフォルト値:308)
play.filters.https.redirectStatusCode = 301

さいごに

至れり尽くせりのFilterが揃ってます。
Play Frameworkに触れた際にはFilterにも是非触ってみて下さい。

アスタミューゼではまだまだエンジニア&デザイナを募集しています。
気になる方は下からご応募下さい!新しい出会いをメンバー一同お待ちしてます!

特許とその制度について 拒絶査定後または特許登録後

お久しぶりです。主に特許関連のデータ処理を担当しているBTと申します。
今回は拒絶査定後または特許登録後の流れについてご説明したいと思います。
宜しくお願いいたします。

拒絶査定後

特許出願が審査によって拒絶査定された場合、出願人そこで諦める必要はありません。
出願人は拒絶査定について納得がいかない場合は、特許庁に対して拒絶査定不服審判を請求することが出来ます。
また、この拒絶査定不服審判の請求と同時に出願内容を補正する事も可能で、この場合は拒絶査定不服審判の前に前置審査が行われます。

前置審査

拒絶査定不服審判の請求と同時に出願内容の補正が有る場合、これが実質上出願人にとって最後の補正のチャンスであることから拒絶査定において指摘された拒絶理由に全てについて、その拒絶理由を解消するような大幅な補正を行うことが多くなります。
このため、わざわざ拒絶査定不服審判を行うよりも、すでに審査の過程で内容を理解しているはずの拒絶査定をした審査官が、拒絶査定不服審判を請求と共にした補正内容を確認することで拒絶理由が全て解消しているか否かの判断が容易に可能です。
そこで、拒絶査定不服審判を請求と共に補正があった場合は、拒絶査定をした審査官に補正内容を確認させ、特許査定が可能かどうかの判断をします。これを前置審査と言います。
拒絶査定をした審査官が補正後の出願内容で特許査定が可能と判断した場合は、拒絶査定不服審判を行うこと無く出願人に対して特許査定を通知します。

拒絶査定不服審判

拒絶査定不服審判の請求と同時に出願内容の補正が無い場合、又は補正があっても前置審査の結果拒絶査定をした審査官が特許査定は出来ない(拒絶査定における拒絶理由が解消していないか、補正によって新たな拒絶理由が生じた場合)と判断した場合は、拒絶査定不服審判が開かれます。
拒絶査定不服審判は、3人ないしは5人の審判官(審査官を長年勤めたベテランの特許庁職員の方々)の合議で、拒絶査定をした審査に誤りは無いか、拒絶査定不服審判を請求と共に補正があった場合はその補正後の内容で特許査定が可能か等を判断します。
審判官の判断の結果、主に審決(成立)、審決(不成立)、審査に差し戻しのいずれかの結果が出されます。
審決(成立)の場合は、特許査定と同じですので、出願人は特許登録料と3年分の年金を納めることで特許権を取得できます。
審決(不成立)の場合は、知的財産高等裁判所(東京高等裁判所の特別の支部で、知的財産権に関する裁判を専門的に受け持つ裁判所)に出訴することにより、出願人は裁判で争うことも可能です。
審査に差し戻しの場合は、審査が再度やり直しになります。これは審査の内容に不備や間違いがあった場合にそれを審査のやり直しで正すためですが、審判官が審査官を教育する目的で行われることもあり、出願人にとっては特許になった場合の特許権の有効期間がさらに減ってしまうことになります。

特許登録後

特許出願が審査によって特許査定された後に、出願人は特許登録料と3年分の年金を納めることで特許権を取得できます。
しかし、特許権は一度取得すれば出願から最大20年の間ずっと安泰というわけではありません。最初の3年間を除いてその後の毎年の年金を納め忘れると特許権は消滅しますし、競合者等の請求により特許権が消滅する事もあります。 それらを見ていきます。

特許異議の申し立て

特許登録がなされて特許公報が発行されてから6ヶ月の間は、誰でも自由にその特許登録された内容について異議申し立てをすることが出来ます。
6ヶ月の間に異議が申し立てられた(複数の場合も有り)場合、3人ないしは5人の審判官により異議申し立ての審理及び決定が行われます(異なる複数の異議申し立ての異議の内容が同じだったりすることもあるので、整理分類して同一内容の異議申し立て毎に行われる)。
異議申し立てを認める場合は、その特許は消滅し、特許権は最初から存在しなかったものと見なされます。
異議申し立ても認めない場合は、その特許はそのまま維持されます。

訂正審判

すでに登録された特許について、その特許権者は特許の内容について何らかの不備や後で説明する特許無効審判によって特許を無効にされる可能性が高い場合、特許権者はすでに登録済みの特許の内容について訂正を行う訂正審判を特許庁に要求することが出来ます。
上記の特許異議申して立てで申し立てたれた内容について特許の内容について訂正が必要と特許権者が判断した場合も同様です。
訂正審判は、3人ないしは5人の審判官により審理されて、審決(成立)となった場合は特許の内容は訂正後のものに変わります。但し、訂正後の内容は訂正前の特許の内容を超えるものであってはなりません。

特許無効審判

登録された特許に対して、利害関係者等がその特許がで無効であることを確認するために、特許庁に対して特許無効審判を請求することが出来ます。
無効となる理由は、審査等の過程で拒絶すべき事情があったにもかかわらず特許となった場合等が多いです。
特許無効審判は、3人ないしは5人の審判官により審理及び決定が行われます。
審決(成立)の場合は、その特許は無効となり、特許権は最初から存在しなかったものと見なされます。
審決(不成立)の場合は、その特許はそのまま維持されます。
どちらの場合でも、審決の結果に不服がある場合は、知的財産高等裁判所(東京高等裁判所の特別の支部で、知的財産権に関する裁判を専門的に受け持つ裁判所)に出訴することにより、特許権者と特許無効審判の請求人は裁判で争うことも可能です。

その他の裁判

特許権者はその特許発明について、原則その発明を実施する権利を独占的に有していますが、他人にまねされることもあります。
このような場合は、その他人に対して東京地方裁判所または大阪地方裁判所に対して特許侵害訴訟を提訴することが出来ます。
また、その他人の行為によって損害が生じている場合は、同じく東京地方裁判所または大阪地方裁判所に対して損害賠償請求を提訴することが出来ます。

まとめ

以上、拒絶査定後または特許登録後について説明をしてきました。
今回で「特許とその制度について」の一連のシリーズは終了となります。
長い間有難うございました。

最後になりましたが、 アスタミューゼでは現在、エンジニア・デザイナーを募集中です。 興味のある方はぜひ 採用サイト からご応募ください。

参考にした資料など

Copyright © astamuse company, ltd. all rights reserved.