Google App Engineのデータストアはバージョン管理いらないみたい?
さて、フロントエンドとなるサーブレット方面は問題はクリアしたのでお次はデータベースを試す。
RDBだとテーブル同士は対等な関係にあるが、GAEのDatastoreは階層構造を持ったものらしい。
昔のデータベースのようだ。階層がある分ましともいえるが。
データはマップのように好きなものを好きなように入れることが出来る。データベースが使えない環境でXMLを使ってやり取りしていたのを思い出した。XMLの属性みたいなもん。もっとも型はいくつかあるようだが。Integerがないのにちょっと驚いたり。Longって富豪的だなぁとかおもっちまった。
高レベルAPIと低レベルAPIとあるようだが、低レベルAPIでやる。JPA等データアクセスフレームワークもJDBCやSQLを理解していないとまともに使えないと思ったため。低レベルAPIという名前を見ると難しそうに見えるが、API一覧を見る限りそんなことはなさそうに見える。
まずは単純なCRUDを作ってみた。
大概の操作にレスポンスは20ms程度かかるとみてよいみたい。結構遅い。
続いてトランザクション。どうやらAppEngine側が楽観的ロックをやってくれるようでユーザーは例外を見るだけでよいようだ。ただし、トランザクションのリトライは全部自分で書く必要がある。この辺はRDB使うやつにしてもフレームワークがサポートしている例は少なく、みんな毎回自分で書いていると思うけどたるいよね。まぁいつもどおりなので自分で書けばいいだけだが。
というわけでMAX10回として処理してみた。countプロパティをインクリメントするだけの処理。
DatastoreService service = DatastoreServiceFactory.getDatastoreService(); int i=0; for(;i<10;i++){ try{ Transaction transaction = service.beginTransaction(); Key key = KeyFactory.stringToKey(parameter); Entity e = service.get(key); Long count = (Long)e.getProperty("count"); if(count == null){ count = 0l; } count++; try { Thread.sleep(5 * 1000);//5秒まて } catch (InterruptedException ex) { } e.setProperty("count", count); service.put(e); transaction.commit(); break; }catch(ConcurrentModificationException ex){ String text = String.format("%d回目 誰かがコミットしたんでもう一度トライ!",i+1); Logger.getLogger(Edit.class.getName()).log(Level.SEVERE, text, ex); } } if(i >= 10){ throw new RuntimeException("コミットが失敗!"); }
みんなこんな感じのコードよく書いてるよね。飽きたよね。
これは5秒かかるようにわざとしている。トランザクションがないと後から5秒以内にアクセスしたときに上書きになるが、他のアクセスからトランザクションが開始されているとCommit時にConcurrentModificationExceptionが発生するように出来てあるようだ。つまり、こちら側でバージョン番号の管理をする必要は一切ない。Datastoreサービス側で見えないところでバージョン番号を保持してやってくれているようだ。
もちろん、関係のないエンティティを更新した場合はリトライが発生することはない。
まぁここまでは予想通り。これから階層や複数のエンティティの処理等の検証をしていこう。