偏った言語信者の垂れ流し

2014-07-19

[]Nexus4のタッチスクリーンディスプレイを交換した

去年の秋ごろからNexus4を使っていますが、先日歩いているときに地面に落としてしまい、タッチパネルのガラスが割れてしまいました。

f:id:nullpobug:20140719150330j:image

サイドのボタンは動作して画面は表示されるのですが、タッチパネルはどこをタップしても完全に反応しない状態になってしまいました。

新品や中古が安いならそれを買えばいいかと思って調べましたが、値段が全然下がってなく高いままだったので、修理するほうが良い様子。

修理方法をウェブ検索で調べたところ、以下のページの見つけたので参考にさせて頂きました。

スマホ Nexus4 タッチパネル ディスプレイ の 交換修理|むるしすてむ

分解、パーツ交換は自己責任

交換用パーツの入手

交換用のパーツは国内ではやはり手に入らないようなので、eBayから購入して輸入する必要があります。各種手続きなど面倒なので、代行サービスを探して利用することにしました。

いくつか見てみたところ、eBayの商品の落札から国内発送まですべてやってくれるセカイモンというサービスがあったので、今回はこれを利用することにしました。

eBay公認日本語オークション セカイモン

「USA LCD Display Touch Screen Digitizer Glass Assembly For LG google nexus 4 E960」で検索すると該当のパーツが出てきます。45ドル程度。

セカイモン経由で購入して手数料、送料すべて込みで、7,717円で入手出来ました。

端末の分解とパーツ交換

分解とパーツ交換の作業については、参考にしたページで紹介されていた動画が参考になりました。

http://www.youtube.com/watch?v=ZrEEVqXg8G8

背面のカバーを外すために、星形(T5)の精密ドライバーが必要です。また、中のパーツなどを外すためには、プラスの精密ドライバーが必要です。

背面のカバーはツメの引っかかりを外すのに少し苦労。マイナスの精密ドライバ2本を使って何とか開けました。うまくやらないと側がボロボロになりますね。

f:id:nullpobug:20140719150331j:image

次にタッチスクリーンディスプレイですが、これは粘着剤(両面テープ?)のようなものでかなり強力にくっついてるので、無理やり剥がすしかなかったです。端の部分はガラスを細かく割りながらマイナスドライバで削り剥がしました。

交換作業で一番時間かかったのがこの工程で、端の取れにくい部分を除去するのに1時間程度かかってしまいました。

f:id:nullpobug:20140719150332j:image

f:id:nullpobug:20140719150333j:image

バラしたパーツとこれから取り付ける購入したパーツです。

f:id:nullpobug:20140719150334j:image

取り付けから蓋を閉じるまでの細かい作業も動画が参考になりました。

タッチパネルを固定するための両面テープは、幅が細く(5mm)て厚さが薄めのものをホームセンターで購入し、大きすぎる場合はハサミで切ってサイズを調整しました。

取り付けが完了した状態のもの。

f:id:nullpobug:20140719150335j:image

電源を入れて問題なく動くことを確認できました。

f:id:nullpobug:20140719150336j:image

作業開始から終了までだいたい3時間ぐらいでした。

2014-07-18

[]Djangoでprefetch_relatedを使ってクエリ数を減らす

prefetch_relatedはDjango 1.4で追加された機能です。

親子関係を表すモデル(多対多になってるものなど)をツリー状に表示する場合、ループ内でクエリを実行しってしまうと、クエリ数が多くて極端に遅くなります(特に2段目とか3段目)。

prefetch_relatedを使うと、事前にリレーション先のデータを取得しておき、ループ内で新たにクエリが実行されないようにできます。

試したバージョンは、Python2.7、Django1.6.5です。

ソースコード

完全なコードはbitbucketに置いてます。

tokibito / sample_nullpobug / source / django / market — Bitbucket

shop/models.py
# coding: utf-8
from django.db import models

class Category(models.Model):
    "カテゴリ"
    name = models.CharField(max_length=40)

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = 'category'

class Item(models.Model):
    "商品"
    name = models.CharField(max_length=40)
    code = models.CharField(max_length=10, unique=True)
    price = models.IntegerField()
    category = models.ForeignKey('Category')

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = 'item'

class Bundle(models.Model):
    "まとめ売り"
    name = models.CharField(max_length=40)
    price = models.IntegerField()
    items = models.ManyToManyField('Item', through='BundleItem')

    def __unicode__(self):
        return self.name

    class Meta:
        db_table = 'bundle'

class BundleItem(models.Model):
    "まとめ売り商品"
    bundle = models.ForeignKey('Bundle')
    item = models.ForeignKey('Item')

    class Meta:
        db_table = 'bundle_item'

django-extensionsのgraph_modelsコマンドで出力すると、こういう構造になっています。

f:id:nullpobug:20140718224950p:image

shop/templates/index.html

テンプレートファイルは、どちらのビュー関数でも同じものを使います。

<html>
<body>
<ul>
{% for bundle in bundles %}
  <li>{{ bundle.name }}
    <ul>
    {% for item in bundle.items.all %}
      <li>{{ item }} - {{ item.category }}</li>
    {% endfor %}
    </ul>
  </li>
{% endfor %}
</ul>
</body>
</html>

bundle、itemのところで2重のforループになっています。また2つ目のほうは、bundleからitemを、itemからcategoryを取得して使うようなテンプレートになっています。

shop/views.py

prefetch_relatedを使わない場合と使う場合のビュー関数をそれぞれ用意して、urls.pyに設定しておきます。

# coding: utf-8
from django.shortcuts import render
from shop.models import Category, Item, Bundle


def no_prefetch_view(request):
    # prefetch_relatedを使わない場合
    bundles = Bundle.objects.all()
    return render(request, 'index.html', {'bundles': bundles})


def prefetch_view(request):
    # prefetch_relatedを使う場合
    bundles = Bundle.objects.prefetch_related('items__category')
    return render(request, 'index.html', {'bundles': bundles})

prefetch_relatedを使う場合、今回はテンプレート中でbundle→item→categoryの順でツリー状に表示するため、「items__category」までをプリフェッチ対象に指定しています。

実行結果

URLアクセスすると、どちらも同じ表示になります。

f:id:nullpobug:20140718224953p:image

prefetch_relatedを使わない場合

debug-toolbarでSQLをみてみると、クエリ数は9回になっています。

テンプレート中の「bundle.items.all」の部分と、「item.category」の部分でクエリが実行されているためです。

f:id:nullpobug:20140718224951p:image

prefetch_relatedを使う場合

debug-toolbarでSQLをみてみると、クエリ数は4回になっています。

bundle_idをINで指定してitemテーブル、category_idをINで指定してcategoryテーブルのデータをまとめて取得しています。

f:id:nullpobug:20140718224952p:image

このようにprefetch_relatedを使うことで、いくらかクエリ数を減らせます。

参考

2014-06-22

[][]Django 1.5.3以降で変更されたセッションシリアライズについて

Djangoセッションデータは、settings.SESSION_ENGINEで指定されたクラスによって保存されます。

Django 1.5.3より前のバージョンでは、セッションデータのオブジェクトシリアライズ、デシリアライズする処理は、ここで指定されるバックエンドクラスに任されていました。

デフォルトでは、pickleモジュールでダンプして、ハッシュ値+base64エンコードした文字列になっていました。

Django 1.5.3以降では、このpickleモジュールでダンプしていた部分が分離され、新たにsettings.SESSION_SERIALIZERという設定項目が追加されました。

SESSION_SERIALIZER

Django 1.5.3以降の1.5系では、settingsにSESSION_SERIALIZERを指定しない場合は従来通りpickleモジュールが使用されます。

また、Django 1.6では、デフォルトシリアライザが、jsonモジュールを使ってダンプするJSONSerializerに変更されました。

SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'

JSONSerializerの場合、jsonモジュールのdumpsでシリアライズできないオブジェクトセッションに入れると、保存できずエラーになります。

例えば、Pythonのobjectクラスを継承したインスタンスや、その他jsonモジュールが対応していない値の型、データ構造などが該当します。

Django 1.6以降で従来通り、Python任意オブジェクトを保存したい場合は、settings.SESSION_SERIALIZERにPickleSerializerを指定するとよいでしょう。

SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

Python以外で作られた別のシステムと連携するのであれば、pickleデータは扱いづらい可能性が高いので、JSONSerializerを使うか、その他自前でシリアライザを作成するとよいでしょう。

2014-06-19

[][]cgroupを使ってCPUとメモリの割り当てを制限する

cgroupを使ってCPUとメモリの割り当てを制限してみたけど、忘れてしまいそうなのでメモを残す。

試した環境はCentOS 6.4。

インストール

CentOS6でcgroupを使うにはlibcgroupをインストール

$ sudo yum install libcgroup

cgconfigサービスを起動すると、/cgroup/ディレクトリが有効になる。自動で起動させるならchkconfigで設定。

$ sudo chkconfig cgconfig on
$ sudo service cgconfig start

気にしそうなところ

  • cpuやmemoryなど利用可能なサブシステムはOSによって異なる?
  • cgconfigサービスで起動時にマウントされるサブシステムは/etc/cgconfig.confで設定
  • cgredサービスを起動すると、ルールベースでプロセスをグループに自動で追加してくれる
  • コマンド群で各種操作できる(lscgroup, cgcreate, cgdelete, cgexecなど)
  • cgsnapshotコマンドは、現在のcgroupの状態をcgconfig.confの形式で標準出力
  • 1グループごとのリソース制限の指定になるので、リソース制限する単位でグループを作る
  • プロセスをフォークした場合、同じグループに所属する
  • cgroupを指定してコマンドを実行するならcgexecを使う
  • cgcreateは既に存在しているグループ名を指定してもエラーにならない(シェルスクリプトでチェック機構なしに毎回実行して大丈夫だった)

グループ作成

今回は、コマンドで作成する。cgconfig.confに設定を書いて有効にする方法もある。

my-groupという名前で作ってみる。サブシステムcpuとmemoryの制限を指定するのでこんな感じ。

$ sudo cgcreate -g cpu,memory:/my-group

作成できたかを確認するにはlscgroupで一覧を見る。

$ sudo lscgroup

作成できていれば、/cgroup/cpu/my-group/と/cgroup/memory/my-group/ディレクトリが作成されている。

$ ls /cgroup/cpu/my-group/
cgroup.event_control  cgroup.procs  cpu.cfs_period_us  cpu.cfs_quota_us  cpu.rt_period_us  cpu.rt_runtime_us  cpu.shares  cpu.stat  notify_on_release  tasks
$ ls /cgroup/memory/my-group/
cgroup.event_control  memory.failcnt      memory.limit_in_bytes      memory.memsw.failcnt         memory.memsw.max_usage_in_bytes  memory.move_charge_at_immigrate  memory.soft_limit_in_bytes  memory.swappiness      memory.use_hierarchy  tasks
cgroup.procs          memory.force_empty  memory.max_usage_in_bytes  memory.memsw.limit_in_bytes  memory.memsw.usage_in_bytes      memory.oom_control               memory.stat                 memory.usage_in_bytes  notify_on_release

このディレクトリ内のファイルに値を書き込めば各種制限を変更できる。

CPUの割り当て制限

cpu.cfs_period_usとcpu.cfs_quota_usの値を調整する。

$ cat /cgroup/cpu/my-group/cpu.cfs_period_us
100000
$ cat /cgroup/cpu/my-group/cpu.cfs_quota_us
-1

1コアの50%に制限するにはcpu.cfs_quota_usに50000と書き込む(cfs_period_usの半分)

$ sudo sh -c "echo 50000 > /cgroup/cpu/my-group/cpu.cfs_quota_us"

メモリの割り当て制限

memory.limit_in_bytes(ユーザーメモリの上限)とmemory.memsw.limit_in_bytes(ユーザーメモリ+スワップの上限)の値。

$ cat /cgroup/memory/my-group/memory.limit_in_bytes
9223372036854775807
$ cat /cgroup/memory/my-group/memory.memsw.limit_in_bytes
9223372036854775807

ユーザーメモリ(物理メモリ)を100MB、スワップを500MBに制限する。

$ sudo sh -c "echo 100M > /cgroup/memory/my-group/memory.limit_in_bytes"
$ sudo sh -c "echo 500M > /cgroup/memory/my-group/memory.memsw.limit_in_bytes"

動作の検証

こういうリソースの割り当て制限の検証に便利なツールってあるのだろうか?知らない。

とりあえずスクリプトを作って動作させて、topコマンドやログを眺めるぐらいしかしてない。

CPUの割り当て制限の確認

無限ループCPUを食いつぶすスクリプトを作る。

test_cpu.py

while True:
    pass

実行結果:

$ ps aux|grep py$
root      5631  0.0  0.1 173332  2628 pts/1    S+   22:42   0:00 sudo cgexec -g cpu,memory:my-group python test_cpu.py
root      5632 49.5  0.1 115044  3720 pts/1    R+   22:42   1:00 python test_cpu.py

CPUの使用率が50%前後になった。

メモリの割り当て制限の確認

文字列を連結していってメモリを使い切るようなスクリプトを作る。

test_memory.py:

s = "0123456789" * 100000
while True:
    s += s

実行結果:

$ sudo cgexec -g cpu,memory:my-group python test_memory.py  # 数秒後に終了する
$ sudo less +G /var/log/messages
Jun 19 22:45:50 localhost kernel: python invoked oom-killer: gfp_mask=0xd0, order=0, oom_adj=0, oom_score_adj=0
Jun 19 22:45:50 localhost kernel: python cpuset=/ mems_allowed=0
Jun 19 22:45:50 localhost kernel: Pid: 5671, comm: python Not tainted 2.6.32-358.el6.x86_64 #1
Jun 19 22:45:50 localhost kernel: Call Trace:
Jun 19 22:45:50 localhost kernel: [<ffffffff810cb5d1>] ? cpuset_print_task_mems_allowed+0x91/0xb0
Jun 19 22:45:50 localhost kernel: [<ffffffff8111cd10>] ? dump_header+0x90/0x1b0
Jun 19 22:45:50 localhost kernel: [<ffffffff8121d0bc>] ? security_real_capable_noaudit+0x3c/0x70
Jun 19 22:45:50 localhost kernel: [<ffffffff8111d192>] ? oom_kill_process+0x82/0x2a0
Jun 19 22:45:50 localhost kernel: [<ffffffff8111d0d1>] ? select_bad_process+0xe1/0x120
Jun 19 22:45:50 localhost kernel: [<ffffffff8111d912>] ? mem_cgroup_out_of_memory+0x92/0xb0
Jun 19 22:45:50 localhost kernel: [<ffffffff81173454>] ? mem_cgroup_handle_oom+0x274/0x2a0
Jun 19 22:45:50 localhost kernel: [<ffffffff81170e90>] ? memcg_oom_wake_function+0x0/0xa0
Jun 19 22:45:50 localhost kernel: [<ffffffff81173a39>] ? __mem_cgroup_try_charge+0x5b9/0x5d0
Jun 19 22:45:50 localhost kernel: [<ffffffff81174b3d>] ? mem_cgroup_try_charge_swapin+0xbd/0xd0
Jun 19 22:45:50 localhost kernel: [<ffffffff811438ed>] ? handle_pte_fault+0x35d/0xb50
Jun 19 22:45:50 localhost kernel: [<ffffffff8104baa7>] ? pte_alloc_one+0x37/0x50
Jun 19 22:45:50 localhost kernel: [<ffffffff8114431a>] ? handle_mm_fault+0x23a/0x310
Jun 19 22:45:50 localhost kernel: [<ffffffff810474c9>] ? __do_page_fault+0x139/0x480
Jun 19 22:45:50 localhost kernel: [<ffffffff810097cc>] ? __switch_to+0x1ac/0x320
Jun 19 22:45:50 localhost kernel: [<ffffffff8150d6e0>] ? thread_return+0x4e/0x76e
Jun 19 22:45:50 localhost kernel: [<ffffffff8151311e>] ? do_page_fault+0x3e/0xa0
Jun 19 22:45:50 localhost kernel: [<ffffffff815104d5>] ? page_fault+0x25/0x30
Jun 19 22:45:50 localhost kernel: Task in /my-group killed as a result of limit of /my-group
Jun 19 22:45:50 localhost kernel: memory: usage 102324kB, limit 102400kB, failcnt 179657
Jun 19 22:45:50 localhost kernel: memory+swap: usage 512000kB, limit 512000kB, failcnt 27
..(以下略)

cgroupの制限でkillされたログが出ている。

参考

2014-06-16

[]Python札幌勉強会で話してきました

Python札幌勉強会でPython3、Django、ApacheSparkの話題で話してきました。

slideshareに資料をアップロード済み。

今回の札幌訪問はオープンソースカンファレンス北海道に参加したり、えにしテックさんのオフィスを訪問したりして、いろいろ話も聞けたのでなかなかよかった。

自宅から羽田空港までオートバイ移動は天気悪くなければありだなー。電車を使うより時間かからないし、始発と終電に縛られない。