乐者为王

Do one thing, and do it well.

使用SQL Server 2000个人版出现的问题

下午在机房值班,14:00的时候有人报告说交易客户端不能登录了,确定问题后两同事急忙重启AR和AS以及交易网关,可是还是不能登录。紧急重启数据库后问题才解决。

出了问题不可怕,关键是找到问题根源所在,解决问题,使同样的错误不再重复地发生。

打电话联系恒生的工程师,向他们提供系统的日志文件,希望他们那边能提供帮助,找到原因。我也在这边分析系统的事件和日志文件,想找出到底是在哪里出的问题。分析着从SQL Server的LOG目录下找到的几个ERRORLOG文件,发现从13:57开始有连续多条如下的警告语句出现:

1
SQL Server已为8个并发查询而优化。36个查询超过了此限制,因而性能可能会受到不良影响。

很快,恒生工程师也打电话来说可能是查询数据库过多导致的问题。这个我们一开始就想过,但关键是那时候只连接进来了400个左右的客户,怎么可能查询过多呢?而且平时也没出现过这种情况。

在网上搜索资料后知道出现这个问题通常是数据库版本的原因,检查我们的SQL Server数据库,发现竟然是2000个人版。后来又从交易结算部们了解到他们因为要做报表执行了查询所有客户资料的操作,并且我们的备份数据库在几个星期前曾因损坏做过恢复,恢复的同事忘记修改备份数据库的时间,导致本应该执行在备份数据库上的查询执行在了主数据库上,引起了数据库崩溃。

再次联系恒生的工程师,准备这个周末切换数据库到企业版。唉,又要加班了!

为什么使用Paperclip而不是Fleximage来实现图片上传

Rails有很多处理上传的插件:

FileColumn是最早的这类插件,比较容易使用。UploadColumn与之类似。ActiveUpload则需要SWFUpload配合使用。attachment_fu作为acts_as_attachment的进化版本非常强大,不过使用起来比较复杂。Fleximage是现在上传和处理图片的首选,据说连Paperclip也比不上它,不过使用下来的情况是使用简单,功能太弱。比如要把上传的图片放到以用户名为目录的文件夹中就实现不了,而且上传的图片名字也不能设定。Paperclip作为一个处理附件上传的插件,既有attachment_fu的功能强大,又有Fleximage的使用简单,相对于FileColumn在灵活性和效率上更胜一筹,而且代码也更优雅。

将用户名作为上传图片的目录,先要在config/initializers中创建一个paperclip.rb文件,定义一个username变量:

1
2
3
Paperclip.interpolates :username do |attachment, style|
  attachment.instance.user.login
end

上面的代码中attachment.instance就是指该附件实例,因为在avatar.rb中有如下语句:

1
belongs_to :user

所以该实例有一个User对象,User对象有个login字段,该字段就是我们需要的用户名。

如何为绩效考核做准备

绩效考核是有压力的,但它也是重新审视你当前的位置、你与你雇主的关系、以及你的职业目标的机会。通过问自己一些关键的问题,会让你的绩效考核更富有成效。你可以在考核之前思考以下的这几个方面:

1. 今年我做了什么?

首先回顾全年(或者自上次考核以后的任何时间段)的工作。如果记不太清楚的话可以检查旧的电子邮件和文件。逐月查看你的职责和成就,包括预期的和意外的,特别要留意那些超出你职责要求以外的任何事情。例如,当雇员人数减少时,你是否承担了更多的职责?你是否找到方法去减少与特定项目或流程相关的成本?还要注意任何没有达到预期的项目,以及任何你经历过的挑战。像发生了什么,以及在最终结果中你的角色是什么?做好这样的准备可以确保你在考核中被提问时不会措手不及,也将有助于奠定你与你主管进行实质性讨论的基础。

2. 我的职业目标和重点是什么?

花些时间去查找和反思上次考核的评价,并提出一些新的目标或需要改进的地方。如果有目标半途而废了,考虑它们是否仍然重要,或者是否新的目标现在更合适。在你的考核期间,要毫不犹豫地去询问你的雇主是否有能力帮助你实现这些目标,因为在组织致力于活下去的时候,许多有价值的职业发展意图会被搁置。同时也要询问是否有你想继续的技术或业务培训?大多数雇主都有兴趣帮助雇员保持他们的职业向前迈进。

3. 我应该要求加薪吗?

即使你认为加薪是当之无愧的,在谈论之前也要考虑到你雇主的财务状况,以及你雇主表达感谢你贡献的不同方式,如灵活调度、在家工作的选项或额外的好处。如果你确实要求加薪,请准备好你为公司节省了时间和金钱方面的具体证据。并对公司的状况,过去的加薪,以及你所在地区其它相同职位的薪酬水平和通货膨胀作个调查,随时准备引用这些数据。

4. 如果得到负面考核怎么办?

首先,切勿对批评小题大作。如果你习惯了赞美,那么一些改进的建议也会让你觉得像是严厉的批判。请记住,即使是最优秀的雇员也有需要改进的地方。你应该做的是与你的主管一起制定计划去解决出现的问题。防卫性地反应或情绪化的批评可能比问题绩效本身更具破坏性。如果你发现自己正试图把过失推卸到同事(或更糟的是,你的老板)身上,最好管住你的舌头。你可以在更客观地审核它之后要求另外的会议来进一步讨论这个问题。如果讨论时批评转变的令人惊讶,这表明你和你主管沟通的不够。建议定期会晤,以便让彼此更好地了解。

绩效考核应该是一次对话,而不是一个审判。带着考核的教训进入来年将有助于你保持你的日常重点和长期目标一致。

计算机编程的21条法则

英文原文:http://www.devtopics.com/21-laws-of-computer-programming/

任何有经验的计算机程序员都知道,有一些不成文的法则支配着软件开发。不过,违反这些法则没有惩罚,相反,经常会有奖励。以下是计算机编程的21条法则:

  1. 任何给定的程序,一旦部署,已经过时。
  2. 修改需求规范来适应程序比反过来做更容易。
  3. 如果一个程序是有用的,那它必将被修改。
  4. 如果一个程序是无用的,那它必将被文档化。
  5. 在任何给定的程序里只有10%的代码会被执行。
  6. 软件会扩展到耗尽所有可用的资源。
  7. 任何有价值的程序至少包含一个错误。
  8. 一次完美演示的概率和观看人数成反比,是所涉及金额的次幂。
  9. 一个程序投入运行至少6个月,它最有害的错误才会被发现。
  10. 不可检测的错误无限多样,相比之下,可检测的错误从定义上来讲是有限的。
  11. 修正错误所需的努力随着时间成指数级增长。
  12. 程序的复杂度会一直增长,直到超出维护它的程序员的能力。
  13. 你自己的任何代码,几个月没看,就像是别人写的。
  14. 在每个小程序里都是一个大程序蠢蠢欲出。
  15. 你开始编写一个程序越早,它花费的时间越长。
  16. 一个胡乱计划的项目需要比预期多花费3倍的时间来完成;一个精心计划的项目只需要花费2倍的时间。
  17. 往一个延迟的项目里添加程序员会使项目更加延迟。
  18. 一个程序至少能完成90%,并且至多能完成95%。
  19. 如果你想麻烦被自动处理掉,你得到的是自动产生的麻烦。
  20. 构建一个连傻瓜都会使用的程序,然后只有傻瓜想要使用它。
  21. 用户真的不知道他们想要什么样的程序,直到他们使用它为止。

使用Fleximage上传图片时的问题

因为只是处理图片,所以选择了Fleximage,一个单纯的上传和处理图片的Rails插件。本来是想用Fleximage和MiniMagick的,据说RMagick内存泄露的问题比较厉害,作为替代品MiniMagick不存在内存泄露的问题,因Fleximage不能指定processor而作罢。

Fleximage项目的Wiki上有些资料已经过时了。它说渲染图片的时候要创建flexi文件,还要修改相应的action方法,添加format.image_type到respond_to代码块中,并且在view中使用formatted_photo_path来显示图片,但事实上formatted_photo_path已经被废弃了(起先也没发现,直到为了解决一个问题查看log后才知道),可以直接使用image_tag或photo_url。或者干脆不建什么flexi文件,也不添加format.image_type到action中,直接就在view中使用embedded_image_tag方法,也可以渲染图片的。

sqlite3-ruby和uninitialized constant Encoding

Ruby 1.8.6 + Rails 2.3.5

创建项目后在运行rake db:migrate时出现了以下错误:

1
2
3
4
5
6
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:migrate
rake aborted!
uninitialized constant Encoding

具体错误信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/active_support/dependencies.rb:443:in 'load_missing_constant'
/active_support/dependencies.rb:80:in 'const_missing'
/active_support/dependencies.rb:92:in 'const_missing'
/sqlite3-0.0.8/lib/sqlite3/encoding.rb:9:in 'find'
/sqlite3-0.0.8/lib/sqlite3/database.rb:66:in 'initialize'
/active_record/connection_adapters/sqlite3_adapter.rb:13:in 'new'
/active_record/connection_adapters/sqlite3_adapter.rb:13:in 'sqlite3_connection'
/active_record/connection_adapters/abstract/connection_pool.rb:223:in 'send'
/active_record/connection_adapters/abstract/connection_pool.rb:223:in 'new_connection'
/active_record/connection_adapters/abstract/connection_pool.rb:245:in 'checkout_new_connection'
/active_record/connection_adapters/abstract/connection_pool.rb:188:in 'checkout'
/active_record/connection_adapters/abstract/connection_pool.rb:184:in 'loop'
/active_record/connection_adapters/abstract/connection_pool.rb:184:in 'checkout'

http://stackoverflow.com/questions/1797199/uninitialized-constant-encoding-with-sqlite3-ruby-on-windows 查到原因说是因为安装了非sqlite3-ruby包所导致。记起安装SQLite时使用的命令是gem install sqlite3,而实际上sqlite3包仅是针对ruby 1.9版本,在1.8.6上需要使用sqlite3-ruby包。用gem list查看发现果然安装的是sqlite3包。gem uninstall sqlite3把它卸载掉,然后装上sqlite3-ruby包,再次运行rake db:migrate,可以了,不再出现错误信息了。

使用CSS创建可伸缩的面包屑

面包屑是一个用来描述层次性链接的术语,告诉用户当前处于网站的什么位置。简单的来说,就是显示从主页到当前页面路径的导航模式。其最简单的形式通常是这样的:

css-breadcrumbs

有许多种不同的方法来实现面包屑。在这里,我们仅使用无序列表和基本的CSS样式。

以下是HTML代码:

1
2
3
4
5
6
7
<ul id="breadcrumbs">
  <li><a href="#">Home</a> > </li>
  <li><a href="#">Main</a> > </li>
  <li><a href="#">Sub</a> > </li>
  <li><a href="#">Sub sub</a> > </li>
  <li>Current page</li>
</ul>

在上述的例子中,除了用户当前所在的页面——即最后一个条目外,其它所有的条目都是链接,指向路径中的某个页面。

如果你试着在浏览器中查看上面的这个例子,你会发现它只是一个简单的无序列表。必须给它添加一些CSS样式才能符合我们的预期。

下面是CSS代码:

1
2
3
4
5
ul, li {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

首先我们要重置基本列表样式的行为:把默认的圆形条目符号隐藏掉,并移除缩进。

1
2
3
4
5
6
7
8
9
10
#breadcrumbs {
  height: 2.3em;
}

#breadcrumbs li {
  float: left;
  line-height: 2.3em;
  padding-left: .25em;
  color: #777;
}

然后,给面包屑设置一个具体的高度。因为所有的碎屑需要保持在同一水平线,所以我们把每个li元素设置成float: left。为保证条目的垂直居中对齐,需要把条目的行高设置成和列表相等。碎屑之间需要留些空间,不然的话就会太拥挤,因此我们给每个碎屑左边加上0.25em的填充。

1
2
3
4
5
6
7
8
#breadcrumbs li a:link, #breadcrumbs li a:visited {
  text-decoration: none;
  color: #777;
}

#breadcrumbs li a:hover, #breadcrumbs li a:focus {
  color: #dd2c0d;
}

最后,给文本链接添加样式,使用text-decoration: none隐藏掉下划线。

显示Android的本地联系电话(改进版)

显示Android的本地联系电话中的代码只能显示联系人的第一个号码,很不方便。如果要列出联系人的所有号码,需要将People.CONTENT_URI改为Phones.CONTENT_URI。具体代码如下:

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
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts.People;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contact_list);

        ListView contactList = (ListView)findViewById(R.id.contactList);

        Cursor contactsCursor = managedQuery(Phones.CONTENT_URI,
                null, null, null, null);
        startManagingCursor(contactsCursor);

        String[] columnsToMap = new String[] {Phones.NAME, Phones.NUMBER};
        int[] mapTo = new int[] {R.id.contactName, R.id.contactNumber};

        SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this,
                R.layout.contact_entry, contactsCursor,
                columnsToMap, mapTo);
        contactList.setAdapter(mAdapter);
    }
}

如何获取Android手机的型号

怎么样才能获的Android手机的型号呢?例如HTC Hero或HTC Magic等名字。android.os.Build类有很多静态属性可以获得机器的信息,其中的MODEL属性就指代手机型号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText("Product Model: " + Build.MODEL);
        setContentView(tv);
    }
}

在我的机器上显示的是:

1
Product Model: HTC Hero

显示Android的本地联系电话

首先要在AndroidManifest.xml中设置允许读取联系人的权限:

1
<uses-permission android:name="android.permission.READ_CONTACTS" />

联系人列表布局文件contact_list.xml:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
    <ListView android:id="@+id/contactList"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

联系人列表项布局文件contact_entry.xml:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
    <TextView android:id="@+id/contactName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView android:id="@+id/contactNumber"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

MainActivity.java文件内容:

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
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts.People;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contact_list);

        ListView contactList = (ListView)findViewById(R.id.contactList);

        Cursor contactsCursor = managedQuery(People.CONTENT_URI,
                null, null, null, null);
        startManagingCursor(contactsCursor);

        String[] columnsToMap = new String[] {People.NAME, People.NUMBER};
        int[] mapTo = new int[] {R.id.contactName, R.id.contactNumber};

        SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this,
                R.layout.contact_entry, contactsCursor,
                columnsToMap, mapTo);
        contactList.setAdapter(mAdapter);
    }
}