502 bad gateway error when deploy rails app with dokku on digitalocean

Follow this how-to-use-the-dokku-one-click-digitalocean-image-to-run-a-ruby-on-rails-app guide, i got nginx error – ’502 bad gateway’.

How to solve?

first, check app logs to see what happen.

dokku logs <your-app-name>

I got DATABASE_URL error. it seems dokku didn’t link my app with PostgreSQL.

Checkout the PostgreSQL database_url:

dokku postgresql:info <your-app-name>

Host: xxxxxx
Port: 49154
User: 'root'
Password: '*********'
Database: 'db'

Url: 'postgres://root:*******@xxxxx:49154/db'

now link this url with my app:

dokku postgresql:link <your-app-name> <your-db-name>

or

dokku config:set <your-app-name> DATABASE_URL='postgres://root:*******@xxxxx:49154/db'

git push again, got db:migration error!

rake manually according to this gist:

docker run -i -t app/mg /bin/bash

cd app/
export HOME=/app
for file in /app/.profile.d/*; do source $file; done
hash -r
cd /app
RAILS_ENV=production rake db:migrate

DONE! push again, it works.

[mongodb]tcmalloc: large alloc out of memory, printing stack and exiting

最近Mongodb会经常突然挂掉,检查日志发现如下的错误:

tcmalloc: large alloc 2061584302080 bytes == (nil) @
Tue Nov 26 17:45:04.539 out of memory, printing stack and exiting:
0xdddd81 0x6cfb4e 0x121021d 0xafcc1f 0xaf815f 0xaf8d1d 0xaf8e0f 0xaf52ae 0xaf53c9 0xb1eb11 0x8ab6a2 0x8d78ca 0x8d951d 0x8daa72 0xa80970 0xa8523c 0x9f9079 0x9fa5a3 0x6e8b88 0xdca34e
 ./mongod(_ZN5mongo15printStackTraceERSo+0x21) [0xdddd81]
 ./mongod(_ZN5mongo14my_new_handlerEv+0x3e) [0x6cfb4e]
 ./mongod(_Znwm+0x6d) [0x121021d]
 ./mongod(_ZNSt6vectorIN5mongo18DocumentSourceSort9KeyAndDocESaIS2_EE7reserveEm+0x6f) [0xafcc1f]
 ./mongod(_ZN5mongo18DocumentSourceSort12populateTopKEv+0x6f) [0xaf815f]
 ./mongod(_ZN5mongo18DocumentSourceSort8populateEv+0x2d) [0xaf8d1d]
 ./mongod(_ZN5mongo18DocumentSourceSort3eofEv+0xf) [0xaf8e0f]
 ./mongod(_ZN5mongo18DocumentSourceSkip7skipperEv+0x6e) [0xaf52ae]
 ./mongod(_ZN5mongo18DocumentSourceSkip3eofEv+0x9) [0xaf53c9]
 ./mongod(_ZN5mongo8Pipeline3runERNS_14BSONObjBuilderERSs+0x1b1) [0xb1eb11]
 ./mongod(_ZN5mongo15PipelineCommand3runERKSsRNS_7BSONObjEiRSsRNS_14BSONObjBuilderEb+0x132) [0x8ab6a2]
 ./mongod(_ZN5mongo12_execCommandEPNS_7CommandERKSsRNS_7BSONObjEiRSsRNS_14BSONObjBuilderEb+0x3a) [0x8d78ca]
 ./mongod(_ZN5mongo7Command11execCommandEPS0_RNS_6ClientEiPKcRNS_7BSONObjERNS_14BSONObjBuilderEb+0x71d) [0x8d951d]
 ./mongod(_ZN5mongo12_runCommandsEPKcRNS_7BSONObjERNS_11_BufBuilderINS_16TrivialAllocatorEEERNS_14BSONObjBuilderEbi+0x5f2) [0x8daa72]
 ./mongod(_ZN5mongo11runCommandsEPKcRNS_7BSONObjERNS_5CurOpERNS_11_BufBuilderINS_16TrivialAllocatorEEERNS_14BSONObjBuilderEbi+0x40) [0xa80970]
 ./mongod(_ZN5mongo8runQueryERNS_7MessageERNS_12QueryMessageERNS_5CurOpES1_+0xd7c) [0xa8523c]
 ./mongod() [0x9f9079]
 ./mongod(_ZN5mongo16assembleResponseERNS_7MessageERNS_10DbResponseERKNS_11HostAndPortE+0x383) [0x9fa5a3]
 ./mongod(_ZN5mongo16MyMessageHandler7processERNS_7MessageEPNS_21AbstractMessagingPortEPNS_9LastErrorE+0x98) [0x6e8b88]
 ./mongod(_ZN5mongo17PortMessageServer17handleIncomingMsgEPv+0x42e) [0xdca34e]

内存溢出了,一开始我以为是有些排序没有加索引,后来一些地方加了索引后还是会出现,细想了下,如果是没加索引的话,是不会让整个Mongodb宕机的。

后来在Mongodb的issue上查到了这样一条提交的bug清单SERVER-10136

原来aggregation如果传递一个$skip特别大的值的时候,就会内存溢出。我看到这个bug已经被修复了,不过是在2.5.2版本,最新的稳定版是2.4.8。所以我们需要在自己的应用程序里面控制,让$skip的值不要超出总长。

[mongodb]exception: $concat only supports strings, not NumberInt32

今天在用mongodb操作aggregation的时候出现这个问题,我是想格式化日期,例如”2013-10-17 04:41:37 UTC”变成”10月17日”,

'fdate' => { '$concat' => ['$date.month', '月', '$date.day', '日'] }

出现 exception: $concat only supports strings, not NumberInt32

原来$concat只能操作字符串,不支持数字类型,解决办法是用$substr

$date形如’2013-10-13 11:17:18 UTC’

'fdate' => { '$concat' => [ {$substr=>['$date', 5, 2]}, '月', {$substr=>['$date', 8, 2]}, '日'] }

$substr接受两个参数,一个是字符串的起点,一个是截取的字符串的长度。

再看mongodb的分组group

在之前的记一次Mongodb性能调优文章中,最后使用了aggregation来做聚合查询,今天又有一个新的需求,需要在之前按日期聚合的基础上再按照店铺来聚合(说实话,到这里已经感觉到非关系数据库这方面的弱势了)。

幸运的是aggregation的操作其实是类似于管道(pipeline),可以进行多次group,这样就方便多次处理数据,得到想要的结果。

def diary_with_shop(opts = {})
  page       = opts[:page] || 1
  per_page   = opts[:per_page] || 20
  conditions = [{'$match' => {user_id: self.id, deleted_at: nil}}]
  conditions << {'$project' => {
    'year'  => {'$year'      => '$created_at'},
    'month' => {'$month'     => '$created_at'},
    'day'   => {'$dayOfYear' => '$created_at'},
    'date'  => '$created_at', 'shop_id' => 1
  }}
  conditions << {'$group' => {
    '_id' => {
      'year' => '$year', 'month' => '$month', 'day' => '$day', 'shop_id' => '$shop_id'
    },
      'date'  => { '$min' => '$date' },
      'food_stories' => { '$push' => '$_id' }
    }}
  conditions << {'$group' => {
    '_id'   => {
      'year' => '$_id.year', 'month' => '$_id.month', 'day' => '$_id.day'
     },
    'date'  => { '$min' => '$date' },
    'shops' => { '$push' => { 'shop_id' => '$_id.shop_id', 'food_stories' => '$food_stories' } }
  }}
  conditions << {'$sort'  => {'date' => -1}}
  conditions << {'$skip'  => (page.to_i - 1)*per_page.to_i}
  conditions << {'$limit' => per_page.to_i}
  Model.collection.aggregate(conditions)
end

如果对比上次的代码,可以发现这里多了一个group操作,第一个group的`_id`带上了shop_id,带上的shop_id在第二个group中又进行了分组,最后得到想要的结果。

在这里,shop只是id,如果想返回shop的名字,可以用类似于关系型数据库的join操作吗?

答案是不行,文档型数据库只能操作一个collection。那如果想得到shop_name,难道需要再次查询吗?尽量不要这样,这样会丧失aggregation的性能,所以如果你有字段在collection中没有,那么—

最好的办法就是直接复制加上去,不用考虑数据库的冗余,这样才能发挥文档型数据库的优势。

其实每个数据库都不可能取代另一个数据库,只有把数据库自有的特点发挥出来,才能达到性能的最大化,所以如果mongodb里面有很多的外键关联,那么可以考虑是否数据库的设计偏离了mongodb的本质。

Mavericks mac 10.9 gem install json 1.8.1 error failed

升级到最新的mac os Mavericks。装json出现错误:

gem install json -v '1.8.1'


Building native extensions.  This could take a while...
ERROR:  Error installing json:
  ERROR: Failed to build gem native extension.

    /usr/local/var/rbenv/versions/2.0.0-p247/bin/ruby extconf.rb
creating Makefile

make "DESTDIR="
compiling generator.c
In file included from /usr/local/var/rbenv/versions/2.0.0-p247/include/ruby-2.0.0/ruby.h:33,
                 from ../fbuffer/fbuffer.h:5,
                 from generator.c:1:
/usr/local/var/rbenv/versions/2.0.0-p247/include/ruby-2.0.0/ruby/ruby.h:51:21: error: string.h: No such file or directory
/usr/local/var/rbenv/versions/2.0.0-p247/include/ruby-2.0.0/ruby/ruby.h:61:21: error: stdint.h: No such file or directory
/usr/local/var/rbenv/versions/2.0.0-p247/include/ruby-2.0.0/ruby/ruby.h:64:23: error: inttypes.h: No such file or directory
/usr/local/var/rbenv/versions/2.0.0-p247/include/ruby-2.0.0/ruby/ruby.h:68:19: error: stdio.h: No such file or directory

解决办法:

xcode-select --install

需要重新安装Command Line Developer Tools

参考链接installing-rails-on-mavericks

[2013-11-29]修改:
如果这个办法还是不行,mac下需要安装下apple-gcc42

brew install apple-gcc42

注意brew 0.9.4应该是不带apple-gcc42的源的,可以使用brew doctor检查下是否和Mavericks兼容,这里需要升级

brew update

记一次Mongodb性能调优

1)关于索引

优化首先看查询是否有索引。

保证每次查询都可以走索引,可以用explain()查看,如果得到的cursor类型是`BasicCursor`,那么你要加索引了。

当一个字段属于多个索引的时候可以用`hint`来强制使用某一个索引。

谨慎使用`$in`和`$or`。

For queries that use the sort() method and use the $or operator, the query cannot use the indexes on the $or fields.

2)关于正则查找

如果Mongodb的正则查找使用 /^/ 匹配,索引可以生效,否则索引不生效,当数据量大的时候,性能非常糟糕。所以对于形如 /^/,/^a.*/,/^a.*$/,建索引吧。

对于非 /^/ 正则匹配,考虑使用全文检索吧,MongoDB 2.4 新引入了 Text Search

不过可惜的是,不支持中文,支持的语言可以查看这里,text-search-languages

所以在优化的过程中,我选择了solr来替代,solr最新的支持地理位置检索,可以很好的和MongoDB配合使用。

3)关于map/reduce

很麻烦,尽量不要使用。

4)关于aggregation

MongoDB2.2引入,基本可以满足大部分的聚合操作了,除了写起来很别扭。

Aggregation Framework Operators

今天用这个解决了一个问题,聚合还是放在数据库级别性能才足够优秀:

    def diary(opts = {})
      page       = opts[:page] || 1
      per_page   = opts[:per_page] || 20
      conditions = [{'$match' => {user_id: self.id, deleted_at: nil}}]
      conditions << {'$project' => {
        'year'  => {'$year'      => '$created_at'},
        'month' => {'$month'     => '$created_at'},
        'day'   => {'$dayOfYear' => '$created_at'},
        'date'  => '$created_at'
      }}
      conditions << {'$group' => {
        '_id' => {
          'year' => '$year', 'month' => '$month','day' => '$day'
        },
        'date' => { '$min' => '$date' },
        'food_stories' => { '$push' => '$_id' }
      }}
      conditions << {'$sort' => {'date' => -1}}
      conditions << {'$limit' => per_page}
      conditions << {'$skip'  => (page - 1)*per_page}
      Model.collection.aggregate(conditions)
    end

注意:如果你出现了,排序不正确,分页不正确,记得 $sort$limit$skip顺序有关,这几个操作要放在最后