短く可読性の高い一意のkey_nameの払い出し方法について

143 views
Skip to first unread message

Yoshinori Asakawa

unread,
Jul 28, 2010, 10:02:20 PM7/28/10
to Google-App-Engine-Japan
はじめまして、浅川と申します。

Kayフレームワークのマニュアルのダンプとリストアの項で触れられている
別のアプリへのリストア後の自動採番のidの重複を回避するため、
ワークアラウンドとして、全てのエンティティを key_name を指定して保存しようとしています。
http://kay-docs-jp.shehas.net/dump_restore.html

key_nameを指定して保存したエンティティは http://foo.com/<key_name>/ のような形で
ユーザからアクセスできるようにしたく、なるべく短く可読性の高い(※) 形式で生成しようと
allocate_idsで取得した自動採番のidを利用した、下記のようなメソッドを作成しました。
(※例えば「WwQdsV6ZSzS0KTWrTkdnsA」よりも「aa1234567」の方がユーザにとって使いやすいかと考えました)

しかしながら、ローカルの開発サーバで実行すると、開発サーバの再起動後に
allocate_idsが同じidを振り出してしまい、一意なkey_nameを作成することができませんでした。
本番のappengine環境では意図したように動いているように見えますが、
何かしらのきっかけで開発サーバと同じ事象が発生してしまうのではないかと懸念しています。

そこで以下2点を皆様にお伺いしたく、ご存知の方がいましたらご教示いただけませんでしょうか。

 1. http://foo.com/<key_name>/ のような形でユーザからアクセス出来るようにするための
   なるべく短く可読性の高い、一意のkey_nameを払いだす方法をご教示いただけないでしょうか。

 2. allocate_idsで採番したidを、idではなくkey_nameとして使用した場合
   同じidが再度払い出されてしまうことがあるのでしょうか?
   それとも開発サーバの固有の事象なのでしょうか?


class BaseModel(db.Model):
@classmethod
def create_new_entity(cls, sid=u"aa", parent=None):
# get unique ID number - I just get 1 here, but you could get
many ...
handmade_key = db.Key.from_path(cls.__name__, 1,
parent=parent)
new_ids = db.allocate_ids(handmade_key, 1)

# db.allocate_ids() may return longs but db.Key.from_path
requires an int (issue 2970)
new_id_num = int(new_ids[0])
key_name = sid+str(new_id_num)

# assign the new ID to an entity
new_key = db.Key.from_path(cls.__name__, key_name,
parent=parent)
if cls.get(new_key):
# if entity related to new_key already exists, raise
exception.
raise KeyNameAlreadyExistException(cls.__name__, key_name)
else:
instance = cls.get_or_insert(key_name=key_name,
parent=parent)

return instance

Takashi MATSUO

unread,
Jul 29, 2010, 11:24:21 PM7/29/10
to google-app-...@googlegroups.com
浅川さん
こんにちは

2010/7/29 Yoshinori Asakawa <yos.a...@gmail.com>:


> はじめまして、浅川と申します。
>
> Kayフレームワークのマニュアルのダンプとリストアの項で触れられている
> 別のアプリへのリストア後の自動採番のidの重複を回避するため、
> ワークアラウンドとして、全てのエンティティを key_name を指定して保存しようとしています。
> http://kay-docs-jp.shehas.net/dump_restore.html

現在の SDK でも、自動採番 id
の処理はまだちょっと甘いように思うので、別アプリにリストアする事が必要なケースでは、意味のあるワークアラウンドだと思います。

しかし、例えばリストア先の app id で、事前に充分大きな数まで allocate_ids
しておくなど、他のワークアラウンドもあります。もしかしたらこちらの方が簡単で良いかもしれません。

> key_nameを指定して保存したエンティティは http://foo.com/<key_name>/ のような形で
> ユーザからアクセスできるようにしたく、なるべく短く可読性の高い(※) 形式で生成しようと
> allocate_idsで取得した自動採番のidを利用した、下記のようなメソッドを作成しました。
> (※例えば「WwQdsV6ZSzS0KTWrTkdnsA」よりも「aa1234567」の方がユーザにとって使いやすいかと考えました)

この方法で、きちんとエンティティが分散するかちょっと不安ですね。要確認

> しかしながら、ローカルの開発サーバで実行すると、開発サーバの再起動後に
> allocate_idsが同じidを振り出してしまい、一意なkey_nameを作成することができませんでした。
> 本番のappengine環境では意図したように動いているように見えますが、
> 何かしらのきっかけで開発サーバと同じ事象が発生してしまうのではないかと懸念しています。

本番サーバーでは起きないはずです。起きたら大変なので。

> そこで以下2点を皆様にお伺いしたく、ご存知の方がいましたらご教示いただけませんでしょうか。
>
> 1. http://foo.com/<key_name>/ のような形でユーザからアクセス出来るようにするための
>  なるべく短く可読性の高い、一意のkey_nameを払いだす方法をご教示いただけないでしょうか。

allocate_ids を使わない方法としてはユニーク制約のついたカウンターとして別 kind を使う方法があるかと思います。

> 2. allocate_idsで採番したidを、idではなくkey_nameとして使用した場合
>  同じidが再度払い出されてしまうことがあるのでしょうか?
>  それとも開発サーバの固有の事象なのでしょうか?

本番では無いはずです。

--
Takashi Matsuo
matsuo....@gmail.com
Kay's daddy

谷村直樹

unread,
Jul 30, 2010, 5:13:09 PM7/30/10
to google-app-...@googlegroups.com
おはようございます。

開発サーバーと本番サーバーですこし試してみました。

■開発サーバーでは・・・
1) allocate_idsで取得したid値をkey_name値に指定した場合
Datastore Viewerで見ると・・・
・key_name値は指定したid値。
・id値は空っぽ。

開発サーバーを再起動すると・・・
・allocate_idsで取得されるid値は、先に取得したのと
同じ値が再度取得される。
・結果、そのid値をkey_name値に指定すると、先に登録
したデータが上書きされてしまう。

2) key_name値を指定しない場合
Datastore Viewerで見ると・・・
・key_name値は空っぽ。
・id値は自動的に設定された値。

開発サーバーを再起動すると・・・
・allocate_idsで取得されるid値は、保存されているid値の次の
id値から取得される。

■本番サーバーでは・・・
1) allocate_idsで取得したid値をkey_name値とした場合
Datastore Viewerで見ると・・・(開発サーバーと表示が違う・・・)
・ID/Name値として、"name=XXXX"という形で値が設定(表示)されている。

本番サーバーを再起動はできません・・・

2) key_name値を指定しない場合
Datastore Viewerで見ると・・・(開発サーバーと表示が違う・・・)
・ID/Name値として、"id=XXXX"という形で値が設定(表示)されている。

本番サーバーを再起動はできません・・・


結局、開発サーバーだと、起動している間はアロケートしたidの情報を
保持していて、再起動後はid値を見て、idの開始値を決めている感じに
みえますね。

本番サーバーは・・・こんな感じになりましたということで・・・。

Yoshinori Asakawa

unread,
Jul 31, 2010, 7:55:27 PM7/31/10
to Google-App-Engine-Japan
松尾さん
谷村さん

おはようございます。浅川です。
ご回答ありがとうございました。

> しかし、例えばリストア先の app id で、事前に充分大きな数まで allocate_ids
> しておくなど、他のワークアラウンドもあります。もしかしたらこちらの方が簡単で良いかもしれません。

なるほど、allocate_idsをこのように使う方法には思い至りませんでした。
確かにこれはシンプルでいいですね。

> この方法で、きちんとエンティティが分散するかちょっと不安ですね。要確認

キー値がシーケンシャルに発行されるため、エンティティの分散に偏りが発生する
可能性があるということでしょうか。このような点も注意が必要なのですね。

> allocate_ids を使わない方法としてはユニーク制約のついたカウンターとして別 kind を使う方法があるかと思います。

この方法は id の払い出しの際に別 kind がボトルネックになりそうなことを懸念していました。
id の払い出し方法として1つ思いついたのですが、「マイクロ秒単位の現在時刻 * 1000 + ランダムな数字(0~999)」
をBase62でエンコードしてみるのもよさそうです。(出力例:tGYFbyIpLGb)
「aa1234567」のような形式に比べて可読性はかなり落ちるので、今回の目的とは外れてしまいますが
単純に短めの id を払出したいだけであれば、こちらの方法も使えそうです。

> 開発サーバーと本番サーバーですこし試してみました。

検証ありがとうございます。私の方でも同様の動きを確認できました。
開発環境で再起動の度に id が重複してしまうのは困りものですね。。
都度データを全て入れなおすのでは開発効率が悪すぎるので、残念ながらこの方法は見送りにします。。

Yoshinori Asakawa
> > 2010/7/29 Yoshinori Asakawa<yos.asak...@gmail.com>:
> >> はじめまして、浅川と申します。
>
> >> Kayフレームワークのマニュアルのダンプとリストアの項で触れられている
> >> 別のアプリへのリストア後の自動採番のidの重複を回避するため、
> >> ワークアラウンドとして、全てのエンティティを key_name を指定して保存しようとしています。
> >>http://kay-docs-jp.shehas.net/dump_restore.html
>
> > 現在の SDK でも、自動採番 id
> > の処理はまだちょっと甘いように思うので、別アプリにリストアする事が必要なケースでは、意味のあるワークアラウンドだと思います。
>
> > しかし、例えばリストア先の app id で、事前に充分大きな数まで allocate_ids
> > しておくなど、他のワークアラウンドもあります。もしかしたらこちらの方が簡単で良いかもしれません。
>
> >> key_nameを指定して保存したエンティティはhttp://foo.com/<key_name>/ のような形で
> >> ユーザからアクセスできるようにしたく、なるべく短く可読性の高い(※) 形式で生成しようと
> >> allocate_idsで取得した自動採番のidを利用した、下記のようなメソッドを作成しました。
> >> (※例えば「WwQdsV6ZSzS0KTWrTkdnsA」よりも「aa1234567」の方がユーザにとって使いやすいかと考えました)
>
> > この方法で、きちんとエンティティが分散するかちょっと不安ですね。要確認
>
> >> しかしながら、ローカルの開発サーバで実行すると、開発サーバの再起動後に
> >> allocate_idsが同じidを振り出してしまい、一意なkey_nameを作成することができませんでした。
> >> 本番のappengine環境では意図したように動いているように見えますが、
> >> 何かしらのきっかけで開発サーバと同じ事象が発生してしまうのではないかと懸念しています。
>
> > 本番サーバーでは起きないはずです。起きたら大変なので。
>
> >> そこで以下2点を皆様にお伺いしたく、ご存知の方がいましたらご教示いただけませんでしょうか。
>
> >> 1.http://foo.com/<key_name>/ のような形でユーザからアクセス出来るようにするための

najeira

unread,
Aug 1, 2010, 1:47:28 AM8/1/10
to Google-App-Engine-Japan
こんにちは。najeiraです。

> 2. allocate_idsで採番したidを、idではなくkey_nameとして使用した場合
>   同じidが再度払い出されてしまうことがあるのでしょうか?
>   それとも開発サーバの固有の事象なのでしょうか?

これは本番では発生しないはずです。
allocate_idsでIDを取得して捨てていくというのを試しましたが、
どんどん数値が大きくなりました。


> 1. http://foo.com/<key_name>/ のような形でユーザからアクセス出来るようにするための
>   なるべく短く可読性の高い、一意のkey_nameを払いだす方法をご教示いただけないでしょうか。

松尾さんの仰る「リストア先の app id で、事前に充分大きな数まで allocate_ids」
というのが良い手段だと思いますが、開発と本番で動作が統一できないということで
あれば、IDを発行するためのカウンタを実装するとよいのではないでしょうか。

シャーディングカウンタの応用のような感じで。

class Sequencer(db.Model):
num = db.IntegerProperty()

@classmethod
def next(cls, name):
shards = 100
id = random.randint(1, shards)
key = '%s%d' % (name, id)
def txn():
obj = cls.get_by_keyname(key)
if obj:
obj.num += shards
else:
obj = cls(num=id)
obj.put()
return obj.num
return db.run_in_transaction(txn)

unique_id = Sequencer.next('Hoge')


上記のコードなら、シーケンサが100個ありますので、
ID発行が衝突する可能性はさがります。

要求されるスループットに応じてシャード数を増やせます。
(ただし途中でシャード数は変更できない)

なお、発行されるIDの数値は、多少順序が前後します。

# コードはいま書いたので、テストはしておりません

kazuo

unread,
Jul 31, 2010, 11:30:28 PM7/31/10
to Google-App-Engine-Japan
みなさま

こんにちは。山崎です。

>  1. http://foo.com/<key_name>/ のような形でユーザからアクセス出来る
> ようにするためのなるべく短く可読性の高い、一意のkey_nameを払いだす方
> 法をご教示いただけないでしょうか。

ぼくはこのような場合、UUIDを使っています。
http://www.python.jp/doc/2.5/lib/module-uuid.html
短く可読性の高い、とは言えませんが一意である事を確信できますし、いろい
ろ考えなくても楽に取得できると思います。
Reply all
Reply to author
Forward
0 new messages