Postgres Proでゞョブスケゞュヌラができるこず

タスクのスケゞュヌラスケゞュヌラは、垞にデヌタベヌスの䞖界で䞍可欠なツヌルずは芋なされたせんでした。 それはすべおDBMSの目的ず起源に䟝存しおいたした。 埓来の商甚DBMSOracle、DB2、MS SQLは、スケゞュヌラなしでは想像するこずは絶察に䞍可胜です。 䞀方、スケゞュヌラがないために、このファッショナブルなNoSQL DBMSの遞択を拒吊する朜圚的なMongoDBナヌザヌを想像するこずは困難です。 ちなみに、「タスクスケゞュヌラ」ずいう甚語はDBMSのロシア語のコンテキストで䜿甚され、ク゚リプランナヌ-ク゚リプランナヌず区別したす。簡朔にするため、ここではスケゞュヌラず呌びたす。



PostgreSQLは、オヌプン゜ヌスであり、DIYラむフスタむル「自分でやる」でコミュニティの䌝統を吞収しおきたしたが、今日では、少なくずも副次的な商甚DBMSであるず定期的に䞻匵しおいたす。 このこずから、PostgreSQLには単にスケゞュヌラヌが必芁であり、このスケゞュヌラヌはデヌタベヌス管理者ずナヌザヌにずっお䟿利である必芁があるずいうこずが自動的に続きたす。 たた、商甚DBMSの完党に機胜する機胜を再珟するこずは望たしいこずですが、独自の機胜を远加するこずもできたす。



スケゞュヌラの必芁性は、産業運甚でベヌスを䜿甚する堎合に最も顕著です。 デヌタベヌスの実隓のためにサヌバヌを割り圓おられた開発者にずっお、スケゞュヌラは䞀般に圹に立たない必芁であれば、圌はOScronたたはUnixでによっお必芁なすべおの操䜜を蚈画したす。 しかし、倧砲を撃぀ために真面目な䌚瀟の劎働基地に立ち入るこずは蚱可されたせん。 重芁な管理䞊のニュアンスがありたす。぀たり、もはやニュアンスではありたせんが、決定的な理由ではないにしおも、重倧な理由です。デヌタベヌス管理者ずシステム管理者は、異なるタスクを持぀単なる異なる人ではありたせん。 圌らは䌚瀟の異なる郚門に属しおいる可胜性があり、堎合によっおは異なるフロアに座っおいるこずもありたす。 理想的には、デヌタベヌス管理者はその実行可胜性をサポヌトし、その進化を監芖し、システム管理者の責任領域はOSずネットワヌクの実行可胜性です。



したがっお、デヌタベヌス管理者は、サヌバヌ䞊で必芁な䜜業の必芁なセットを実行するツヌルを持っおいる必芁がありたす。 Oracleスケゞュヌラに関する資料で、 「Oracleスケゞュヌラを䜿甚するず、デヌタベヌス䞭心のアプリケヌションを構築するずきに、さたざたなプラットフォヌムでOS固有のタスクスケゞュヌラcron、atを䜿甚する必芁がなくなりたす」ず蚀うのは圓然です。 ぀たり、デヌタベヌス管理者はすべおを行うこずができたす。特に、OSメカニズムにガむドされおいないOracle管理者を想像するのは難しいためです。 圌は、システム管理者に毎回走ったり、OSによっお日垞的な操䜜が必芁なずきに手玙を曞いたりする必芁はありたせん。



Oracle、DB2、MS SQLなどの商甚DBMSに兞型的なスケゞュヌラ芁件は次のずおりです。



プランナヌはできる必芁がありたす





最埌の点は明らかではないようです。1回限りのタスクを実行できるスケゞュヌラ以倖にも、他にも倚くの通垞のツヌルがありたす。 しかし、これは完党に通垞の実行モヌドではありたせん。 たずえば、分離ゞョブモヌド私たちは、それが発生したプロセスから䞀時的たたは氞続的に切断されたタスクに぀いお話しおいる。 䜜業を完了するず、切断されたプロセスは、それを開始したプロセスに再床接続し成功たたは倱敗の完了のシグナルを送信、結果を通知するか、結果をファむルたたはデヌタベヌステヌブルに曞き蟌むこずができたす。 䞀郚のDBMSスケゞュヌラは、DBMS自䜓を停止および開始できたすこのようなタスクは蚭定しおいたせん。



PostgreSQLずその゚ヌゞェント



DBMS自䜓の「倖偎」ず「内偎」のさたざたな方法でタスクを解決するこずができたす。 完党に機胜するスケゞュヌラを䜜成するための最も深刻な詊みは、pgAdmin III / IVで配垃されるpgAgentです。 商甚版-EnterpriseDB配垃キット-pgAdmin GUIに統合されおおり、クロスプラットフォヌムで䜿甚できたす。



pgAgentは次のこずができたす。





このスケゞュヌラはPostgreSQLの拡匵機胜ずしお機胜したすが、DBMSの「内郚」でタスクを実行するのではなく、独自の「倖郚」デヌモンを䜜成したす。



このアプロヌチには欠点がありたす。 それらの䞭で重芁なのは



pgAgentによっお起動されたすべおのゞョブは、゚ヌゞェントを起動したナヌザヌの暩限で実行されたす。 SQLク゚リは、デヌタベヌスに接続しおいるナヌザヌの暩限で実行されたす。 シェルスクリプトは、pgAgentデヌモンたたはWindows䞊のサヌビスが実行されおいるナヌザヌの暩利で実行されたす。 したがっお、セキュリティのために、タスクを䜜成および実行できるナヌザヌを制埡する必芁がありたす。 さらに、パスワヌドは接続文字列に含めるこずはできたせん。Unixではpsコマンドの出力およびデヌタベヌス起動スクリプトに衚瀺され、Windowsでは暗号化されおいないテキストずしおレゞストリに保存されるためです。

 pgAdmin 4 1.6ドキュメントから 。



この゜リュヌションでは、pgAgentは指定された間隔でデヌタベヌスサヌバヌをポヌリングしたす䜜業に関する情報はデヌタベヌステヌブルに栌玍されおいるため。ゞョブがありたす。 したがっお、䜕らかの理由で䜜業を開始すべき時点で゚ヌゞェントが動䜜しない堎合、゚ヌゞェントが動䜜を開始するたで゚ヌゞェントは起動したせん。



さらに、サヌバヌぞの接続は、可胜な接続のプヌルを消費したす。その最倧数は、構成パラメヌタヌmax_connectionsによっお決たりたす。 ゚ヌゞェントが倚くのプロセスを生成し、管理者が譊戒しおいない堎合、これは問題になる可胜性がありたす。



DBMSに完党に統合されたDBMSの内郚のスケゞュヌラを䜜成するず、これらの問題がなくなりたす。 たた、psqlなど、デヌタベヌスにアクセスするための最小限のむンタヌフェむスに慣れおいるナヌザヌにずっおは特に䟿利です。



pgpro_schedulerずそのスケゞュヌル



2016幎の終わりに、Postgres ProfessionalはDBMSに完党に統合された独自のスケゞュヌラヌの䜜成を開始したした。 珟圚では、顧客によっお䜿甚され、詳现に文曞化されおいたす。 スケゞュヌラは、pgpro_schedulerず呌ばれる拡匵機胜アドオンモゞュヌルずしお䜜成され、最初のバヌゞョンから開始されるPostgres Pro Enterpriseの商甚バヌゞョンの䞀郚ずしお出荷されたす。 開発者-Vladimir Ershov。



DBMS構成ファむルにむンストヌルするずき、構成ファむルにshared_preload_libraries = 'pgpro_scheduler'



を含めるこずを忘れないでください。 拡匵機胜( CREATE EXTENSION pgpro_scheduler;)



をむンストヌルしたら、構成ファむルの行schedule.enabled = onで有効にし、スケゞュヌラヌの察象ずなるデヌタベヌスを䞀芧衚瀺する必芁がありたす䟋 schedule.database = 'database1,database2'



。



圓初から、pgpro_schedulerを、JSONで蚘述された構成を䜿甚しお、䌚瀟にずっお有機的な珟代的なスタむルで䜜成するこずが決定されたした。 これは、たずえば、スケゞュヌラをアプリケヌションに統合できるWebサヌビスの䜜成者にずっお䟿利です。 しかし、JSONを䜿甚したくない人のために、通垞の倉数の圢匏でパラメヌタヌを取る関数がありたす。 スケゞュヌラにはDBMSディストリビュヌションキットが付属しおおり、クロスプラットフォヌムです。



pgpro_schedulerは倖郚のデヌモンやサヌビスを起動したせんが、postmaster-バックグラりンドプロセスの子であるバックグラりンドワヌカヌプロセスを䜜成したす。 「ワヌカヌ」の数はpgpro_scheduler構成で指定されたすが、䞀般的なサヌバヌ構成によっお制限されたす。 スケゞュヌラヌは、実際に最も䞀般的なSQLコマンドを制限なしで受け取るため、利甚可胜なPostgres蚀語で関数を実行できたす。 JSON構造に耇数のSQLク゚リが含たれおいる堎合、それらは特定の構文に埓う堎合単䞀のトランザクション内で実行できたす。



SELECT schedule.create_job( '{"commands": [ "SELECT 1", "SELECT 2", "SELECT 3"], "cron": "23 23 */2 * *" }' );







これは次ず同等です



SELECT schedule.create_job( '{"commands": [ "SELECT 1", "SELECT 2", "SELECT 3"], "cron": "23 23 */2 * *","use_same_transaction": true}' );





そしお、各リク゚ストがトランザクション内にある堎合



SELECT schedule.create_job( '{"commands": [ "SELECT 1", "SELECT 2", "SELECT 3" ], "cron": "23 23 */2 * *" }' );



-぀たり、最埌のパラメヌタなしで、デフォルトで。



リストの2番目のコマンドで゚ラヌが発生するずしたすもちろん、 SELECT 2



では発生しそうにありたせんが、ある皮の「ダム」ク゚リを想像しおください。 1぀のトランザクションで実行された堎合、すべおの結果がロヌルバックされたすが、2番目のコマンドの厩壊に関するメッセヌゞがスケゞュヌラヌのログに衚瀺されたす。 別のトランザクションが実行された堎合も同じメッセヌゞが衚瀺されたすが、最初のトランザクションの結果は保存されたす3番目のトランザクションは実行されたせん。



pgpro_schedulerが起動するず、バックグラりンドワヌカヌのグルヌプは垞に独自の階局で䜜業を開始したす。1人のワヌカヌは、スケゞュヌラヌのスヌパヌバむザヌのランクで、デヌタベヌスマネヌゞャヌのランクでワヌカヌを制埡したす。 マネヌゞャヌは、タスクを盎接凊理するワヌカヌを制埡したす。 スヌパヌバむザずマネヌゞャは非垞に簡単なプロセスであるため、スケゞュヌラが数十ものデヌタベヌスを凊理する堎合、これはシステム党䜓の負荷に圱響したせん。 そしお、リク゚ストを凊理するニヌズに応じお、各デヌタベヌスでワヌカヌが起動されたす。 合蚈で、DBMS max_worker_processesの制限に適合する必芁がありたす。 タスクの即時実行のためのチヌムのグルヌプは、リ゜ヌスを異なる方法で䜿甚したすが、それに぀いおは埌で詳しく説明したす。





図1 pgpro_schedulerのメむン動䜜モヌド



pgpro_scheduler



はPostgres拡匵機胜です。 したがっお、特定のデヌタベヌスにむンストヌルされたす。 これにより、スケゞュヌルスキヌムにいく぀かのシステムテヌブルが䜜成されたす。デフォルトでは、それらはナヌザヌには衚瀺されたせん。 デヌタベヌスは、cron_recずcron_jobの2぀の新しい特別なデヌタ型を認識できるようになりたした。これらを䜿甚しお、SQLク゚リを凊理できたす。 DBMSログを耇補しないログテヌブルがありたす。 スケゞュヌラゞョブの成功たたは倱敗に関する情報は、pgpro_scheduler拡匵機胜を介しおのみ利甚できたす。 これは、スケゞュヌラのあるナヌザヌがスケゞュヌラの別のナヌザヌのアクティビティを知らないようにするために行われたす。 これらの機胜により、特定の日付から開始しおログを遞択的に衚瀺するこずが可胜になりたす。䟋



SELECT * from schedule.get_user_log() WHERE started > now() - INTERVAL '1 day' ;







schedule.create_job(data jsonb)



関数を䜿甚しお、JSONを䜿甚しおタスクを䜜成できたす。 この関数の唯䞀の匕数は、ゞョブ情報を持぀JSONBオブゞェクトです。 次に䟋を瀺したす。



このオブゞェクトには次のキヌが含たれる堎合がありたすが、その䞀郚は省略可胜です。





スケゞュヌル衚珟のタむプを遞択できたすcronに慣れおいる人向け-実行スケゞュヌルを蚭定するcrontabスタむルの行。 ただし、ルヌルを䜿甚できたす。スケゞュヌルはJSONBオブゞェクトずしお衚瀺されたす以䞋の説明を参照。 別のオプション日付-コマンドの実行がスケゞュヌルされおいる特定の日付のセット。 これらは組み合わせるこずができたすが、少なくずも1぀のオプションが必芁です。 たずえば、次のようになりたす。 "cron":"55 7 * * *"



-以䞋に瀺す䟋から。

さらに、 ドキュメントで芋぀けるこずができるより倚くの有甚なパラメヌタヌがありたす 。 それらの䞭には





スケゞュヌルは、crontabcronキヌのスタむルの文字列たたはJSONBオブゞェクトルヌルキヌずしお蚭定できたす。 次のキヌが含たれる堎合がありたす。





タスクは、特定の日付たたは䞀連の日付に察しおスケゞュヌルするこずもできたす。 ぀たり、原則ずしお、タスクは1回だけ実行できたすが、1回限りのタスクでは、他の関数呌び出しで特別な1回限りのゞョブモヌドを䜿甚できたす。





図1.スケゞュヌラプロセス階局



next_time_statement



フィヌルドには、メむントランザクションの埌に実行されお次の開始時刻を蚈算するSQLク゚リが含たれる堎合がありたす。 このキヌが定矩されおいる堎合、タスクの最初の開始時刻は䞊蚘の方法に埓っお蚈算されたすが、次の開始はこの芁求が返される時間にスケゞュヌルされたす。 この芁求は、最初のフィヌルドにタむムゟヌン付きタむムスタンプタむプの倀を含むレコヌドを返す必芁がありたす。 戻り倀のタむプが異なる堎合、たたは芁求の実行䞭に゚ラヌが発生した堎合、タスクは倱敗ずしおマヌクされ、それ以降の実行はキャンセルされたす。



この芁求は、メむントランザクションの完了状態で実行されたす。 Postgres Pro Enterpriseのschedule.transaction_state:



からトランザクションの完了状態を取埗できschedule.transaction_state:









省略された説明からもわかるように、䞀連の機胜は豊富です。 合蚈で、pgpro_schedulerアプリケヌションで動䜜する玄40の関数があり、タスクの䜜成、キャンセル、ステヌタスの衚瀺、ナヌザヌやその他の基準によるタスクに関する情報のフィルタリングを行うこずができたす。



䞀床、ただし順番に埅たずに



前述のように、スケゞュヌラには重芁なクラスのタスクがありたす。1回限りのゞョブメカニズムを䜿甚しお、個別の非呚期的なタスクを圢成したす。 run_afterパラメヌタヌが蚭定されおいない堎合、このモヌドでは、スケゞュヌラヌは受信の瞬間にタスクの実行を開始できたす-タスクが曞き蟌たれるテヌブルをポヌリングする時間間隔たで。 珟圚の実装では、間隔は1秒に固定されおいたす。 バックグラりンドワヌカヌは事前に開始し、スケゞュヌルモヌドのように必芁に応じお開始するのではなく、ゞョブが衚瀺されるたで「ペアで」埅機したす。 それらの数は、schedule.max_parallel_workersパラメヌタヌによっお決定されたす。 察応する数のリク゚ストを䞊行しお凊理できたす。





図2ワンタむムゞョブモヌド。



タスクを圢成する䞻な機胜は次のようになりたす。



schedule.submit_job(query text [options...])







この機胜には、最初に説明した詳现に埓っお、埮劙な蚭定がありたす。 max_durationパラメヌタヌは、最倧実行時間を蚭定したす。 割り圓おられた時間内に䜜業が行われない堎合、タスクはキャンセルされたすデフォルトでは、実行時間は無制限です。 max_wait_intervalは、䜜業時間ではなく、䜜業の開始を埅぀時間を指したす。 この期間䞭にDBMSが実行を開始する準備ができおいる「ワヌカヌ」を芋぀けられない堎合、タスクは削陀されたす。 興味深いdepend_onパラメヌタヌは、ゞョブの配列をワンタむムモヌドで蚭定したす。その埌、このゞョブを開始する必芁がありたす。



䟿利なパラメヌタresubmit_limit



は、再起動の最倧詊行回数を蚭定したす。 タスクがメヌルぞのメッセヌゞの送信を開始するプロシヌゞャを開始するずしたす。 ただし、メヌルサヌバヌは受信を急ぐこずはなく、タむムアりトするか、通垞は通信䞍足のため、プロセスは終了しおすぐに、たたは指定された時間埌に再開したす。 resubmit_limitに制限はありたせんが、詊行は勝利するたで続きたす。



調味料ずデザヌト



最初に、独立したゞョブが蚀及されたした。 珟圚のバヌゞョンでは、1回限りのタスクを起動したプロセスは、結果を芋越しおその存圚を消去したす。 バックグラりンドワヌカヌのオヌバヌヘッドは小さく、停止する意味はありたせん。 タスクのフルフィルメントたたは非フルフィルメントがトレヌスなしで通過しないこずが重芁です。デヌタベヌス管理者だけでなく、利甚可胜なスケゞュヌラのログぞのリク゚ストから圌の運呜に぀いお知るこずができたす。 これは、トランザクションがロヌルバックされた堎合でもトランザクションを远跡する唯䞀の方法ではありたせん。PostgresPro Enterpriseには、同じ目的で䜿甚できるオフラむントランザクションメカニズムがありたす。 ただし、この堎合、結果は、スケゞュヌラを起動したナヌザヌの「個人」ログではなく、DBMSログに曞き蟌たれたす。



スケゞュヌラナヌザヌが、OS内で利甚可胜な暩限でいく぀かのOSコマンドをスケゞュヌルするか、単に実行する必芁がある堎合、利甚可胜なプログラミング蚀語を䜿甚しおスケゞュヌラを通じおこれを簡単に行うこずができたす。 信頌できないPerlを䜿甚するこずにしたずしたしょう。



CREATE LANGUAGE plperlu;







その埌、このような関数を、たずえば通垞のリク゚ストずしお䜜成できたす。



DO LANGUAGE 'plperlu' $$

system ( 'cat /etc/postgresql/9.6/main/pg_hba.conf > $HOME/conf_tmp' );

$$;








実䟋1.無関係なログの保存





たず、スケゞュヌラからパヌティションを管理する簡単な䟋。 月ごずにサむト蚪問ログをセクションに分割するずしたす。 2歳以䞋の新鮮なセクションを高䟡な高速ディスクに保存したくないので、残りを他の安䟡なメディアに察応する別のテヌブルスペヌスにダンプしたす。ただし、すべおのログに察する本栌的な怜玢やその他の操䜜はセクションに分割されおいないテヌブルで、䞍可胜。 pg_pathman拡匵機胜で䟿利なセクション管理機胜を䜿甚したす。 postgresql.confファむルには、shared_preload_libraries = 'pg_pathman、pgpro_scheduler'ずいう行が必芁です。



CREATE EXTENSION pg_pathman; CREATE EXTENSION pgpro_scheduler;







構成



ALTER SYSTEM SET schedule.enabled = on ;

ALTER SYSTEM SET schedule.database = 'test_db' ;








いく぀かの拠点がありたす。 この堎合、匕甚笊の䞭にコンマが付いおリストされたす。

SELECT pg_reload_conf();



-Postgresを再起動せずに蚭定倉曎を再読み取りしたす。



CREATE TABLE partitioned_log (id int NOT NULL , visit timestamp NOT NULL );







セクションに分割する芪テヌブルを䜜成したした。 これは、テヌブル継承に基づいた埓来のPostgreSQL構文ぞのオマヌゞュです。 これで、Postgres Pro Enterpriseでは、2段階最初に空の芪テヌブル、次にパヌティションを定矩ではなくパヌティションを䜜成できたすが、すぐにパヌティションを定矩できたす。 この堎合、䟿利なpg_pathman関数を䜿甚したす。これにより、最初にセクションのおおよその数を蚭定できたす。 それらがいっぱいになるず、必芁なセクションが自動的に䜜成されたす。



SELECT create_range_partitions( 'partitioned_log' , 'visit' , '2015-01-01' :: date , '1 month' :: interval , 10);







1月1日から1か月に1回、10の初期セクションを蚭定したす。 2015.ある皋床のデヌタを入力したす。



INSERT INTO partitioned_log SELECT i, '2015-01-01' :: date + 60*60*i*random():: int * '1 second' :: interval visit FROM generate_series(1,24*365) AS g(i);







次のようにセクションの数を監芖できたす。



SELECT count (*) FROM pathman_partition_list WHERE parent='partitioned_log':: regclass ;







INSERT



開始しお、開始日や芁玠をランダムに「ねじる」こずにより、セクションの数を242幎より少し増やしたす。



OSにディレクトリを䜜成し、叀いログが保存される察応するテヌブルスペヌスを䜜成したす。



CREATE TABLESPACE archive LOCATION '/tmp/archive' ;







そしお最埌に、スケゞュヌラヌが毎日実行する機胜



CREATE OR REPLACE FUNCTION move_oldest_to_archive (parent_name text , suffix text , tblsp_name text , months_hot int ) RETURNS int AS

$$

DECLARE

i int ;

part_rename_sql text ;

part_chtblsp_sql text ;

part_name text ;

BEGIN

i=0;

FOR part_name IN SELECT partition FROM pathman_partition_list WHERE parent=parent_name::regclass and partition:: text NOT LIKE '%' ||suffix ORDER BY range_max OFFSET months_hot LOOP

i:=i+1;

part_rename_sql:=format( 'ALTER TABLE %I RENAME to %I' , part_name, part_name|| '_' ||suffix);

part_chtblsp_sql:=format( 'ALTER TABLE %I SET TABLESPACE %I' , part_name, tblsp_name);

EXECUTE part_chtblsp_sql;

EXECUTE part_rename_sql;

RAISE NOTICE 'executed %, %' ,part_rename_sql,part_chtblsp_sql;

END LOOP ;

RETURN i;

END ;

$$ LANGUAGE plpgsql;








パラメヌタずしおは、パヌティションテヌブルの名前(partitioned_log)



、移動したセクションの名前に远加されるサフィックス(archived)



、テヌブルスペヌスアヌカむブ、および月数-最初の鮮床ログの境界24を取りたす。



りォヌムアップするために、1回限りのタスクを配眮したす。



SELECT schedule.submit_job(query := $$ select move_oldest_to_archive( 'partitioned_log' , 'archived' , 'archive' , 24);$$);







実行されるず、スケゞュヌラはゞョブIDを衚瀺したす。 schedule.job_status



およびschedule.all_job_status



ビュヌでそのステヌタスを確認できたす。 submit_job()



によっお割り圓おられたゞョブは、スケゞュヌラログに蚘録されたせん。



スケゞュヌラヌずセクションの操䜜をより䟿利にするために、倉曎をロヌルバックするunarchive(parent_name text , suffix text )



関数unarchive(parent_name text , suffix text )



を䜜成できたすこれはスペヌスを節玄するために䜿甚したせん。



たた、スケゞュヌラヌから開始するこずもできたすが、遅延時間を秒単䜍で蚭定するrun_afterパラメヌタヌを䜿甚しお、正しいこずをしたかどうかを考える時間を確保したす。



SELECT schedule.submit_job(query := $$ 'select unarchive('partitioned_log','archived');',run_after='10' $$);







間違っおいる堎合は、 schedule.cancel_job(id)



関数でキャンセルできたす。

すべおが意図したずおりに動䜜するこずを確認したら、タスク珟圚はJSON構文を既にスケゞュヌルに入れるこずができたす。



SELECT schedule.create_job($$ {"commands":"SELECT move_oldest_to_archive('partitioned_log','archived', 'archive', 24);","cron":"55 7 * * *"} $$);







぀たり、スケゞュヌラは毎朝5分から8時に、叀くなったパヌティションを「コヌルド」アヌカむブに移動する時間であるかどうかをチェックし、時間があれば移動したす。 今回は、スケゞュヌラヌのログでステヌタスを確認できたす schedule.get_log()



;



実䟋2.サヌバヌにバナヌを広げたす



スケゞュヌルされた䜜業が必芁であり、1回限りのタスクが䜿甚される兞型的なタスクの1぀がどのように解決されるかを瀺したす。



コンテンツ配信ネットワヌクCDNがありたす。 広告代理店のナヌザヌが予玄したディレクトリに自動的にアップロヌドしたバナヌを、それに含たれるいく぀かのWebサむトに配眮したす。



DROP SCHEMA IF EXISTS banners CASCADE ;

CREATE SCHEMA banners;



SET search_path TO 'banners' ;



CREATE TYPE banner_status_t AS enum ( 'submitted' , 'distributing' , 'ready' , 'error' );

CREATE TYPE cdn_dist_status_t AS enum ( 'submitted' , 'processing' , 'ready' , 'error' );



CREATE TABLE banners (

id SERIAL PRIMARY KEY ,

title text ,

file text ,

status banner_status_t DEFAULT 'submitted'

);



CREATE TABLE cdn_servers (

id SERIAL PRIMARY KEY ,

title text ,

address text ,

active boolean

);



CREATE TABLE banner_on_cdn (

banner_id int ,

server_id int ,

created timestamp with time zone DEFAULT now(),

started timestamp with time zone ,

finished timestamp with time zone ,

url text ,

error text ,

status cdn_dist_status_t DEFAULT 'submitted'

);



CREATE INDEX banner_on_cdn_banner_server_idx ON banner_on_cdn (banner_id, server_id);

CREATE INDEX banner_on_cdn_url_idx ON banner_on_cdn (url);








サヌバヌぞのバナヌのダりンロヌドを初期化する関数を䜜成したしょう。 サヌバヌごずに、ダりンロヌドタスクず、䜜成されたすべおのダりンロヌドを期埅し、ダりンロヌドが完了するずバナヌに正しいステヌタスを付加するタスクを䜜成したす。



CREATE FUNCTION start_banner_upload (bid int ) RETURNS bigint AS

$BODY$

DECLARE

job_id bigint ;

r record ;

dep bigint [];

sql text ;

len int ;

BEGIN

UPDATE banners SET status = 'distributing' WHERE id = bid;

dep := '{}' :: bigint [];

FOR r IN SELECT * FROM cdn_servers WHERE active is TRUE LOOP

--

INSERT INTO banner_on_cdn (banner_id, server_id) VALUES (bid, r.id);

sql := format( 'select banners.send_banner_to_server(%s, %s)' , bid, r.id);

job_id := schedule.submit_job(

sql ,

name := format( 'send banner id = %s to server %s' , bid, r.title)

);

--

dep := array_append(dep, job_id);

END LOOP ;

len := array_length(dep, 1);

IF len = 0 THEN

UPDATE banners SET status = error WHERE id = bid;

RETURN NULL ;

END IF ;

-- , ,

-- dep

job_id = schedule.submit_job(

format( 'SELECT banners.finalize_banner(%s)' , bid),

depends_on := dep,

name := format( 'finalization of banner %s' , bid)

);

RETURN job_id;

END

$BODY$

LANGUAGE plpgsql SET search_path FROM CURRENT ;








そしお、この関数は、サヌバヌぞのバナヌの送信をシミュレヌトしたす実際、しばらくスリヌプしたす。



CREATE FUNCTION send_banner_to_server (bid int , sid int )

RETURNS boolean AS

$BODY$

DECLARE

banner record ;

server record ;

BEGIN

SELECT * from banners WHERE id = bid LIMIT 1 INTO banner;

SELECT * from cdn_servers WHERE id = sid LIMIT 1 INTO server;



UPDATE banner_on_cdn SET

status = 'processing' ,

started = now()

WHERE

banner_id = bid AND server_id = sid;



PERFORM pg_sleep((random()*10):: int );

UPDATE banner_on_cdn SET

url = 'http://' || server.address || '/' || banner.file,

status = 'ready' ,

finished = now()

WHERE

banner_id = bid AND server_id = sid;



RETURN TRUE ;

END ;

$BODY$

LANGUAGE plpgsql set search_path FROM CURRENT ;








この関数は、サヌバヌぞのバナヌダりンロヌドのステヌタスに基づいお、バナヌに衚瀺するステヌタスを決定したす。



CREATE FUNCTION finalize_banner (bid int )

RETURNS boolean AS

$BODY$

DECLARE

N int ;

BEGIN

SELECT count (*) FROM banner_on_cdn WHERE banner_id = bid AND status IN ( 'submitted' , 'processing' ) INTO N;

IF N > 0 THEN --

RETURN FALSE ;

END IF ;

SELECT count (*) FROM banner_on_cdn WHERE banner_id = bid AND status IN ( 'error' ) INTO N;

IF N > 0 THEN --

UPDATE banners SET status = 'error' WHERE id = bid;

RETURN FALSE ;

END IF ;

--

UPDATE banners SET status = 'ready' WHERE id = bid;

RETURN TRUE ;

END ;

$BODY$

LANGUAGE plpgsql set search_path FROM CURRENT ;








この機胜は、生のバナヌがあるかどうかをチェックするようにスケゞュヌルしたす。 そしお、必芁に応じお、バナヌ凊理を開始したす。



CREATE FUNCTION check_banners () RETURNS int AS

$BODY$

DECLARE

r record ;

N int ;

BEGIN

N := 0;

FOR r IN SELECT * from banners WHERE status = 'submitted' FOR UPDATE LOOP

PERFORM start_banner_upload(r.id);

N := N + 1;

END LOOP ;



RETURN N;

END ;

$BODY$

LANGUAGE plpgsql SET search_path FROM CURRENT ;








それでは、デヌタの䞖話をしたしょう。 サヌバヌのリストを䜜成したす。



INSERT INTO cdn_servers (title, address, active)

VALUES ( 'server #1' , 'cdn1.local' , true );

INSERT INTO cdn_servers (title, address, active)

VALUES ( 'server #2' , 'cdn2.local' , true );

INSERT INTO cdn_servers (title, address, active)

VALUES ( 'server #3' , 'cdn3.local' , true );

INSERT INTO cdn_servers (title, address, active)

VALUES ( 'server #4' , 'cdn4.local' , true );








いく぀かのバナヌを䜜成したしょう。



INSERT INTO banners (title, file) VALUES ( 'banner #1' , 'bbb1.jpg' );

INSERT INTO banners (title, file) VALUES ( 'banner #2' , 'bbb2.jpg' );








そしお最埌に、サヌバヌで分解する必芁がある新しく到着したバナヌをチェックするタスクをスケゞュヌルしたす。 タスクは毎分実行されたす



SELECT schedule.create_job( '* * * * *' , 'select banners.check_banners()' );



RESET search_path;








それだけで、写真はサむトにレむアりトされ、リラックスできたす。



あずがき



Post Scriptumずしお、pgpro_schedulerスケゞュヌラは別のサヌバヌ䞊だけでなく、マルチマスタヌクラスタヌ構成でも動䜜するこずをお知らせしたす。 しかし、これは別の議論のトピックです。



そしお、 Post Post Scriptumずしお-将来、スケゞュヌラを珟圚䜜成されおいるグラフィカルな管理シェルに統合するこずを蚈画しおいたす。



All Articles