Living a Simple Life is a Happy Life

有饭吃,自由自在,就非常开心

svn1.7+ Zsh报错

| Comments

更新svn(subversion)>1.7后 zsh 的svn自动完成会报错。

_arguments:comparguments:312: invalid argument: [--cl]:arg:

修改方法:

打开文件:/usr/share/zsh/4.3.11/functions/_subversion

找到35行左右

${=${${${(M)${(f)"$(LC_ALL=C _call_program options svn help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)-([[:alpha:]]) \[--([a-z-]##)\](:arg:)#/(--$match[2])- $match[1]$match[3] (-$match[1])--$match[2]$match[3]} 改为

${=${${${(M)${(f)"$(LC_ALL=C _call_program options svn help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)(-##)([[:alpha:]]##) \[--([a-z-]##)\](:arg:)#/(--$match[3])$match[1]$match[2]$match[4] ($match[1]$match[2])--$match[3]$match[4]}

参考资料: http://www.zsh.org/mla/workers/2011/msg01448.html

自定义Django的密码策略

| Comments

Django 从1.4 版本开始,包含了一些安全方面的重要提升。其中一个是使用 PBKDF2 密码加密算法代替了 SHA1 。另外一个特性是你可以添加自己的密码加密方法。

Django 会使用你提供的第一个密码加密方法(在你的 setting.py 文件里要至少有一个方法)

1
2
3
4
5
6
7
8
PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher', # Insecure Hashes
    'django.contrib.auth.hashers.MD5PasswordHasher', # Insecure Hashes
    'django.contrib.auth.hashers.CryptPasswordHasher', # Insecure Hashes
)

但Django默认生成的密码策略往往会带上 md5_xxx, pbkdf2_xxx,同其他系统整合的时候,一般是没有这些前缀的,这就需要我们自定义一个密码策略。

下面介绍下如何定义一个简单的 md5(md5(password, salt)) 密码策略。

step1

建立一个app,django-admin.py startapp ownmd5

step2

在 ownmd5中 建立 hashers.py 文件,加入 OwnMd5PasswordHasher 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import hashlib
from django.utils.translation import ugettext_noop as _
from django.utils.datastructures import SortedDict
from django.utils.crypto import constant_time_compare
from django.utils.encoding import force_bytes, force_str, force_text
from django.contrib.auth.hashers import BasePasswordHasher, mask_hash


class OwnMD5PasswordHasher(BasePasswordHasher):
    """
    The Salted MD5 password hashing algorithm (not recommended)
    """
    algorithm = "ownmd5"

    def encode(self, password, salt):
        assert password is not None
        assert salt and '$' not in salt
        hash = hashlib.md5(hashlib.md5(force_bytes(salt + password)).hexdigest()).hexdigest()
        return hash

    def verify(self, password, encoded, salt):
        encoded_2 = self.encode(password, salt)
        return constant_time_compare(encoded, encoded_2)

    def safe_summary(self, encoded, salt):
        return SortedDict([
            (_('algorithm'), self.algorithm),
            (_('salt'), mask_hash(salt, show=4)),
            (_('hash'), mask_hash(hash)),
        ])

###step3

将 OwnMD5PasswordHasher 加入到settings.py 中:

1
2
3
PASSWORD_HASHERS = (
    'ownmd5.hashers.OwnMD5PasswordHasher',
)

这样,默认的user及auth模块都会采用自定义的md5算法。

参考:

https://docs.djangoproject.com/en/1.6/topics/auth/passwords/

Auto Reload Modules in Django Shell

| Comments

python manage.py shell

太常用了,但是每次修改模块代码后,总要 exit—>reset 才能重新load一遍代码,实在是不爽。

也曾经改造ipython,想要代码修改后实时载入,各种坑,最后各种懒之下还是老老实实reload。

最近发现一个扩展,django-externsions:

https://github.com/django-extensions/django-extensions

装上好用不少,貌似还比较靠谱,推荐之。

Vim 对选中范围内容批量替换

| Comments

vim可以很方便的用 %s/src/dst/g 批量替换。

但是我想对ctrl+v 块选择的内容做批量替换就麻烦一点了,每次都得打一串很长很长的脚本命令。

发现的一个插件做这个事,vis:

https://github.com/vim-scripts/vis

支持两个命令模式:B 和 S

B 在选定区域内执行命令

ctrl+v选中内容后,使用:B cmd,此时命令栏的状态应该是:'<,'>B cmd这样。

例如替换内容, ctrl-v :B s/pattern/becomes/

执行外部命令,ctrl-v :B !sort

S 在选定区域内查找内容

ctrl+v选中内容后,使用:S pattern,此时命令栏的状态应该是:'<,'>S pattern这样。

解脱了….

记一下免得又忘掉。

Vim 命令行操作小技巧

| Comments

vim 使用:进入命令行后,除了直接敲命令外,一般会有两个操作:

1.查看历史指令并执行

2.复制寄存器中的内容 1 查看历史指令需要在进入命令行之前使用q:,再选则就OK了。

2 使用寄存器,一般复制时使用” + [寄存器名称] + y,进入命令行后再输入ctrl R,选择相应的寄存器粘贴就OK了。

vim tooltips

Display CRLF as ^M:

1
:e ++ff=unix

Rake Gen Deploy Rejected in Octopress

| Comments

Octopress老版本,例如我用的,在一台新机器上check 下来Repo,重建source分支时,会报rejected错误。

怎么pull也不管用,解决办法参考这里:

http://stackoverflow.com/questions/17609453/rake-gen-deploy-rejected-in-octopress

悲剧提示,我后来发现有一段blog被revert掉了,诡异的是在github上面的提交记录也没了,要慎用,要做好备份。

从MongoDB迁移到TokuMx

| Comments

WHY:

原因无它,MongoDB的 BSON格式带来的磁盘空间消耗实在太严重了,将mongodb的数据库文件gzip一把,一般能到原大小的1/10。

mongodb提出的解决办法有以下几个:

定期repaire或Compact,但是repaire带来的性能消耗实在太大,repaire或compact的时候插入性能基本上就是渣了,另外100G级别的数据库文件需要数小时才能压缩完毕。

采用Capped Collections,这样在创建collections的时候可以指定数据库文件能占用的最大空间大小及单个document大小,当存储数据超过这个大小的时候会自动删除最老的数据,空出空间来。但这样做弊端就是你不知道会有多少数据被删掉,另外需要你对这个表插入的document大小心中有数。

建立自己的清理机制,定期把无用的数据清理出去。这样虽然比Capped Collections可控制,但是对删除掉的磁盘空间利用率进一步下降了,很多时候,你删掉一半数据,能重新利用的空间可能也就10%。这是由MongoDB对于单个Document的空间分配机制决定的。

最后一种方法就是合理规划,分库分表,然后在mongodb.conf里面设置选项:directoryperdb = true,这样mongo每个数据库都会建立一个文件夹,这样删除单库的时候数据文件就删干净了,空间自然清理出来了,这个选项我强烈推荐打开,即使你没有空间上的困扰,打开后也对数据库管理维护由不小的方便。当然指望这种办法删数据局限性就太大了。

详细说一下mongo对于删除空间的重新利用方法:

1.首先mongodb删除一个document的时候并不是物理上真正删除数据,而是维护一个deleteList链表数组,每次删除就在链表里面做一个标记。怎么表示这次删除的空间大小范围呢,如图示:

1
2
3
4
5
6
7
8
9
 ---------------------------------------
 |<32 | 32-64| 64-128|....| < 4M| 4M-8M| --deleteList
 ---------------------------------------
  |       |      |     |     |      |
  |       |      |     |     |      |
 +-+     +-+    +-+   +-+   +-+    +-+
 +-+     +-+    +-+   +-+   +-+    +-+
         +-+    +-+   +-+   +-+    +-+
                +-+   +-+   +-+    +-+

2.对每一个被删除的docment计算其大小,然后插入到合适的链表中去,这样下次插入新数据的时候,先计算合适的空间大小,再在这个链表数组中找到合适的空闲空间指针地址,插入数据。如果没有合适的,再去开辟新空间。

3.这个链表数组每个大小区间是代码里写死的,可参见namespace_detail.h: int bucketSizes[] = { 32, 64, 128, 256, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000, 0x200000,0x400000, 0x800000};

那么插入一条新的docement时,如何计算这个合适的空间分配大小就要斟酌了。mongo采取两种办法,选择哪一种可以在创建collection的时候指定:

1.Padding计算方式,这也是mongodb默认的方式。每次开辟空间的时候,采用公式 实际大小*(1+paddingFactor),这个 paddingFactor一般比较小,在0.01-1之间,是根据插入document的大小自动变化的。可以在mongodb的shell里用db.stats()查看这个值。另外,repaire会把这个值置为1,compact操作可以手工指定这个值。

2.采用usePowerOf2Size方式,这种方式和mongodb的磁盘空间分配比较相像,就是以2^2, 2^3, 2^4….大小增长,每次分配相近的空间大小

网上有人评价道:这两种方式各有优劣,padding方式会为文档开辟更合适的大小,而且paddingFactor比较小,一般为0.01-0.09,不会浪费空间,文档更新小的话也不会移动文档位置。但是当大量更新和删除的时候,这种方式重复利用空间的能力就比较小,因为在deleteList中,不太容易找到合适的已删除文档,而且一旦更新就会又移动位置,磁盘重复利用率低,增长快,碎片多。相比之下,usePowerOf2Size方式,Mongodb每次都会开辟比文档大的多的空间,使用空间变多,但是更新和删除的容错率就会比较高,因为在deleteList列表中更容易找到合适的删除文档(每个列表中的文档大小都是相同的固定的),更新的时候也不会大量移动位置,磁盘重复利用率高,增长慢。

看起来这些策略很靠谱,但我实际用起来两种方法其实效果都不好,另外usePowerOf2Size的表现好一些,mongodb自2.6开始把这种分配方式变成默认的了。

啰嗦了半天,我想说的就是, mongodb的存储受他的MMap内存管理所限,改来改去利用率没有本质提升。

How to do?

升级Tokumx

看看Tokumx的介绍: “TokuMX is the MongoDB you know and love but built on top of Fractal Tree indexes from Tokutek.”

从上层看,Tokumx 可以看成是Mongodb的克隆,仅仅是底层的存储方式用它们的Fractal Tree算法替换了mongodb的存储而已。

关于Fractal Tree,了解不多,从官方文档看,是对B-Tree的一个改进,通过对BTree的下级树叶保存子节点的缓冲区减少IO次数,另外可以用zlib等压缩算法存储数据

存储方式的改变,也改变了MongoDB默认用的MMap内存管理算法,Tokumx采用自定义的内存管理,直接表现就是占用内存可以手工控制了(事实上也推荐你指定一个内存占用值),不像MongoDB那样对内存的占用贪得无厌。得益于Fractal Tree,因为I/O的减少,分形树索引不会要求索引必须小于内存。即使超过内存的限制,TokuMX依然可以维持很高的写入性能。

可以参考这里 以及这里 以及这里 以及这里

Tokumx宣称了很多很好的特性:

  • Benefits for Developers
  • 20x faster w/o tuning
  • Transactions w/o tedious code
  • Switch w/o changing your app
  • Benefits for DevOps
  • Use fewer servers; avoid upgrades
  • 90% compression = less flash
  • Scalability w/o losing data integrity

与MongoDB对比,官方宣称的限制有两个:

  • 不支持全文索引
  • 不支持GEO地理信息

我们看中的就是他的磁盘占用,对这两个限制不Care。

Migrating data from MongoDB into TokuMX

怎样迁移,参考官方Wiki

How about

将Mongodb(2.4.9) 迁移到TokuMx (1.5.0),插入的document多是4K左右,原Mongodb数据库达到TB级别,感性体验:

  • 数据存储占用空间大幅下降,说只用原来的1/10并不夸大

  • 每个collection及index都会存在单独的文件中,这样删除单表或索引后会立即释放占用的空间

  • 同一个表,删除老数据,插入新数据,空间重复利用率>80%

  • 写入速度没有20x的提升,但各种情况下,最差情况提升1倍是有的

  • 读性能没有下降

在我看来,与MongoDB相比,Tokumx的不足有以下几点:

  • 稳定性; 因为采用了自定义的内存管理,不如MongoDB的MMap方式管理稳定,如果cacheSize设置不合适,而Tokumx的机器还有其他占用内存比较大的进程,会导致OOP,被系统杀掉的几率比较大。这要求我们设置cacheSize要斟酌一下。可以参考这里; 顺便说一下,如果你的系统内存占用控制得当,是没有多大问题的。

  • Tokumx的 Capped Collections性能比较渣,但是在我看来,存储方式的改变已经不需要这种方式了,所以这个不是问题。

  • 我没有找到从Tokumx重新迁回MongoDB的现成工具,将来要迁回来,可能要手工写工具自己导数据

  • Mongodb升级,新Feature的支持,还有商业化的问题。Tokumx的官网上的blog有人问了这个问题,问Tokumx有没有同MongoDB Merge的计划,开发者做了回答,很详尽。

    首先Tokumx的开发团队很小的,可能不会及时跟进Mongodb的新Feature移植

    第二两家公司有不同的商业化考量,另外代码的Merge有一定工作量,未来不太可能合并

    Tokumx只专注于存储性能的改进

更新:经过一段时间使用,发现了如下问题:

  • 压缩后大量update是会带来大量碎片

  • 高写入性能会造成同步出现巨大延迟

  • 还是会莫名down掉,down掉几率还是要比Mongodb大

再次更新:经过实际数据测试,usePowerOf2Size的磁盘重复利用的确比较好

  • 采用Padding 的存储方式,磁盘重复利用率为32%

  • 采用usePowerOf2Size 的存储方式,磁盘重复利用率为73%

  • 采用usePowerOf2Size 的存储方式比Padding 的存储方式,磁盘多占用10%

所以假如有一年的数据,Padding的存储方式,storageSize为1T,设定过期时间是1年,那么第二年后,数据库增长为1.7T

同样的数据,usePowerOf2Size 的存储方式,storageSize为1.1T,设定过期时间是1年,那么第二年后,数据库增长为1.5T

这个数字会根据插入记录的离散度有所变化,但大概比例应该就是这样的。

总的来说,Tokumx还是只适合单机用啊,机器一多,各种毛病都来了~~~,大家如果分布式,等mongodb 2.8版本更靠谱一些。

2015-05 更新:升级到 mongodb3.0.3,切换到 WiredTiger存储引擎 ,各方面工作良好,建议升级 。

Export Json by Mongoexport

| Comments

Exp:

mongoexport -d foo -c exp -o exp.json -q '{"ts":{"$gte":0}}'

Notice:

You should use `"`  not  `'` in query string

Freeze Svn:externals for Tags/branch

| Comments

svn的external link在多个项目互相引用时很有用。

但我们想要做tag及branch的时候,需要将external link的文件或目录固定在某个版本。

搜了一下,基本上有几个办法:

使用tortoisesvn>1.7版本,打tag及branch的时候可以固定在某一版本:

¦   http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-dug-branchtag.html


缺点是这样打出来的branch,checkout下来后,svn up还是回到最新的版本。

使用一个perl脚本,可以在打branch的时候把extern link一起拷贝到branch底下,这样相当于是建立了一个新的external link拷贝,修改branch不会影响到trunk

¦   https://svn.apache.org/repos/asf/subversion/trunk/contrib/client-side/svncopy/


缺点是这样打出来的branch底下会多出来external link的拷贝,不完美。

有人开发了工具, C#的,但我在win7下面打branch的时候会报错

¦   http://svnxf.codeplex.com/

原先以为这样的事情总该有个simple的解决办法的,但还真是没找到,自己实际写个脚本想做这个事情才发现不简单。想要做这件事的前提是要把代码check到本地来,这样就不如直接调命令了,如果远程操作svn库,就需要三方开发。

最后简单的办法是:

如果是打tag,采用方法一,用tortoisesvn固定在一个版本

如果是打branch,先用tortoisesvn 固定在一个版本,再把branch分支checkout下来,用命令 svn propedit svn:externals .取消external link,再重新加入版本库,最后提交。

参考:

http://stackoverflow.com/questions/1982538/how-to-have-tortoisesvn-always-freeze-svnexternals-for-tags

为centos添加EPEL Repo

| Comments

centos默认的源软件不是很全,大部分时候需要添加EPEL源。

centos5.x

wget http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
sudo rpm -Uvh remi-release-5*.rpm epel-release-5*.rpm

centos6.x

wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo rpm -Uvh remi-release-6*.rpm epel-release-6*.rpm

添加完毕后可以到/etc/yum.repos.d里面看一下

$ ls -1 /etc/yum.repos.d/epel* /etc/yum.repos.d/remi.repo
/etc/yum.repos.d/epel.repo
/etc/yum.repos.d/epel-testing.repo
/etc/yum.repos.d/remi.repo

设置remi repository

remi repository更新很快,如果你很需要htopxxx最新版本这样的话最好打开

sudo vim /etc/yum.repos.d/remi.repo

编辑 [remi]段:

name=Les RPM de remi pour Enterprise Linux $releasever - $basearch
#baseurl=http://rpms.famillecollet.com/enterprise/$releasever/remi/$basearch/
mirrorlist=http://rpms.famillecollet.com/enterprise/$releasever/remi/mirror
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi
failovermethod=priority

开发工具套装

yum groupinstall "Development Tools"

SCL源

http://wiki.centos.org/AdditionalResources/Repositories/SCL

解决仓库安装失败的问题

有时候某个软件可以Search,但安装一直报错:

http://download.fedoraproject.org/pu...ry.sqlite.bz2: [Errno 12] Timeout: <urlopen error timed out>
Trying other mirror.

最简单的修复办法就是重建repos

yum clean all
rpm --rebuilddb
yum update

90%的情况会奏效

yum只下载安装包

[RHEL5]
yum -y install yum-downloadonly
yum install mongodb-org --downloadonly

[RHEL6]
yum install yum-plugin-downloadonly
yum install --downloadonly --downloaddir=. mongodb-org

yum提取已经安装的rpm包

yum install yum-utils
yumdownloader <package>
yumdownloader <package> --resolve

yum升级kernel

http://elrepo.org/tiki/kernel-ml

elrepo url

http://elrepo.org/tiki/tiki-index.php

http://elrepo.org/linux/kernel/

http://mirrors.sohu.com/centos/6.6/xen4/x86_64/Packages/

mongodb RPM repo

https://repo.mongodb.org/yum/redhat

163镜像源

http://mirrors.163.com/.help/centos.html

rpm 常用命令

  • 重装某个包
1
rpm -ivh --replacepkgs xxx.rpm
  • 修改prefix
1
2
rpm -qp --queryformat "%{defaultprefix}\n" <packagefile>
rpm -ivh --prefix <packagefile>
  • 查询某个包包含的文件
1
2
rpm -ql <packagefile>
rpm -qs <packagefile>
  • 查询某个包包含的配置文件
1
rpm -qc <packagefile>
  • 查询某个包安装时要执行的脚本文件
1
rpm -q --scripts <packagefile>
  • 查询某个文件属于哪个包
1
rpm -qf <filepath>
  • 查询某个可执行文件的配置文件和log文件
1
rpm -qcf <filepath>
  • 寻找最近安装的包
1
2
find /bin -type f -mtime -14 | rpm -qF
rpm -qa --queryformat '%{installtime} %{name}-%{version}-%{release} %{installtime:date}\n' | sort -nr +1 | sed -e 's/^[^ ]* //'
  • 寻找最大的安装包
1
rpm -qa --queryformat '%{name-%{version}-%{release} %{size}\n' | sort -nr +1}'
  • 解压一个rpm文件
1
rpm2cpio xxx.rpm | cpio -div