機能 #1
openv3.1に向けて v3.0の積み残し
Added by keiji ono about 1 year ago. Updated 9 months ago.
0%
Description
- api作成時のwhere句のチェック、”同じモノ”チェック
- incremental ciew maintenance対応
- セキュリティ強化:コンテンツ、プログラム変更、改ざん検知
要確認事項
jetelina_delete_flg=1のデータの自動削除
使われていないapiの自動削除 → 100日間アクセスがなければ自動的に削除する。90日目に通知する → "削除されるかもしれないapi”リスト表示 ← ファイル作成 apilifetime "apino,lifetime" アクセス解析のたびにアクセスがあったらlifetimeを100に戻す。アクセスがなかったらlifetimeをカウントダウンする。
Updated by keiji ono about 1 year ago
- table名が含まれているか
- from句にあるtableのカラムであるか
- 型はあっているか string -> "が必要
functionpanel.js checkGenelicInput()を別jsライブラリにして、込み入った処理を他に影響しないようにしてやる。
subquery.js
ムリムリムリ なので止めた (゚∀゚) 2025/4/28 もしいい手が見つかればリトライしてみる。
Updated by keiji ono about 1 year ago
保護すべきファイル
設定ファイル類
- base.jdic -> initialize時のdate time
- JetelinaConfig.cnf -> post処理による変更毎を基本とする
- JetelinaSqlList -> 〃
- 操作の前にファイルのタイムスタンプとDBに格納されているDataを比較する
- 1が問題なければ操作を実行する
- 書き込み操作なら、書き込み終了後にDBのdataを更新する
- 読み込み操作ならなにもしない
- 2で問題が発生した場合は操作を中断してオペレータに報告する
”いいから実行せい”となったら当該処理を実行する。実行したあとどうしようか。。。
プログラムファイルはどうしようかなぁ
この処理は新しいjlファイルで実行しよう。
Updated by keiji ono about 1 year ago
2.incremental view maintenance対応
"incremental matelialized view"という、データの実体を伴い且つ、元tableの更新も差分だけ自動で反映される機能があるらしい。
postgres/mysqlでは拡張機能で、oracleでは”トリガー"と呼ばれる標準機能みたいだ。
そこで、
- 複数tableを使っていて且つ、実行回数が多い
SQL文をviewにする。view table名称はSQL番号(api no)の"js**_view"とする。
viewにしたら
- view化後のSelect文が必要
viewにした後
- create viewしたsqlを"jv**"としてSQLリストに登録する
- 元のSQLとjv**を紐づける
- 元のSQLにアクセスがあった場合、jv**と同じなら(バインディングが同じなら)jv**を実行する。違えば元SQLを実行する。
- "jv**"はapiリストには載せない
ivmを使うには該当dbにextentionを追加する必要がある。
ivmが利用できるかどうか調べる
select * from pg_available_extensions where name = 'pg_ivm'
の結果が帰ってきたら利用可能
postgres=# select * from pg_available_extensions where name = 'pg_ivm';
name | default_version | installed_version | comment
--------+-----------------+-------------------+--------------------------------------------
pg_ivm | 1.7 | 1.7 | incremental view maintenance on PostgreSQL
(1 行)
extension追加
create extension if not exists pg_ivm
extension削除
drop extension pg_ivm
Updated by keiji ono about 1 year ago
2.increment...をどう使うかだよなぁ。
sub queryに{}のパラメタがあるとそもそもviewが作れない。
履歴から一番多い{}パラメタをivm化するというのもなんだかなぁ。
subに{}が無く且つ、アクセス数が多いものを自動的にivm化するってのがいいのかなぁ。でもそれはユーザの意図に合うのだろうか?
ivmはシンプルなtableであることを前提としているらしいので、csvからtableを作成するjetelinaのコンセプトには合致している。
となると、tableのrelationはivmにおまかせという風にすればjetelinaとの整合性も良さげな感じがする。
ivmと同等の機能はoracle/mysqlではtriggerで実行している。これが問題。triggerはスクリプトを書かないといけなくて、しかもしばしばビジネスロジックになってしまう。
ivmは任意のsqlをview化するだけで更新可能にもしている点がちがう。つまり、欲しいデータのsqlを書いてそれがtable間で複雑な関係だったりする場合にはなんとも頼もしい機能になる。なにせ「勝手に」やってくれるから。
この点に注目してpostgreをoracle/mysqlと差別化して「jetelinaはrdbmsではpostgresを推しますよ」というのは戦略としてアリかもしれない。
実際のimvの使い方
e.g js7として
select ftest3.ftest3_name,ftest2.ftest2_name,ftest3.ftest3_age,ftest2.ftest2_age from ftest3 as ftest3,ftest2 as ftest2 where test2.ftest2_ave < 0.3 and ftest3.ftest3_ave < 0.2
なる複数tableを扱うSQLがあるとする
imv化するためにselect文としてcreate_immv()を実行する
'js7' -> 'jv7' として対応をとっている
select create_immv('jv7','select ftest3.ftest3_name,ftest2.ftest2_name,ftest3.ftest3_age,ftest2.ftest2_age from ftest3 as ftest3,ftest2 as ftest2 where ftest2.ftest2_ave < 0.3 and ftest3.ftest3_ave < 0.2');
すると'jv7'というtableが新規に作成される(dropする場合は普通に drop table jv7)
postgres=# \dt
リレーション一覧
スキーマ | 名前 | タイプ | 所有者
----------+---------------------+----------+----------
public | ftest2 | テーブル | postgres
public | ftest3 | テーブル | postgres
public | jetelina_user_table | テーブル | postgres
public | jv7 | テーブル | postgres ← 新規に作成される
'jv7'の中を見てみると’js7'の実行結果がtableになっているのがわかる
postgres=# \d jv7
テーブル"public.jv7"
列 | タイプ | 照合順序 | Null 値を許容 | デフォルト
-------------+-------------------+----------+---------------+------------
ftest3_name | character varying | | |
ftest2_name | character varying | | |
ftest3_age | integer | | |
ftest2_age | integer | | |
postgres=# select * from jv7;
ftest3_name | ftest2_name | ftest3_age | ftest2_age
-------------+-------------+------------+------------
ZZZ | ZZZ | 10 | 10
ZZZ | YYY | 10 | 20
ここでftest2 table上でftest2_nameを 'ZZZ' -> 'ZZZ1'にupdateしてみるとjv7 tableが更新されていることがわかる
postgres=# update ftest2 set ftest2_name='ZZZ1' where ftest2_jt_id=1;
UPDATE 1
postgres=# select * from ftest2 where ftest2_name='ZZZ1';
ftest2_jt_id | ftest2_name | ftest2_sex | ftest2_age | ftest2_ave | jetelina_delete_flg
--------------+-------------+------------+------------+------------+---------------------
1 | ZZZ1 | m | 10 | 0.1 | 0
(1 行)
postgres=# select * from jv7;
ftest3_name | ftest2_name | ftest3_age | ftest2_age
-------------+-------------+------------+------------
ZZZ | YYY | 10 | 20
ZZZ | ZZZ1 | 10 | 10 <- 'ZZZ1'になっている
さらに、ftest2 tableに新規データをinsertすると、js7の条件に合っていればjv7 tableに自動的に追加されている
postgres=# insert into ftest2 (ftest2_name,ftest2_sex,ftest2_age,ftest2_ave ,jetelina_delete_flg) values('KKK','m',20,0.2,0);
INSERT 0 1
postgres=# select * from ftest2;
ftest2_jt_id | ftest2_name | ftest2_sex | ftest2_age | ftest2_ave | jetelina_delete_flg
--------------+-------------+------------+------------+------------+---------------------
2 | YYY | f | 20 | 0.2 | 0
3 | XXX | m | 30 | 0.3 | 0
1 | ZZZ1 | m | 10 | 0.1 | 0
4 | KKK | m | 20 | 0.2 | 0 <- 新規追加
(4 行)
postgres=# select * from jv7;
ftest3_name | ftest2_name | ftest3_age | ftest2_age
-------------+-------------+------------+------------
ZZZ | YYY | 10 | 20
ZZZ | ZZZ1 | 10 | 10
ZZZ | KKK | 10 | 20 <- 'KKK'は'..ave=0.2'なのでjs7のsubquery条件に合致するので自動的にjv7に追加されている
(3 行)
もちろん条件に合わないとjv7 tableには追加されない
postgres=# insert into ftest2 (ftest2_name,ftest2_sex,ftest2_age,ftest2_ave ,jetelina_delete_flg) values('GGG','m',20,0.4,0);
INSERT 0 1
postgres=# select * from ftest2;
ftest2_jt_id | ftest2_name | ftest2_sex | ftest2_age | ftest2_ave | jetelina_delete_flg
--------------+-------------+------------+------------+------------+---------------------
2 | YYY | f | 20 | 0.2 | 0
3 | XXX | m | 30 | 0.3 | 0
1 | ZZZ1 | m | 10 | 0.1 | 0
4 | KKK | m | 20 | 0.2 | 0
5 | GGG | m | 20 | 0.4 | 0 <- 新規追加
(5 行)
postgres=# select * from jv7;
ftest3_name | ftest2_name | ftest3_age | ftest2_age
-------------+-------------+------------+------------
ZZZ | YYY | 10 | 20
ZZZ | ZZZ1 | 10 | 10
ZZZ | KKK | 10 | 20
(3 行) <- 'GGG'は'.._ave=0.4'でjs7のsubquery条件に合わないので追加されていない
もちろんftest2上でdeleteするとjv7上のデータも消える
postgres=# delete from ftest2 where ftest2_name='KKK';
DELETE 1
postgres=# select * from ftest2;
ftest2_jt_id | ftest2_name | ftest2_sex | ftest2_age | ftest2_ave | jetelina_delete_flg
--------------+-------------+------------+------------+------------+---------------------
2 | YYY | f | 20 | 0.2 | 0
3 | XXX | m | 30 | 0.3 | 0
1 | ZZZ1 | m | 10 | 0.1 | 0
5 | GGG | m | 20 | 0.4 | 0
(4 行)
postgres=# select * from jv7;
ftest3_name | ftest2_name | ftest3_age | ftest2_age
-------------+-------------+------------+------------
ZZZ | YYY | 10 | 20
ZZZ | ZZZ1 | 10 | 10
(2 行)
作成されたivmの実行queryを見るには
postgres=# select immvrelid, get_immv_def(immvrelid) from pg_ivm_immv;
* where句をつけて immvrelid='jv7'とかしても、それは効かない。:P
immvrelid | get_immv_def
-----------+-------------------------------------------------------------------------------------------------------------------------
jv7 | SELECT ftest3.ftest3_name, +
| ftest2.ftest2_name, +
| ftest3.ftest3_age, +
| ftest2.ftest2_age +
| FROM ftest3 ftest3, +
| ftest2 ftest2 +
| WHERE ((ftest2.ftest2_ave < (0.3)::double precision) AND (ftest3.ftest3_ave < (0.2)::double precision))
36209 |
36261 |
36286 |
36312 |
36337 |
jv16 | SELECT palt.palt_first_name, +
| people_1000.people_1000_first_name +
| FROM palt palt, +
| people_1000 people_1000 +
| WHERE ((palt.palt_jt_id < 100) AND ((100 < people_1000.people_1000_jt_id) AND (people_1000.people_1000_jt_id < 200)))Updated by keiji ono about 1 year ago
imvのtableに格納されるjs*のSQLにjetelina_delete_flg=0であることを明示する必要がありそう
元tableでjetelina_delte_flg=1とdelete扱いされたときには、imvのtableの当該データも削除されるからOK
- 複数tableを使用しているsqlは原則jv*化する
- 試しにjs*の実行速度とjv*の実行速度を比較する
- js*の実行速度がjv*の実行速度よりも著しく遅い場合はjv*化優先候補となる
- js*の実際のアクセス数を見る
- js*のアクセス数が高いようならjv*化する
- jv*化したjs*はJetelinaSqlList上でjv*で表される
- api listの表示時にjv* -> js* と変換する
- js* apiが呼ばれて実行される際にはjs*とjv*のorを取って、存在する方のsqlを実行する
- api access count/exectute speed の各グラフもjs* -> jv* のapi名変更を行う
- js*/jv*対応ファイルを作成する
- js実行時には常にこれを参照して対応するjvがあればそちらを実行する
- jv実行時は単純に該当jvをselectすればいいだけなのでsqlは単純になる
- jv実行時のjsonフォームをjsに合わせる必要がある
- 6-9が不要になる
js*/jv*対応ファイルはJetelinaSqlList同様DataFrameにしてメモリに格納すればいいから比較のための処理速度はあまり気にならないかもしれないので、プログラミングを考えるとこっちのほうがよさそうだな。
Updated by keiji ono about 1 year ago
これらにはsqlのテスト実行と速度計測のための関数がある。
SQLA..はテスト用のtable作成/削除とかやってくれているので、これを上位プログラムとしてここからPgTest..を呼び出すことにしよう。
すでにSQLA..main()でタイマー機能があるからこれを使おう。
#5の1-5を実行するとして、
- 1はconfig/JetelinaTableApiRelationからpostgresで複数tableを使用しているjs*を抽出する -> collectIvmCandidateApis()
- PgIVM..を呼び出して2を実行する -> experimentalRun()
- jv化対象となるjs*を新規のファイルに記録しておく
- 定期実行(毎日一回とか)で4を見てjv化の判断をおこない、5まで実行する
これなら既存プログラムへの影響は軽微っぽい。
2025/6/3
js/jv対応表はconfig/JetelinaJvmnizedJsApiListに記録され、Df_JsJvListで運用される
Jetelina.InitApiSqlListManager.ApiSqlListManager.Df_JsJvList
2×2 DataFrame
Row │ js jv
│ String3 String3
─────┼──────────────────
1 │ js7 jv7
2 │ js8 jv8
Df_JsJvListの作成・更新タイミングは
1.起動時にSQLAnalyzer.init()で初期実行される <- JetelinaJvmmize... file があれば。
2. 〃 定期的にmulti table利用のsqlを探って実行される
3.js*が削除された時に対応するjv*があれば実行する <- 要実装 deleteJvApiFromMatchingList(), dropIVMtable()はもうあるから、いつ呼ぶかってこと
2025/6/1
2でivm tableを作成してその実行速度をfileに記録するところまでexperimentalRun()でやっていたが、それをやめて、SQLAnalyzer.executeIVMtest()はivm test runの平均実行速度を戻り値として取得し、これとivmにしないsqlの実行速度を比較して「ある程度」ivm効果が確認できるようならivm化することとしよう。これによってアクセス数云々というのは関係なくする。ivm効果が確認できないようならdropIVMtable()を呼び出してivm化をなかったことにする。
ivm化することにしたならjs/jv対応ファイルに追記すると。
jv化するところまではOK.
次は、
- js作成時にjv化検討も行う(PgDBController.compareJsAndJv()実行) <-「js作成時にjvも」同時でなくてもいいよね。同時じゃないほうがいいか?
- api呼び出しでjvを呼び出し <- PgDBController.executeApi()に実装済 6/15
5により、jv*のsqlがシンプルではなくなったのでjv*のsqlをどこかに格納しておく必要がでてきた - api削除でjvを同時に削除 <- PostDataController.deleteApi()(実体はApiSqlListManager.delteApiFromList()), deleteTable()(実体はDBDataController.dropTable())でやれるかな <- 6/16 done
- Oops, table list にjv*が載っている。そりゃそうだjv*は普通のtableだから。表示しないようにちょっと工夫しないとな。 <- 6/17 done #7参照
- js*作成時のsqlにlimitやoffset句がある場合はjv*にも引き継がないといけないのでは?
<= 現状でもjs*ではlimit/offsetが効く。これらの句はwhere句の後ろにあるといいらしい。さてjv*に反映だな。
ivmは"HAVING, ORDER BY, LIMIT/OFFSET, UNION/INTERSECT/EXCEPT, DISTINCT ON"をサポートしていないらしい。なので、これらがsubquery句にあった場合は文字列を分離してjv*の実行文に後付してやるのか。メンドイな。
SQL結合に使う句がある場合はivm化しないということにしよう。これらのclausesはivmのバージョンアップで変わる可能性があるから時々見直しが必要かも。clause ivm-nize having X order by ○ limit ○ offset ○ union X intersect X except X distinct on X tablesample X values X for update X share X
これらのclauseを判別してivm tableを作成するかどうか決めるところまではPgIVMController.createIVMtable()で実装できた。問題は次、compareJsAndJv()でjs*とjv*の速度比較をして、よさげならjv*を使うことにするとしているが、sqlに変数'{...}'があると比較実行できない。もちろんclauseがあっても。
さて、どうしよう。
Updated by keiji ono about 1 year ago
- PgVMController.checkIVMExistence()の実行をGenie起動時に一度呼び出す
- table listにivmのtableが表示される <- ivmは”普通のtable”だからこうなる 6/17解決
解決方法:quote: PgDBController._getTableList() DataFrames.filter!(row -> row.tablename != "jetelina_user_table", df) → DataFrames.filter!(row -> row.tablename != "jetelina_user_table" && row.tablename ∉ Df_JsJvList[!,:jv] , df) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ なんかjuliaっぽい:)
ivm未導入時にはconf内の"pg_ivm"はfalseになっている。
ivmを導入したら一度Geneiを再起動すると1でivmが認識されて"pg_ivm"がtrueになる。
以降、js*作成時にivm化が検討される。
では、ivm導入前に複数tableを使っているsqlはいつivm化が検討されるべきか?答えはSQLAnalyzer.collectIvmCandidateApis()が持っている。
10/29
ivm tableは非表示にすることにしたので元に戻す。ref. #11
Updated by keiji ono about 1 year ago
PgDBController.dropTable()のSQLに"if exists"を追加して、api削除時にivm tableも一緒に削除しようとした時に存在しなかったらスルーするようにする。
drop table
-> drop table if exists
MySqlでは今の所存在するtableだけが削除されるハズなので、こっちはそのままにしておく。
Updated by keiji ono 12 months ago
ivm機能についてちょっと考え直している。
🔸従来の考え方
複数tableからivm talbを作成する事は実行速度の計測と実際の利用頻度から決めることにしていた。
この方式ではivmの機能と制限をそのまま受け入れて、view的な使い方をユーザが意識しないでやれるようにという方針。
この方式ではivm tableには一つのapiしかない。
🔹今考えていること
ivm tableは実態としてtableなんだからivmの制限とか気にしないでいきたい。
どのtbleを使って新し'talbe'を作るかはユーザの任意に任せ、jetelinaは作成されたivm talbeを新たなtableとして使う。
この方式ではivm tableには普通に複数のapiを作ることができる。但し、insert/delete/updateの処理は元のtableに対して行う。なので、元のtableのinsert/delete/update apiを組み合わせたsqlとしてあげればいい。
この方式では、従来の方式と違ってivm tableをユーザに意識的に作成させる。なので"ivm table作成"コマンドを用意してあげることになる。
つまりtable作成方法は2つになる
- ファイルアップロード
- ivm table作成コマンド実行
この方式ではivm table同士のさらなる組み合わせもできそうだけど、それはどうなんだろう?
Updated by keiji ono 10 months ago
ivmを使った機能について新しいアイデア
従来は「複数tableのselect文のパフォーマンスを見て、ivmを使った方がいいようなら自動的にそうする。ユーザはそれを知らなくてもいい」ということでやっていたが、むしろ「ユーザが意識して使う」というのはどうだろうか。複数の「マスターテーブルから必要なデータを選択して新しいテーブルを作る」という方がユーザには理解しやすく受け入れやすいのではなかろうか。これを「バスケット方式」と呼ぶ。
マスターテーブルはcsvアップロードされたヤツになる。
- dbiはselectのみ
- insert/update/deleteのdbiは今まで通り
- tableのリレーションなんか一目でわかると嬉しい
- データ選択する際に表示するテーブルにはサンプルデータも表示してデータをイメージしやすくする。nullアリデータでも何か例が表示されるようならベスト
Updated by keiji ono 9 months ago
"suggestion for api speed"の実行は、ログイン時に自動的にチェックされる。
jeteilnalib.js #1317
getAjaxData(scenario["analyzed-data-collect-url"][3]);
それと、コマンド実行はここ
statspanel.js #192
} else if (inScenarioChk(ut, 'stats-check-suggestion-cmd')) {
// confirm the suggestion file existing
getAjaxData(scenario["analyzed-data-collect-url"][7]);
このスピード比較機能は残しておいて、表示されるアイコンを変えよう。
対象画像: img/jetelina-concern.png
画像はalertにした。これを消すのは"general-thanks-cmd"、つまり"thank you"で消える。
jetelinalib.js #1562
changeChatGirlImage("chat");
Updated by keiji ono 9 months ago
公開repositoryを設定する。
git-jetelina/Jetelina
me@jetelina.org / jetelina123