woshidan's blog

あいとゆうきとITと、とっておきの話。

データベースのデータのバックアップの種類について

データベーススペシャリストの問題を見ていて、データの更新範囲や内容によって、差分バックアップと増分バックアップの使い分けを検討する話が面白かったのでメモ。

なお、最初に断っておきますが、リストア周りについて実際業務で担当したことはなく、興味があったので調べたことをまとめているだけなので、何かあったらマサカリください。

差分バックアップと増分バックアップとは

インターネットで検索したら、記事によって定義が揺らいでいてそのまま使って大丈夫か、という感じだったので、

http://www.atmarkit.co.jp/ait/articles/0306/14/news003.html https://www.backstore.jp/blog/2016/08/17/differential_and_increment_backup/ http://qiita.com/Tocyuki/items/f34ca92b8bf6e0014923

あたりを参考にまずこの記事の中でいちおう言葉の定義をしておきます。それで肝心の定義ですが、

  • 毎回、最後にフルバックアップを取った時点からのデータの差分のバックアップを取るものを 差分バックアップ
  • 最初にバックアップを取るときは、最後にフルバックアップを取った時点からのデータの差分となるが、それ以降は前回バックアップを取った時からの差分になるものを 増分バックアップ

とします。図にすると下図のようになります。

差分バックアップと増分バックアップの使い分け

データの更新され方による使い分けの話

取り上げられていたのは、平成28年度の午後Iの問題なのですが、

  • 結構大きなテーブルで、一部だけが頻繁に更新されるテーブルのバックアップを取る場合
    • 更新範囲が重複しがちなことがわかっている場合、差分バックアップにすると増分バックアップより適用する必要のあるバックアップの量が小さくなる
  • 何かの履歴といった増えることはあっても後から更新されることがないテーブルのバックアップを取る場合
    • 差分バックアップにしてしまうとフルバックアップを取った時点から遠くなるにつれ一度にバックアップを取る量が増えてしまうので増分バックアップにする

という趣旨の内容で、いままで二種類あるけどよくわからないな、差分は容量が増えて悪いやつだ、みたいなゆるふわなイメージを持っていた自分には面白かったのでした。

運用に関するもの

www.backstore.jp www.atmarkit.co.jp

差分バックアップと増分バックアップの定義について調べていたら、そういえば、復旧作業に関する話も出てきていたのでこちらもメモ。

さて、問題に取り上げられていたような巨大な業務用システムのデータベースなどだったら、バックアップのデータをバックアップ用のサーバに転送する時間もバカにならないので上記のようなことを計算してみて考える必要がありますが、実際、ノートPC上のファイルの中身程度だったら毎回フルバックアップで十分ということもあります。

増分バックアップは復元時の各バックアップファイルを適用していく作業が煩雑だったり、途中のバックアップファイルが欠落した場合戻せなくなってしまったりするため、手動で管理する必要がある場合は大変そうです。

差分バックアップはそれぞれのタイミングでのバックアップのデータサイズが前節であげた場合以外は大きくなりがちで、バックアップを取りたいタイミングが増えるにつれて容量を圧迫しがちですが、更新作業に必要となるのは、フルバックアップ + 差分バックアップのファイルのみであり、手順もわかりやすく復旧処理が簡単だそうです。

実際のコマンドについて調べて書こうかと思ったけど、火傷しそうだったのでやめます。

emulator: ERROR: This AVD's configuration is missing a kernel file!

エミュレータネタ連発。上記エラーが発生した時に確認することを順にメモ。

エラーメッセージ全体

Cannot launch AVD in emulator.
Output:
emulator: ERROR: This AVD's configuration is missing a kernel file! Please ensure the file "kernal-qemu" is in the same location as your system image.
emulator: ERROR: ANDROID_SDK_ROOT is defined (/Users/woshidan/Library/Android/sdk) but cannot find kernel file in (/Users/woshidan/Library/Android/sdk/system-images/) sub directories

発生したエミュレータAPIバージョンとABIとどこ製か

一部のエミュ(主にARM系)のエミュにはGoogle社製のものとAndroid Open Source Projectによるものがあって、自分の手元で発生したのは

で発生。

kernal-qemuとは

QEMUとは - OSS用語 Weblio辞書

QEMU(仮想マシンエミュレータ)のハード的なところとソフト的なところをつないでくれるソフト。カーネル。 どうも、Androidエミュレータは、QEMUを利用してARM系のエミュをx86などのCPUのPC上で動かしているそうです。

今回のエラーはエミュレータ作成の時に指定したターゲットに対して、このファイルがないと言われています。

実はでもなく、システムイメージが何かよくわかってないので用語や文末がふわふわですが、エミュレータに指定できるターゲットに相当するディレクトリがありまして、そのディレクトリの中に kernal-qemu がないと言われていることになります。

発生した場合に確認すること

  • SDK Manager上で該当システムイメージがインストールされているか
  • システムイメージが入っているディレクトリを開いてみて kernal-qemuがあるか
    • ディレクトリのパスの例
      • Google社製 /Users/woshidan/Library/Android/sdk/add-ons/addon-google_apis-google-16-1/images/armeabi-v7a
      • Android Open Source Project製 /Users/woshidan/Library/Android/sdk/system-images/android-15/google_apis/x86

実際に当該ディレクトリを覗いてみた例

// エラーが出ない
$ ls /Users/woshidan/Library/Android/sdk/system-images/android-15/google_apis/x86
NOTICE.txt      kernel-ranchu       source.properties
build.prop      package.xml     system.img
kernel-qemu     ramdisk.img     userdata.img
// エラーが出る
$ ls /Users/woshidan/Library/Android/sdk/add-ons/addon-google_apis-google-16-1/images/armeabi-v7a
NOTICE.txt  build.prop  ramdisk.img system.img  userdata.img

どうするか

本来はないと言われているkernel-qemuを調達できればいいのですが、いまのところ、Linuxよくわかってないし、方法不明*1。 もしかすると、SDK Managerをアップデートした後、システムイメージをアンインストールして再インストールすると直るかもしれないですが、最新バージョンのシステムイメージが欲しい場合などを除き、Android Open Source Projectの方のディレクトリには kernel-qemu が含まれていたため、未検証です。

また、自分がこの不具合に遭遇したARM系というのはたいていの実機が該当するので、実機で検証するか実機が欲しいと交渉した方が早いかもしれません。

現場からは以上です。

*1:誰かわかったら教えてください

ERROR: resizing partition e2fsck failed with exit code 8

stackoverflow.com

Androidエミュレータの設定をいじっている最中、上図のエラーダイアローグを稀によく見かけていたような気がするのですがいままで原因がわからないうちになんか回避してしまっていたのでした。その原因と対応がなんとなくわかったのでメモ。

Androidエミュレータの設定やデータは /Users/${username}/.android/avdディレクトリの中にそれぞれのエミュレータ専用の設定ファイル(.ini)と設定ファイルやデータをしまうためのディレクトリ(.avd)が用意されています。

avd$ pwd
/Users/woshidan/.android/avd
avd$ ls
5.1_WVGA_API_21.avd
5.1_WVGA_API_21.ini
API15.avd
API15.ini
...

個々のエミュレータ用の設定やデータが入っている.avdの中身は以下となっています。

API15.avd$ ls
cache.img       hardware-qemu.ini   userdata.img
config.ini      sdcard.img
emulator-user.ini   userdata-qemu.img

件のエラーはこの中のデータなどが入っているファイルシステムである .imgファイルの形式がおかしいため起こるようです。これは、エミュレータの設定をいじってから再起動などをしていると起こることがあります。

エラーメッセージ中にあるe2fsckコマンドについてはこちらによると、

ext2/ext3/ext4ファイルシステムの整合性をチェックし、修復する。マウント中のファイルシステムに対してはこのコマンドを実行しないようにする(修復をかけるとファイルシステムが壊れる恐れがある)。

なので、このコマンドがうまく走ってくれたらファイル形式の問題は解決しそうです。

また、一部の .imgファイルは起動時に存在しなければ作り直されるので、中に入っているデータに未練がなければ消して作り直してみてもよさそうです。エミュだし。ということで、

  • 手動で e2fsck を各ファイルにかけてみる
  • cache.img, userdata-qemu.imgを削除する
  • エミュレータを作り直す

などで解決しそうですというか、私は2つ目と3つ目で解決した後StackOverflowで一つ目の方法を見つけたのでした。

現場からは以上です。

AppiumでiOSのテストのサンプルを動かすところまで

iOSAndroidのUIテストをSeleniumのように書くことができるAppiumというものがあるそうですが、 説明を見ていてもさっぱり挙動がわからなかったので、動かしてみたメモです。

各種のバージョンは

  • Appium 1.5.3
  • maevn 3.3.9

です。スクショにAviraが映っていますが、昨日のAvast誤検知祭りの名残です。

DLしてきたAppiumを起動

Appium: Mobile App Automation Made Awesome.からAppiumをDLしてきて起動してみます。

f:id:woshidan:20170223064606p:plain

f:id:woshidan:20170223064619p:plain

テストが実行される様子

今回は

github.com

のサンプルコードを実行してみます。

また、サンプルコードの実行にはmaevnが必要なので事前にDLしておきます。

f:id:woshidan:20170223064718p:plain

f:id:woshidan:20170223064731p:plain

iOSのシミュレータが起動。 f:id:woshidan:20170223064810p:plain

f:id:woshidan:20170223064841p:plain

f:id:woshidan:20170223064852p:plain

デフォルトではテスト終了後iOSのシミュレータはシャットダウンされます。 f:id:woshidan:20170223065230p:plain

参考

qiita.com

iOSのidentifierForVendorとadvertisingIdentifierについて

お仕事で調べる必要があったのでメモ。

identifierForVendor

https://developer.apple.com/reference/uikit/uidevice/1620059-identifierforvendor?language=objc

  • アプリのベンダー(開発元)ごとにデバイスが一意に見えるように発行されるアルファベットと数字からなる文字列
  • もう少し具体的に言うと、アプリのベンダーを識別するためにvendor IDというものを発行していて、vendor IDに対して一意
    • vendor IDは基本的にApple Play Storeに登録された情報をもとに決まる
    • Play Storeに登録されていないアプリ(B to Bなど)の場合はBundle IDをもとに決定するが、iOS6以前とiOS7以降で異なる
      • iOS6: bundle IDの区切りの先頭2つ(1つしかない場合はBundle ID全体)
      • iOS7: bundle IDの最後以外の要素(1つしかない場合はBundle ID全体)
  • nilになるような場合
    • しばらく待ってから再び値を取得し直す
    • どういう事例があるかと言うと、ユーザーがアンロックしているのに、デバイスが再起動しているとき(ユーザーがアンロックしている状態で起動したとき?)
  • 同じベンダーのアプリを全部アンインストールしてから再インストールすると値が変わる
    • 同じベンダーのアプリが1つでも残っていたら値が変わらない

ASIdentifierManager

https://developer.apple.com/reference/adsupport/asidentifiermanager

  • 広告を提供する専用のIDへのアクセスを提供
  • これはユーザーが広告トラッキングを制限しているかどうかを示すフラグ
  • このクラスは広告を提供するシステムを実装する開発者によって利用されることを想定
    • 一般のアプリ開発者は、彼らが開発する広告用SDKとのリンクのためであっても、このクラスに直接メッセージを送る必要はない
  • 広告IDの取得方法
    • ASIdentifierManagerクラスのsharedインスタンスを取得
    • 広告トラッキングが制限されているかどうかをisAdvertisingTrackingEnabledで指定
    • 広告IDをadvertisingIdentifierプロパティを通して取得する(注意: 広告トラッキングが制限されている場合、この値は00000000-0000-0000-0000-000000000000(In iOS 10.0 and later))

advertisingIdentifier

https://developer.apple.com/reference/adsupport/asidentifiermanager/1614151-advertisingidentifier

  • アルファベットの文字列でデバイス毎に一意になるように発行される数字の列
  • UIDeviceから取得できるidfvとは異なり、この値はすべてのベンダーのアプリ内で同一の値を返す
  • このIDは変化するかもしれないので、キャッシュしてはいけない
    • たとえば、ユーザーがデバイス(との連携?)を消したなどで
  • 広告トラッキングが制限されている場合、iOS10.0かそれ以降ですべて0の文字列になっている
  • 値がnilの場合
    • この値は再取得される
    • これが起きる理由としてはユーザーがアンロックしているのに、デバイスが再起動しているとき(ユーザーがアンロックしている状態で起動したとき?) (IFDVと同様)

isAdvertisingTrackingEnabled

https://developer.apple.com/reference/adsupport/asidentifiermanager/1614148-isadvertisingtrackingenabled

  • ユーザーが広告トラッキングを制限しているかどうかを示す値
  • このプロパティの値を広告トラッキングを行う前に確認すること
    • もしこの値がFALSEであるならば、広告IDは次の限られた目的にしか使えない
      • 帯域制限
      • 属性
      • コンバージョンイベント
      • ユニークユーザー数の推定
      • 不正な広告の検出
      • デバッグ
        • iOS10以降だとユニークユーザー数の推定にも使えないような?

後からコード上でBitmapを与える場合、ImageViewのadjustViewBounds=trueの指定が動かない

結論

onMeasureで一部人間にはよく分からない動きをしている気がします。

  • ImageViewのandroid:adjustViewBounds属性がtrueで、ImageViewの幅などを直接指定していない場合
    • ImageViewを許される限り拡大した場合の高さと幅を求める(widthSize/heightSize)
    • 上記の高さ or 幅を適用したとして、他辺の長さを求める(newWidth/newHeight= widthSize * 縦横比の場合 / heightSize / 縦横比)*1
    • 下記の最小値 >=newWidth/newHeightの場合、ImageViewのリサイズが行われる
      • DrawableのサイズとPaddingの合計値(in pixel)
      • 親Viewから許容されている値(in pixel)
      • android:minHeightあるいはandroid:minWidthで指定している値(in pixel)
    • 上記の最小値 < newWidth/newHeightの場合、ImageViewのリサイズが行われない
    • 現状のImageViewとDrawableのアスペクト比がほぼ同じ場合もonMeasureでリサイズしない

コード読み

主に関係あるのはImageViewのonMeasureメソッドなのでこれを読んでいきます。

また、下記のケースは親Viewから具体的にImageViewの大きさが制限されていない*2場合のケースとなります。

android:adjustViewBounds="true"の場合、まずDrawableのサイズを取得します。

            w = mDrawableWidth;
            h = mDrawableHeight;
            if (w <= 0) w = 1;
            if (h <= 0) h = 1;

            // We are supposed to adjust view bounds to match the aspect
            // ratio of our drawable. See if that is possible.
            if (mAdjustViewBounds) {
                resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
                resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
                
                desiredAspect = (float) w / (float) h;
            }

そのあとにPaddingの値を取得します。

        int pleft = mPaddingLeft;
        int pright = mPaddingRight;
        int ptop = mPaddingTop;
        int pbottom = mPaddingBottom;

そして、現在の条件でImageViewを拡大していい最大サイズの計算をします。

        if (resizeWidth || resizeHeight) {
            /* If we get here, it means we want to resize to match the
                drawables aspect ratio, and we have the freedom to change at
                least one dimension. 
            */

            // Get the max possible width given our constraints
            widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);

            // Get the max possible height given our constraints
            heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);

次に、縦か横をこのどちらかの大きさにあわせた場合のもう一辺の長さを計算します。

// 横方向にリサイズしていい場合は、まず縦の長さを目一杯撮った場合の横幅(newWidth)の計算をします
                    // Try adjusting width to be proportional to height
                    if (resizeWidth) {
                        int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
                                pleft + pright;

                        // Allow the width to outgrow its original estimate if height is fixed.
                        if (!resizeHeight && !mAdjustViewBoundsCompat) {
                            widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
                        }

新しく計算した長さが、先ほど計算したImageViewを拡大していい最大サイズ以下であればリサイズをします。

if (newWidth <= widthSize) {
    widthSize = newWidth;
    done = true;
} 

横方向にリサイズできなかった場合は、縦方向のリサイズを試みます。

resolveAdjustedSizeの中身ですが、MeasureSpecの値が、MeasureSpec. AT_MOSTの場合、

  • DrawableのサイズとPaddingの合計値(in pixel)
  • 親Viewから許容されている値(in pixel)
  • android:minHeightあるいはandroid:minWidthで指定している値(in pixel)

の最小値となっています。

    private int resolveAdjustedSize(int desiredSize, int maxSize,
                                   int measureSpec) {
        int result = desiredSize;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize =  MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                /* Parent says we can be as big as we want. Just don't be larger
                   than max size imposed on ourselves.
                */
                result = Math.min(desiredSize, maxSize);
                break;
            case MeasureSpec.AT_MOST:
                // Parent says we can be as big as we want, up to specSize. 
                // Don't be larger than specSize, and don't be larger than 
                // the max size imposed on ourselves.
                result = Math.min(Math.min(desiredSize, specSize), maxSize);
                break;
            case MeasureSpec.EXACTLY:
                // No choice. Do what we are told.
                result = specSize;
                break;
        }
        return result;
    }

現場からは以上です。

*1:少し大雑把なので、正確にはこの辺 https://github.com/android/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/ImageView.java#L1010

*2:親Viewのandroid:layout_widthがwrap_contentなどで、MeasureSpec.getModeでMeasureSpec.AT_MOSTが返ってくる場合

RubyとrbenvとgemとBundlerと環境ごとのgem

いまさらですが、こないだ会社でRailsに携わるもののたしなみとしてnokogiriのインストールにはまったときに先輩に教えていただいたときのメモ。

  • rbenvでRubyのバージョンを管理している場合、local*1やglobal*2を管理している際、systemと表記されているのはrbenvを介さずにPCにインストールされているRubyを指す
    • MacだとデフォルトでインストールされているRubyがsystemと表記されているのを見かけるのが多い
  • rbenvないし、systemと表される開発者のPCにインストールされたRubyに対する形でgemをインストールしていく
    • gem(RubyGems)はRuby に特化した apt-get と同じようなパッケージングシステムでgemはこれに対応する形で公開されている
  • Bundlerはカレントディレクトリに置かれたGemfileの内容に基づいて自身以外のgemの依存関係を管理するgemである.
    • gemであるので、バージョンや管理場所が異なるRubyごとにインストールする必要がある
    • /Users/woshidan/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/rails-4.2.1 のようにRubyに対応する場所にインストールされているgemをGemfileで指定された条件で利用させるようにする

f:id:woshidan:20170130224653p:plain

あわせてよみたい参考記事。

qiita.com

qiita.com

オブジェクト指向スクリプト言語 Ruby | ライブラリ

*1:特定のディレクトリ以下でのバージョン. globalより優先

*2:rbenvで管理している範囲全体に適用