[mayaa-user:1006] Re: builderをカスタマイズしてexec processorを自動挿入したい

Susumu ISHIGAMI [E-MAIL ADDRESS DELETED]
2014年 2月 9日 (日) 18:03:06 JST


石上です。

sugaさん
ご回答ありがとうございます。


> それなら、そのid指定でデータを取得する処理の、DBへアクセスする部分で
> Pageスコープにキャッシュするようにする、ですかねえ。

このようなことを既に行っていたのですが、
キャッシュが存在しないときの初回検索が問題でした。

現状メモリ上にキャッシュが存在する2回目は高速なのですが、初回レンダーだけがなぜかかなり遅い状況です。

大ざっぱにいうと、初回のSQLが
select * from item where item_id = 1;
select * from item where item_id = 2;
select * from item where item_id = 3;
:
:
select * from item where item_id = 150;
のようになっていました。
そこで上記がselect * from item where item in (1,2,3.....150)になるようにしてみましたが、
残念ながら思ったよりも高速化しませんでした。
きちんとプロファイリングせずログからの憶測でパフォーマンス対策をしてしまう悪い例を実践してしまったようですね。
ただ、いただいたご回答で、BuilderやTemplateProcessorTreeの概念が勉強できました。

もう一度ボトルネックになっている箇所を解析してみます。
そこで何かMayaaの標準的なことがわかったら別件で報告させていただきます。

この質問としては、以上で終了と致します。
いろいろありがとうございました。

2014年2月9日 0:05 suga <ko.suga @ gmail.com>:
> suga です。
>
> 2014-02-08 16:17 GMT+09:00 Susumu ISHIGAMI <susumu.ishigami @ gmail.com>:
>> 具体的には、あるidを使用するとDBからデータをロードし、そのタグの子要素で
>> ロードしたデータが使えるというものを提供したのですが、そのidをページ内で150以上使用されてしまい、
>> キャッシュなどがない初期表示時に150本のSQLが発行されてしまう問題がありました。
>
> それなら、そのid指定でデータを取得する処理の、DBへアクセスする部分で
> Pageスコープにキャッシュするようにする、ですかねえ。
>
> テストしやすさやメンテナンス性を考えると、可能な限りシンプルな解決策の方が
> 良いかと思います。
>
> Processorを作っているのであれば、パフォーマンス的(速度、消費メモリとも)に
> Javaのコードで解決する方がRhinoに任せるより良いです。
>
>
>> InjectionResolverとTemplateBuilder(SpecificationBuilder)の役割分担が理解できていなかったのですが
>> BuilderはXMLファイルをパースするところから始まって、TemplateProcessorのツリーを生成するのがゴールであって、
>> InjectionResolverやSpecificationNodeはその過程で作られるということだと理解しました。
>
> InjectionResolverは、m:id とか m:xpath とかのように、テンプレートのどこに
> どのProcessorを対応させるか、ということを解決するものです。
>
> TemplateBuilderからInjectionResolverを使う、という感じですね。
>
>
>> TemplateBuilderImplのafterBuildでdoInjectionが行われることによって、
>> SpecificationNodeからTemplateProcessorツリーが作られるので、
>> TemplateBuilderImplをカスタマイズする場合で、SpecificationNodeをいじる場合は
>> super.afterBuildの前に行うべきであり、Processorのツリーを直接いじるならどちらでも構わない。
>> そんなところでしょうか。
>
> 大体そんなところです。
>
> --
> suga ( ko.suga @ gmail.com )
>
>
> 2014-02-08 16:17 GMT+09:00 Susumu ISHIGAMI <susumu.ishigami @ gmail.com>:
>> 石上です。
>>
>> sugaさんご回答ありがとうございます。
>>
>>>これだけ読むと Mayaa(一般にはjsp)に渡す前にServletで全部結果をまとめておくのが良いのでは、と思うのですが。
>>>全部のデータを持つのが現実的では無いサイズの場合はキャッシュできないでしょうし。
>>
>> まずは前提が省略されていたことをお詫びします。
>> せっかくですので、公開可能な範囲で状況を共有致します。
>>
>> 今回扱っているWebアプリでは、開発者とテンプレート記述者が異なる場所で働いており、
>> テンプレート記述者はよりエンドユーザーに近い立場の方が担当されています。
>> テンプレート記述者は「汎用的な」制御を開発に求め、自由にページを構築しています。
>>
>> そのため、開発者が想定外の使い方をテンプレート記述者がしてしまうことがあり得ます。
>> 今回は機能要件は満たすがパフォーマンスが悪いテンプレートが作られてしまいました。
>> 具体的には、あるidを使用するとDBからデータをロードし、そのタグの子要素で
>> ロードしたデータが使えるというものを提供したのですが、そのidをページ内で150以上使用されてしまい、
>> キャッシュなどがない初期表示時に150本のSQLが発行されてしまう問題がありました。
>>
>> そこでテンプレート上部に必要なレコードのIDを列挙して頂くことを打診しましたが、
>> 頻繁にページを変更したいということで、デザインを変更するごとに、ページ上部の記述を変更することは
>> 避けたいという希望を受け、ページ内のデータを自動で解析することを考えました。
>> その方法としてbuilderをカスタマイズし、ビルド時にテンプレートの構造データをカスタマイズすることを考えました。
>>
>>>描画時にはビルドの結果出来上がる TemplateImpl が持つ TemplateProcessor のツリーだけを使うので、
>>>そこに破綻がなければ問題ありません。
>>>
>>>ただ、元のテンプレートの情報を使うような Processor (echoなど)には
>>>対応する値をセットしておかないと正常に動作しないことがあるでしょう。
>>
>> ご説明ありがとうございます!
>>
>> ・レンダリング時にはTemplateProcessor のみを使用する
>>  つまり、
>>  template.insertProcessor(0, proc);
>>  さえ正常に行えれば、自由にテンプレートの処理をカスタマイズできる
>> ・元のテンプレート情報を使うProcessorは対応する値のsetが必要
>>     proc.setOriginalNode(htmlNode);
>>     proc.setInjectedNode(exec);
>>  この部分を指しているのではないかと思います。
>>
>> InjectionResolverとTemplateBuilder(SpecificationBuilder)の役割分担が理解できていなかったのですが
>> BuilderはXMLファイルをパースするところから始まって、TemplateProcessorのツリーを生成するのがゴールであって、
>> InjectionResolverやSpecificationNodeはその過程で作られるということだと理解しました。
>>
>> TemplateBuilderImplのafterBuildでdoInjectionが行われることによって、
>> SpecificationNodeからTemplateProcessorツリーが作られるので、
>> TemplateBuilderImplをカスタマイズする場合で、SpecificationNodeをいじる場合は
>> super.afterBuildの前に行うべきであり、Processorのツリーを直接いじるならどちらでも構わない。
>> そんなところでしょうか。
>>
>> TemplateBuilderImplのソースを読ませて頂いていると、
>> SAXを使ってXMLをパースしているところまで見つけられましたので、
>> Mayaaの内部ロジックに近づけたことに感動を覚えました。
>>
>>
>>
>> 2014年2月6日 20:38 suga <ko.suga @ gmail.com>:
>>> suga です。
>>>
>>>> よくわかっていないことが、SpecificationNodeとProcessorは一対一に
>>>> しなければならないのかということです。
>>>
>>> 基本的には、SpecificationNode は Processor のインスタンスを作るための指示なので、
>>> 通常のビルドプロセス後であれば何かを追加したところで Mayaa 側からは何もしません。(できません)
>>>
>>>> 例えば今後このような対応を複数重ねあわせた時、
>>>> 後発の処理が同じhtmlタグにプロセッサーを上書きしてしまうのでしょうか。
>>>> それとも、insertChildNodeで、テンプレートにダミーのタグを追加する方が良いでしょうか?
>>>
>>> それは「後発の処理」次第ですね。
>>> TemplateBuilder#afterBuild であればそのテンプレートに対する Mayaa の処理は
>>> 終わっているので、「複数重ね合わせ」る処理がどうするかです。
>>>
>>>
>>>> このような書き方は問題ありませんでしょうか?
>>>
>>> これも以下同文で、拡張したビルド処理の後にさらに拡張するのであればそれ次第、というところです。
>>>
>>> 描画時にはビルドの結果出来上がる TemplateImpl が持つ TemplateProcessor のツリーだけを使うので、
>>> そこに破綻がなければ問題ありません。
>>>
>>> ただ、元のテンプレートの情報を使うような Processor (echoなど)には
>>> 対応する値をセットしておかないと正常に動作しないことがあるでしょう。
>>>
>>>
>>>
>>> おそらく前提が省略されているだけだと思いますが、
>>>
>>>> 具体的には画面で何回も細切れにlazyローディングなデータのselectを発行してしまって遅いので、
>>>> 最初にまとめてがっつり取ってきてキャッシュしておきたいという場面があります。
>>>
>>> これだけ読むと Mayaa(一般にはjsp)に渡す前にServletで全部結果をまとめておくのが良いのでは、と思うのですが。
>>> 全部のデータを持つのが現実的では無いサイズの場合はキャッシュできないでしょうし。
>>> --
>>> suga ( ko.suga @ gmail.com )
>>>
>>>
>>> 2014-02-03 Susumu ISHIGAMI <susumu.ishigami @ gmail.com>:
>>>> 石上です
>>>>
>>>> そのまま処理をすると遅くなってしまうテンプレートがあり、
>>>> builderを改良してソースコードを解析した上で、
>>>> 事前にテンプレート定義を書き換えてしまって高速化しようと考えました。
>>>>
>>>> 具体的には画面で何回も細切れにlazyローディングなデータのselectを発行してしまって遅いので、
>>>> 最初にまとめてがっつり取ってきてキャッシュしておきたいという場面があります。
>>>> 既に実装はほぼできていて、次のような方式で実現しました。
>>>>
>>>> TemplateBuilderImpl
>>>> をextendした独自クラスで、
>>>> afterBuildをOverrideし、specificationの中身を解析した上で、
>>>> 動的にscriptを生成し、execプロセッサーを強制的に追加することで、
>>>> そのscriptが実行されると適切にキャッシュが作られて
>>>> 後続の処理が高速化される
>>>>
>>>> ここで、execプロセッサーを動的追加するために以下の様なコードを
>>>> 書きました。(インデントを見やすくするためタブを全角スペースに変換しています)
>>>>
>>>> static void injectHeaderScript(Specification specification, String script) {
>>>>   Template template = (Template) specification;
>>>>   QName qName = QNameImpl.getInstance("exec");
>>>>   LibraryManager libraryManager = ProviderUtil.getLibraryManager();
>>>>   ProcessorDefinition def = libraryManager.getProcessorDefinition(qName);
>>>>   SpecificationNodeImpl exec = new SpecificationNodeImpl(qName);
>>>>   exec.addAttribute(QNameImpl.getInstance("script"), "${" + script + "}");
>>>>   SpecificationNode htmlNode = getHtmlNode(template);
>>>>   TemplateProcessor proc = def.createTemplateProcessor(htmlNode, exec);
>>>>   if (htmlNode != null) {
>>>>     proc.setOriginalNode(htmlNode);
>>>>     proc.setInjectedNode(exec);
>>>>     template.insertProcessor(0, proc);
>>>>   }
>>>> }
>>>>
>>>> static SpecificationNode getHtmlNode(NodeTreeWalker current) {
>>>>   Specification specification = SpecificationUtil.findSpecification(current);
>>>>   for (Iterator<?> it = specification.iterateChildNode(); it.hasNext();) {
>>>>     SpecificationNode node = (SpecificationNode) it.next();
>>>>     if (node.getQName().getLocalName().toLowerCase().equals("html")) {
>>>>       return node;
>>>>     }
>>>>   }
>>>>   return null;
>>>> }
>>>>
>>>> よくわかっていないことが、SpecificationNodeとProcessorは一対一に
>>>> しなければならないのかということです。
>>>>
>>>> 例えば今後このような対応を複数重ねあわせた時、
>>>> 後発の処理が同じhtmlタグにプロセッサーを上書きしてしまうのでしょうか。
>>>> それとも、insertChildNodeで、テンプレートにダミーのタグを追加する方が良いでしょうか?
>>>>
>>>> また、htmlタグを探さずに、単に
>>>>   SpecificationNode node = (SpecificationNode) template.getChildNode(0)
>>>>   TemplateProcessor proc = def.createTemplateProcessor(node, exec);
>>>> としても動きました。
>>>> この場合、nodeはDOCTYPEノードになるようです。
>>>> このような書き方は問題ありませんでしょうか?
>>>>
>>>> マニアックで申し訳ありませんが、
>>>> builderを書き換えてprocessorやnodeを操作できると、面白いことができそうです。
>>>> _______________________________________________
>>>> mayaa-user mailing list
>>>> mayaa-user @ ml.seasar.org
>>>> https://ml.seasar.org/mailman/listinfo/mayaa-user
>>> _______________________________________________
>>> mayaa-user mailing list
>>> mayaa-user @ ml.seasar.org
>>> https://ml.seasar.org/mailman/listinfo/mayaa-user
>>
>>
>>
>> --
>> Susumu ISHIGAMI
>> susumu.ishigami @ gmail.com
> _______________________________________________
> mayaa-user mailing list
> mayaa-user @ ml.seasar.org
> https://ml.seasar.org/mailman/listinfo/mayaa-user



-- 
Susumu ISHIGAMI
susumu.ishigami @ gmail.com


mayaa-user メーリングリストの案内