こんにちは。神7の中だと圧倒的に福禄寿が神推しの武井です。
(ちなみに草7の中だとハコベラ神推し)
さて、クラウド業界の中で私が神推ししているkintone。
しかし、完璧というのは傷一つない宝玉のことであり、
傷を見つけては舐り回すのが私の稼業です。
問題提起~根本の根本はどこか~
私は今から一ついい提案をします。
「kintoneには”メンテナンス画面”が絶対的に必要だ」。
何故か。
kintoneは毎月最低1回、6時間ほどに及んで定期メンテナンスをします。
その他、臨時メンテナンスもあります。
メンテナンス中、ずっと利用できないわけではありませんが、
断続的に接続が切れたり、APIコールが失敗するなどします。
この間、kintoneのサービス利用の入り口自体は全く閉じられていません。
すると、どうなるか。
カスタマイズJSや夜間バッチなどでAPIをコールすると、予期せず失敗します。
失敗するということはどういうことかというと、
処理が途中で止まるという前提でプログラムしていない限り、
処理が途中で止まり、何が起こるか不透明な状態になります。
多くの場合、カスタマイズJSでは、新規作成や更新の処理が途中で止まって、レコード保存ができなくなったり、
夜間バッチでは、途中までは更新されたのに失敗したところから先は完全に未処理状態になるという状況になります。
これらの場合、想定通りのデータでない状態、
すなわちデータ不整合が発生することになるのです。
一番根本的な部分は恐らく、割と頻繁にこの長時間メンテナンスをしているという点でしょうが、
不必要に延ばしているわけでもないと思うので、ここはある程度については仕方ないとします。
次に、システム側(kintone側)で強制的に利用できないようにすることはできないのかという点ですが、
年に一度くらいならともかく、この頻度で6時間程度止めるというわけにはいかないのでしょう。
システムによってはその方が被害が大きくなる可能性もあります。
ちなみに2016年6月のkintone稼働率は公式サイト発表で100%ですが、
これは定期メンテナンスの時間を計算から除外している数字です。
メンテナンス中は一切利用できないナイーブなシステムである場合、
実質99.17%まで落ちます。
これをどう感じるかはあなた次第です!m9(・ω・)
個人的には、可能であれば計画停止時間をもっと短くした方がよいと考えています。
例えば2:00~4:00などであれば、夜間バッチならその時間を避けるなどで対処できるからです。
1:00~7:00だと日次夜間バッチはまず使えません。
上記の通り、根源的な問題は計画停止時間が頻繁かつ長すぎることにあると考えており、
その結果、(締め出しが必要なシステムで)強制的に締め出しを行っていないがために、エラーが起こり得る状況を醸成してしまっているのです。
データ不整合が起こるくらいなら利用できなくさせた方が良い(と考えるシステム管理者もいる)はずです。
これが今回の問題提起です。
既述のごとく前者の根源的な問題はいかんともしがたい部分が類推できるとはいえ、
まずここを6時間→3時間くらいには短縮すべきで、
然る後に後者の問題を早急に対処すべきと結論付けます。
温故知新――経験事例を中心に――
今まで私が経験して来た中だと、本件の対応方針は大きく2つに分かれました。
すなわち、
- 何もしない
- メンテナンス画面を作る
のどちらかです。
何もしない場合、事前にユーザーへシステム管理者がメンテナンスのお知らせを発信し、
その中でメンテナンス期間中は利用しないよう呼びかけるといったことが行われていましたが、
その実、全く効果なく、利用されてしまっていました。
その結果、メンテナンス中の操作が原因でデータ不整合が発生していました。
また、日次バッチなどでエラーが発生しているかどうかを目視ないしバッチ内アラート処理で確認し、
常にエラーがあるのかないのかに戦々兢々とし、
エラーがあれば即座に改修と焼き土下座という毎日でした。
一方、メンテナンス画面を作る事例では、
メンテナンスごとに「カスタマイズで作成したメンテナンス画面」を無理矢理表示させていました。
とはいえ、カスタマイズを一切していないような純粋培養のkintoneで、
毎度毎度メンテナンスの度に利用できなくなるのは、むしろ改悪といえるでしょう。
先に結論
標準機能としてメンテナンス画面の表示/非表示を切り替えられる機能が早急に求められる。
これは定期/臨時メンテナンスに対応できるだけでなく、
SIer等による本番環境へのシステム導入の際にも用いることができます。
需要は一定数、確実にあると付言しておきます。
いつものカスタマイズによる対処法の提案
これ以上いたずらにSIer諸兄姉の膝が焼かれないためにも、
ささやかながらメンテナンス画面風味のものを置いておきます。
考え方としては以下の通りです。
- 「メンテナンス管理」というアプリを作成する。
- あらかじめ「メンテナンス管理」アプリにメンテナンス日時を入力しておく。
- 全体JavaScriptでアプリを見に行き、メンテナンス日時の範囲内であれば、メンテナンス画面を表示し、一切操作させない。
- ただし、あらかじめ入力しておいた、システム管理者等の特別なユーザーにはメンテナンス画面を表示しない。
ではまず「メンテナンス管理」アプリを作りましょうね~。
アプリはこんなん作ってください。
- 開始日時(フィールドコードstartDate)
- 終了日時(フィールドコードendDate)
- メンテ非表示(個人)(フィールドコードmPerson)
- メンテ非表示(組織)(フィールドコードmOrg)
次に本題のJavaScriptちゃんをコネコネしちゃいましょうね~。
// メンテナンスアプリを検索 kintone.api("/k/v1/records", "GET", {app: 40, query: "startDate <= NOW() and endDate >= NOW()"}, function (resp) { var records = resp.records; if (records.length > 0) { // メンテ画面を出さない組織のコードを格納 var groupKengen = []; groupKengen = records[0].mOrg.value; // メンテ画面を出さないユーザーのコードを格納 var kojinKengen = []; kojinKengen = records[0].mPerson.value; // 組織を検索 kintone.api('/v1/user/organizations', 'GET', {code: kintone.getLoginUser()['code']}, function (respOrg) { var orgs = respOrg; var findFlg = 0; // 自分の組織が非表示として登録されているかループ L: for (var i = 0; i < orgs.organizationTitles.length; i++) { for (var j = 0; j < groupKengen.length; j++) { if (orgs.organizationTitles[i].organization.code === groupKengen[j].code) { findFlg = 1; break L; } } ; } // 組織で登録されていない場合、自分自身が登録されているかループ if (findFlg === 0) { for (var i = 0; i < kojinKengen.length; i++) { if (kintone.getLoginUser().code === kojinKengen[i].code) { findFlg = 1; break; } } } // 組織もユーザーも該当しない場合、メンテナンス画面を表示 if (findFlg === 0) { var startDate = new Date(records[0].startDate.value); var startMonth = (startDate.getMonth() + 1); var startDay = startDate.getDate(); var startTime = "0" + startDate.getHours(); startTime = startTime.slice(-2); var startMinute = "0" + startDate.getMinutes(); startMinute = startMinute.slice(-2); var endDate = new Date(records[0].endDate.value); var endMonth = (endDate.getMonth() + 1); var endDay = endDate.getDate(); var endTime = "0" + endDate.getHours(); endTime = endTime.slice(-2); var endMinute = "0" + endDate.getMinutes(); endMinute = endMinute.slice(-2); if (location.href.indexOf("k/#/portal") > 0) { var Interval = setInterval(function () { if ($(".gaia-argoui-select.ocean-portal-dropdown-menu[title='表示するアプリを切り替える']").length > 0 && $(".gaia-argoui-appscrollinglist-readmore:last").length > 0 && $(".gaia-argoui-appscrollinglist-list:last").children().length > 0) { clearInterval(Interval); $("body").html(""); $("body").css("background-color", "white"); var $weather = $('<div id="modal" class="box"><div id="box-min" style="padding-left: 20px;"><div id="header"><h2 style="font-size:-webkit-xxx-large;position:relative">【やめとけ】</h2></div><div class="content" style="width: 700px;"><h3 style="font-size:large;position:relative">' + endMonth + '月' + endDay + '日 ' + endTime + ':' + endMinute + 'まで待て!<br><br>【メンテナンス予定】<br>' + startMonth + '月' + startDay + '日 ' + startTime + ':' + startMinute + ' ~ ' + endMonth + '月' + endDay + '日 ' + endTime + ':' + endMinute + '</h3></div></div></div>'); $("body").append($weather); $('#header').append('<img id="images" src="http://blogimg.goo.ne.jp/user_image/48/7e/aff7c9260cc8a4903ccd702d552bbafe.jpg" style="position:absolute; top: 0px; left: 400px">'); setInterval(function () { $("#images").animate({ 'top': Math.round(Math.random() * 90) + "%", 'left': Math.round(Math.random() * 90) + "%" }, 500); }, 100); } }, 100); } else { $("body").html(""); $("body").css("background-color", "white"); var $weather = $('<div id="modal" class="box"><div id="box-min" style="padding-left: 20px;"><div id="header"><h2 style="font-size:-webkit-xxx-large;position:relative">【やめとけ】</h2></div><div class="content" style="width: 700px;"><h3 style="font-size:large;position:relative">' + endMonth + '月' + endDay + '日 ' + endTime + ':' + endMinute + 'まで待て!<br><br>【メンテナンス予定】<br>' + startMonth + '月' + startDay + '日 ' + startTime + ':' + startMinute + ' ~ ' + endMonth + '月' + endDay + '日 ' + endTime + ':' + endMinute + '</h3></div></div></div>'); $("body").append($weather); $('#header').append(''); setInterval(function () { $("#images").animate({ 'top': Math.round(Math.random() * 90) + "%", 'left': Math.round(Math.random() * 90) + "%" }, 500); }, 100); } } }); } } );
これを全体JSとしてアップしてください。(もちろんjQueryも)
アプリIDは40としています。
さて、今回一番の工夫ポイントはどこでしょうか。
正解は、
メンテナンス画面で河童がウネウネするところです!!
これにより、メンテナンス日時の範囲内だと、
どこにいても、JavaScriptが読み込まれるタイミングで、
メンテナンス画面に強制的に移動させることができます!!
ただし、先ほど設定したメンテナンス非表示に指定した者は除きます。
なぜなら、管理者まで操作できなくなったら誰も河童を止められなくなるからです!!!
デメリットとしては、あまりにも多くのユーザーが使うシステムの場合、
めちゃめちゃAPIが叩かれることになります。
(そもそもメンテナンス中にはAPIコールが弾かれてしまうことも……。)
また、ユーザーが操作し続けていた場合の処理を考える必要があります。
やはりその辺りも含めて標準機能で実装していただきたいものです。
河童がウネウネする形で。