飴屋

mixiアプリ/日記15

新旧ID変換の計画

http://developer.mixi.co.jp/appli/spec/pc/restful-api-for-pc/sample/person-friends-api/

このページに具体的なソースコードつきでOAuthを使った署名付きリクエストの実装方法が掲載されていました。私はGoogle App EngineのPython版をサービスプロバイダにあてているのでPythonの項目を参考に実装を進めればよさそうです。これを契機にRESTful APIの本格導入に踏み切るのも面白いかもしれませんが、それはまた別の話です。

mixiアプリにあっては他のサービスとの連携やセキュアな通信が必ずしも保障されている必要のないものも多そうです。実際にRESTful APIを利用することなく、マイミク情報を取得したりデータの永続化を行うことも可能です。これらのアプリは今回のID仕様変更の影響を受けなそうです。ID仕様変更の影響を受けるのは、旧IDをキーに外部のサーバーから情報をひいたり、旧IDをキーに外部のサーバーの情報を更新して始めて発生するようです。では、新旧IDの変換APIを使ってやるべきことを明確にすると、主キーの旧IDを新IDに書きかえればよさそうです。

ここでいくつか問題になりそうなことを考えてみます。

  1. 旧IDが数値だったのに、新IDは13文字の文字列値になるので、データベースの設計によっては変換した値を入れられないんじゃないか?
  2. そもそも、各データの一意性を担保するためにメインのキーの値を変えられないようなデータベースだったら、IDの書き換えだけじゃだめじゃないか?
  3. 旧IDは本当に消してもいいのか?
  4. 変換作業中はデータベース上に新旧IDが併存してしまうが問題はないか?
  5. 変換作業が正常に完了したことをどうやって確認しようか。

これを以下に検証してみます。

  1. Google App Engineでは各フィールドに入れる値のタイプを指定します。データベースの検索性、ソート性を考えたらまぁ、当然ですね。でも、私はIDのフィールドを元から文字列値を入れるように設定していたので、問題にならなそうです。ひょっとしたら、他のプラットフォームでも同様なサービスを展開するかもしれないなと考えて、入力値を数値に制限するのを辞めておいたのが功を奏しました。
  2. Google App Engineでは特にそんな制限はなさそう。また変換作業中、旧IDと新IDがたまたま重複して一意制約を崩してしまうこともなさそうです。
  3. なんか怖いから新しくフィールドを用意して、旧IDを退避しておいた方がいいかな・・・。しかし、どうやら変換作業完了後にアプリの管理画面から新IDへの移行の完了をmixiに通知すると旧IDでの運用にはもう戻せないそうです。じゃあ、要らないのかな。しかし、一発勝負な感じに変な重圧を感じます。
  4. 新IDに移行したユーザに関しては、アプリの設定が旧IDを使用下にあると正しく動作しませんので、変換作業中はサービスを一時停止しておくべきでしょうね。時間がかかりそうなら停止期間を告知しておくのがよさそうです。
  5. 何か自分の思いもよらないところで不具合が発生する可能性が消しきれなくて怖いですね。データを目視できる範囲にも限りがあります。人事を尽くして天命を待つしかないのかな・・・。心配なら新しいアプリを作成して、動くかどうか先にテストしておくという方法もあります。ただ、新しいアプリには(個人開発においては)利用者ゼロの状態なので、複数人のアクションを伴う項目のテストが難しくなりそうです。

以上、肝に銘じて以下の工程で作業を進めようと思います。

  1. ID変換APIを仮組していくつかのサンプルケースで変換処理を試してみる
    1. 自分のID
    2. アプリ利用中のマイミクのID
    3. アプリ利用中のマイミクじゃない人のID
    4. アプリ未利用の人のID(かつて利用していた人)
  2. 一括してユーザのIDを変換するプログラムを実装する
  3. サービスの一時停止を告知する
  4. 一括変換プログラム実行
  5. 変換結果の検証
  6. 問題がなければ新IDへの移行完了をmixiに通知
  7. 自分のアカウントで動作を検証
  8. 一時停止の告知を取り下げる

新旧ID変換の実装

Google App Engineではどうもoauthのライブラリが標準的に用意されていないのかインポートに失敗したのでgoogle codeのoauthのプロジェクトからpython用のモジュールをダウンロードしたものを配置しました。あとはサンプルコードに従って、以下のようなコードを実行してみました。

from oauth import oauth
import urllib, urllib2

class ConvertID(webapp.RequestHandler):
def get(self):
consumer_key = '(アプリのキー文字列)'
secret_key = '(秘密鍵)'
url = 'http://api.mixi-platform.com/os/0.8'
params = {
'fields' : 'platformUserId',
'format' : 'json',
'xoauth_requestor_id' : '(自分の旧ID)'
}
consumer = oauth.OAuthConsumer(consumer_key, secret_key)
request = oauth.OAuthRequest.from_consumer_and_token(
consumer,
http_method='GET',
http_url=url+'/people/(自分の旧ID)/@self',
parameters=params
)
request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, None)
uri = '%s?%s' % (request.http_url, urllib.urlencode(params))
r = urllib2.Request(uri, headers=request.to_header())
try:
self.response.out.write( urllib2.urlopen(r).read() )
except urllib2.HTTPError, e:
self.response.out.write( e )

ここで定義しているConvertIDクラスに適当なURLを結び付けてアクセスしたところ、自分の情報の取得に成功しました。「consumer_key」「secret_key」の二つの情報は、アプリ管理画面から該当アプリ用の情報を調べてコピーします。

パラメータの「fields」には新IDを取得するために「platformUserId」を指定してあります。「format」には「json」を指定しましたが、これは指定しなくてもjson形式で値が返ってくるようでした。逆に「xml」とか別の形式を指定したら500エラーが返ってきましたよ。ってことはサーバ上でjsonをパースしないとですね。
「xoauth_requestor_id」は要するにRESTful APIでいうところの「@me」(主体となるユーザ)のIDを指定すればよいようです。まぁ、アプリの変換作業に関しては主体も何もないですが、この項目を省略すると認証に失敗します。

自分の情報は取得できましたので、他のユーザのケースについてはどうでしょうか。以下にまとめます。

アプリのオーナー(自分)取得OK
アプリのオーナーのマイミクでアプリの利用者取得OK
アプリのオーナーのマイミクでアプリを使ってない人取得失敗
アプリのオーナーのマイミクじゃないけどアプリの利用者取得OK
アプリのオーナーのマイミクじゃないしアプリも使ってない人取得失敗

まぁ、試すまでもありませんでしたが、アプリの利用の有無で情報が取得できるかどうかが決まっているという結果でした。2-legged OAuthで認証する以上、事前に「アプリを利用するから情報をアプリに渡してもいいよ」という許諾がなされていないとダメだよねというお話です。

それで、一つ問題になるのですが、一度アプリを始めたけど、つまらないからアプリの利用を停止しちゃった人というケースが考えられます。この人はアプリを現在利用していないので、変換APIを使って新IDを取得することはできません。この人が「やっぱあのアプリ、バージョンアップして面白くなったらしいから再開しよう」って思ったときに以前のデータを引き継げるような作りにしてあっても、以前とこれからではIDが変更されてしまっているため、引き継ぎはできなくなります。・・・まぁ、仕方ないか。mixiさんのポリシー的には、一度ユーザが辞めた以上、そのユーザに関するアプリのデータは即時消去という考え方なんでしょうか?mixi自体が提供する永続化データAPIもアプリを辞めたら消えちゃうのかな・・・?とにかく変換できない以上、変換できなかったデータには死んでもらうしかないですね・・・。

変換後の検証作業

IDの変換作業がトラブルもなく完了しました。IDを書きかえるだけでしたし、間違って旧IDを消してしまわない限り復旧できると思って、そこだけ気をつけて作業しました。その後、mixiアプリの管理画面より、新IDへの移行申請を行いました。「本当にIDの変換が済んでますね?それならここにチェックしてください。あっ、ちなみに申請前の状態には戻せませんからね〜♪」というメッセージに近いものが表示されるので、気圧されそうになりましたが、躊躇していても仕方がないので申請を済ませました。

次に、動作に問題がなさそうなことを軽く確認して、「仕様変更につきメンテナンス中」という表記をアプリ実行画面から外しました。これでユーザもまた自由にアプリを使えるようになったはずです。でも、まぁ一応全機能が正しく動作するか確認した方がよいでしょう。そしたら、いくつか動作がID移行前と異なる個所を発見しました。

まず一つは、mixiIDをJavaScriptでmixiネームに置換して表示する処理があったのですが、IDの形式が変更されていたため、「\d+」では正規表現でマッチしなくなっていただけでした。これは「\w+」でよいはず。13文字って決まってるので「\w{13}」でもよいかも。

次も似たような問題だったのですが、JavaScriptのコード中、IDを引数に渡す際に文字列としてではなく、数値として渡していう個所があったため、数値が入るべき部分に文字列が入っていてエラーになる部分がありました。引用符で囲えばこの問題はクリアです。

もう一つは・・・プロフィール画像のサムネイルのURLをそのまま表示したら、サイズが大きくなってました・・・???いつから?ID変換と関係あるの?78ピクセル四方ぐらいの画像だったのが、最大で180ピクセルに広がりましたよ。mixiアプリが提供するサムネイルURLが中サイズのものから大サイズに変わったのかもしれませんね。サムネイル画像を取得している部分に全てURLを変換する関数をつけて元に戻りました。画像のURLについては以下のページを参照してください。

http://developer.mixi.co.jp/appli/spec/pc/url_rule/

調べれば調べるほど、いろいろボロが出続けますね。永続化していた情報の一部が消えたような挙動をしていたので調べてみたら、旧IDを永続化情報に挿入している部分がありました。旧IDはもう使えないので何というかこれは完璧に失敗です。いやはや、mixiの提供する永続化データベースには揮発性の高いデータばかり入れていたので、消えてしまってもクリティカルじゃないんですが、これはうっかりしてました。残った旧IDもゴミ情報なので、消し込む処理を追加しないとだめですね。

もろもろ対処して、やっとIDの変換作業が完了しました。あと30年ぐらいはIDを変換しようとか言わないで欲しいですね。

Date:2011/7/3

日記一覧