Tuesday, February 10, 2009

App Engine中的事务隔离级别

DataStore中的事务隔离级别介绍。
关键是这一段话:
A request that looks up an updated entity by its key at a time after Milestone A is guaranteed to see the latest version of that entity. However, if a concurrent request executes a query whose predicate (the 'where clause' for you SQL/GQL fans out there) is not satisfied by the pre-update entity but is satisfied by the post-update entity, the entity will only be part of the result set if the query executes after the commit() operation has reached Milestone B.
1。通过key来检索的查询,是肯定能获得entity的最新版本的。(当然是在MilestoneA之后,MilestoneA之前的修改对其他的查询是不可见的)
2。通过断言(就是where字句,或者构造出来的query对象)来检索的查询,有可能所出来的某些结果不满足查询条件,也有可能有些满足查询条件的结果没有被检索出来。
为什么?
因为2中的查询是通过扫描索引来检索entity的,有可能查询的时候索引还没有更新,此时扫描索引记录是满足断言的,而返回结果集的时候记录已经被更新了,此时记录不满足断言,但是结果集已经被确定,这条记录就被当作满足断言的记录返回了。

所以DataStore只对上面英文中的两种情况作保证,这就是传说中的Read Committed transaction isolation level?

Labels: ,

Friday, November 09, 2007

一个Memory Access Violent问题的解决:

问题出现:
最近做一个项目,原来的开发平台是VC6,最近搬到VS2005上并新做一功能,但是在开发期间跑程序的时候经常发生内存越界的错误,提示写访问0x00000010位置的内存。
因为是正在开发的程序,所以很快的定位到了发生错误的代码,是一个对EnterCriticalSection的调用,紧接其后的是一行对两个long型的比较语句,代码如下:

EnterCriticalSection( &sndlock );
if( sndr != sndw ) {
sndno = sndr;
} else {
sndno = -1;
}
sndlock是一个全局变量,一个临界区的结构,用来进行线程互斥的。

EnterCriticalSection函数的定义是:
void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);

CRITICAL_SECTION结构的定义是

typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

//
// The following three fields control entering and exiting the critical
// section for the resource
//

LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
各字段具体含义请查一下MSDN

初步分析:
初步判断问题出在EnterCriticalSection里面。我的第一直觉是有缓冲区溢出,因为以前也遇到过,缓冲区溢出写乱了堆栈,导致栈内保存的EBP,ESP等寄存器被毁坏,函数返回时马上遇到内存越界的错误。可是这一般是发生在自己写的函数里面,而EnterCriticalSection是一个久经革命考验Windows API,不太可能在其内部还有缓冲区溢出。

第一个解决方案:
总结现象,发现并不是每次走到这里都会导致越界,并且出错的几率还蛮小,几乎是2,30比1。考虑到是多线程的程序,有位同事就提出可能出错的时候是临界区正好被别的线程占用了,而正好另一段程序也是在此临界区中做了IO操作,如果临界区资源不可用,当前线程调用EnterCriticalSection就会出问题,并给出了一个解决方案:将其他线程在临界区中的IO操作移出临界区。可是问题就来了,按照MSDN的说法,如果临界区资源被其他线程占用,EnterCriticalSection函数调用会一直阻塞到资源被释放,而绝不应该出现内存越界访问的错误啊。

跟踪:
多线程的界面程序,跟踪定位的比较困难,因为之前也没这方面经验,哪位大大有经验请不吝赐教!幸好这里还是一个比较特殊的位置,正好是进入临界区的代码。
当然在做事情之前放一把狗还是有用的,可是无论是搜EnterCriticalSection还是Windows内核缓冲区溢出都找不到有价值的答案。心存疑惑的马上跟踪汇编代码,
进入EnterCriticalSection之前,先查看一下sndlock结构,这个东西的地址是0x0053870c,再看它的的值,咦?其它成员都正常,可是DebugInfo指针怎么会是0x00000000?我想一定是存放Debug信息的,就算是NULL也问题不大吧。继续跟踪定位到ntdll.dll! 7c958fea(),的确是干了坏事
7C958FE8 mov eax,dword ptr [esi]
7C958FEA inc dword ptr [eax+10h]
此时 eax 为 0x00000000,要对ptr [eax+10h]加个1当然是出错了。
那么问题就肯定出在esi上了,esi此时为0x0053870c,看到没有?正好是sndlock的地址,7C958FE8 mov eax,dword ptr [esi] 这一句话就是把DebugInfo的值放到eax,然后。。。惨剧就发生了。

弯路:
当然过程不是这样的一帆风顺的,同事猜测是临界区资源被占用导致内存越界之后,为了证明他的观点“错误”,我在进入EnterCriticalSection之前,手动修改了sndlock结构,把LockCount从0xffffffff改为0x00000000,把RecursionCount从0改为1,把OwningThread改为界面主线程的的句柄号,然后再进入EnterCriticalSection函数,还真的是每次都会导致出错。怎么跟MSDN说的不一样嘛,难道真的有bug?
为了证明Windows API没有错误,我另外谢了一个程序,模拟一个线程占用了临界区资源后,另一个线程调用EnterCriticalSection,但是结果一切正常,没有发生越界错误。
真是奇怪了,老子来了招野蛮的,两个程序一起跑,从调用EnterCriticalSection的地方单步跟踪反汇编,最后才找出来esi指向的DebugInfo指针不一样,一个为0x00000000,另一个为可访问的内存。

罪魁祸首:
那么是谁把sndlock.DebugInfo搞成了0x00000000呢?我又一次使出了野蛮大法,先查找所有sndlock,然后全部设置断点,跑程序,发现居然调用了两次InitializeCriticalSection,可是调用InitializeCriticalSection再DeleteCriticalSection ,然后重新初始化临界区InitializeCriticalSection也不会出问题啊(我在自己的小程序里面已经试验过了)。那么就把sndlock.DebugInfo放到Watch窗口里面,看它什么时候变的。
其中的过程就不多说,最后反正是定位在一行这样的代码上
time( (time_t*)&begtm );
对,就是它改了sndlock.DebugInfo,可是为什么呢?这个函数跟sndlock有什么关系?

真相大白:
那么我们就来看看它们有什么关系,此begtm是一long型的全局变量,看看begtm的地址0x00538708,就比sndlock结构的地址少4!!!那么很可能time函数溢出了,废话少说,让我们看看time()函数的定义(用VS的Goto Define):

static __inline time_t __CRTDECL time(time_t * _Time)
{
return _time64(_Time);
}
那么参数类型time_t *又是什么呢:

#ifndef _TIME_T_DEFINED
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t; /* time value */
#else
typedef __time64_t time_t; /* time value */
#endif
#define _TIME_T_DEFINED /* avoid multiple def's of time_t */
#endif
定位到的是typedef __time64_t time_t这一行,那么__time64_t又是什么类型呢?
同样使用VS的Goto Define:
typedef __int64 __time64_t; /* 64-bit time value */

看到了吗?就是它,我们的begtm定义的时候是long型的,也就是占用4byte,而此时的time函数会把它的参数当64位的整形处理,就是说把0x00538708以后的8个字节都修改了,而这个时间的整形表示是不大于2的32次方减一的,所以紧跟在begtm后面的我们的小可怜sndlock.DebugInfo就被无情的强暴成了0x00000000(此处略去关于bigendian littleendian介绍若干)

解决方案:
凶手找到了,那我们就来搞定它把,time()函数说需要time_t*做参数,那我们就给它time_t*吧。修改全局变量begtm的声明为
time_t begtm;
重新编译,连接,测试。没有问题。就算在进入EnterCriticalSection之前,手动修改sndlock结构同样不会造成越界错误,这个线程只是痴痴的等在那里,永远不回来。。。

另一个解决办法是,编译的时候定义一下_USE_32BIT_TIME_T宏,使用32位的时间值。不过我觉得还是修改程序比较好,程序本来就有错嘛。

那么为什么在VC6的环境下不出错呢?可能您已经想到了,VC6的环境下的time_t是定义成32位整形的,正好是long的长度,不会出任何问题。

经验和教训:
1.程序差错的时候不能放过任何细微的地方,如果在发现DebugInfo为0x00000000的时候不放过,就可能少走后面的弯路,更快的找到问题所在。
2.不能得过且过,遇到问题要深入分析。如果我按照同事的观点,把IO放到临界区外,此处进入临界区的时候,资源被占用的机会变少,的确会减少错误发生的次数,但是却不能完全解决,遇到合适的时机,错误发生的条件仍然会满足。如果就因此放弃调查,可能今后就酿成重要事故。
3.调用函数的时候,最好根据函数定义的参数类型调用,不能看到time_t是long typedef过来的就直接声明为long型。如果以后编译参数改变或者编译器改变就很容易出现莫名其妙的错误。
4.基础知识还是很重要的,特别是做C或者C++的东西。

Friday, September 28, 2007

Google Toolbar buttons

Today I created 2 google toolbar buttons you can use it to access my homepage and picasaweb.
To install, click the 2 links below:
Install 7ever button
Install picasaweb button
Install Linux Manpage button

Friday, August 24, 2007

闲得无聊,用手机上上这几个"有名"的网站(5.7ever,新浪,slashdot篇)

  • 7ever

这个网站当然算不上有名,不过是俺自家的,拉出来溜溜不过分吧?就当做个广告了。








就不评论自己的东西了,不过放在上面的Adsence的广告好像没有显示出来。。。

  • 新浪



基本和网易一样,失望透顶。

  • slashdot
新闻站,希望它在手机上表现好一点,好让我们在旅途中也可以看nerd新闻。




比较失望,载入页面太大,内容太多,不适合手机浏览。不过一条一条的新闻和回复都能看出来,如果您是在钱多不在乎,那也不是不能上,对不?

闲得无聊,用手机上上这几个"有名"的网站(4.空中网,Live space篇)

  • 空中网
广告词是:手机上网就上空中网,我们来看看怎么样:



显示效果还是不错的,专业的还就是不一样。用来看看新闻什么的还不错。


  • Live space
微软同学有没有考虑到我们手机浏览space的感受呢?考虑到space已经支持Firefox,那我们的移动版Opera的表现如何呢?

这个是我的Live space首页:







页面显示还算正常除了顶部的两个图标让人看了有些头晕,不过似乎又是一个发送PC相同内容的家伙。看到了吗?200多K了还没有载入完毕,这得给移动贡献多少钞票啊,不愧是M$,中国移动找找移动内容合作伙伴肯定会考虑你们的。。。

看到picasaweb刚才优异的表现,大家一定关心live space的相册如何吧?结果让我们有点失望:




发送的内容好像不大了,载入也挺快,可是。。。可是。。。我们要看的照片呢?找Gates同学要吧

闲得无聊,用手机上上这几个"有名"的网站(3.Google篇)

  • Google
互联网毋庸置疑的老大,看看他们有什么高明之处:
首页你们已经看见过了吧,很简洁,专为移动终端定制,
通过抓包发现用手机上www.google.com,被重定向到了www.google.com/m,大家可以用PC的浏览器浏览zhege
这个首页大小是0.81 KB (833 字节),那个logo图片大小是1.98 KB(2026 字节)
总共3K不到。

那么它的搜索结果页面呢?
Look:





结果页面总共2.42 KB (2,479 字节),考虑到logo图片使用了IF-MODIFY-SINCE 的header,流量应该不超过64字节,所以总共结果页面还是3K不到。

顺便跑到picasaweb看了看,这部分的显示效果可以用惊艳来形容,绝对适合手机浏览

这是相册列表:



照片(拍的还不错吧):




不得不佩服Google的功力啊,人家赚这么多钱可不是白来的。

闲得无聊,用手机上上这几个"有名"的网站(2.ebay篇)

  • ebay
ebay的首页







总体来讲显示还是不错的,栏目也比较清晰,页面灭有混乱的感觉。

那么进入卖东西的页面呢?随便点了一个book的链接:




感觉还是和163什么的差不多,扔给我一个100多K的页面,虽然显示还行,没有混乱,但是头上很多都是其他产品的链接,要下拉50%以后才能看到有效内容。

Thursday, August 23, 2007

闲得无聊,用手机上上这几个"有名"的网站(1.网易,百度篇)

  • 网易

网易上来就是一个登录框,这和后面的新浪是一样的.然后还是对我象普通浏览器一样,直接把他的主页发给了我,看见那个滚动条变得多细了吗?这个首页载入完,俺的GPRS流量可增加不少(带图片好几百K啊),我的钱包啊...

频道列表


这个mm还可以看看.hot

那么网易的手机频道呢?是不是对移动设备做过优化?让我们来看看.
这是网易的手机频道:




看来只是一个卖手机的地方...
  • 百度
那百度的表现如何呢?


首页,表现不错,内容不多,显示很正常
那么来搜索一下呢?



搜索结果页面的表示还是不错的,产生的流量也不大,感觉和PC浏览差别不大,(怀疑百度也是完全发送相通页面,只是百度的内容都是text,所以没有产生如网易般恐怖的流量).只是搜索结果先出来的是一串广告,下面才是真正的搜索结果. 不过鉴于百度搜索结果的前几页都是广告,这也不算什么了...

闲得无聊,用手机上上这几个"有名"的网站

测试一下网站对手机浏览的友好度,和手机显示这几个网站的效果.

*测试目标网站:
  • 网易 http://www.163.com
  • 百度 http://www.baidu.com
  • ebay http://www.ebay.com
  • Google http://www.google.com
  • 空中网 http://www.kong.net
  • live space http://sevenever.space.live.com
  • 7ever http://www.sevenever.com
  • 新浪 http://www.sina.com
  • slashdot http://slashdot.org
测试工具:
浏览器: Opera 8.00
系统: Motorola E6(E690) Linux kernel 2.4.20

测试方法:
使用浏览器浏览各网站. 当然我还没有烧钱到乱用GPRS上网的地步, 我是在自己的Debian PC和E6之间建立了Ethernet On USB, 然后通过PC做gateway, 让E6上网的.
看看我们用的工具:

PC连着手机

用来浏览的手机

这是一次HTTP访问过程的抓包
GET / HTTP/1.1
User-Agent: MOT-MOTOROKR E6/1.0/R533_G_11.12.02P Mozilla/4.0 (compatible; MSIE 6.0; Linux; MOTOROKR E6; 781) Profile/MIDP-2.0 Configuration/CLDC-1.1 Opera 8.00 [
Host: www.baidu.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, multipart/mixed, application/vnd.wap.multipart.mixed, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: zh-cn,en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Cookie: BAIDUID=******************************
Cookie2: $Version=1
Cache-Control: no-cache
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers

HTTP/1.1 200 OK
Date: Thu, 23 Aug 2007 14:34:22 GMT
Server: BWS/1.0
Content-Length: 1742
Content-Type: text/html
Expires: Thu, 23 Aug 2007 14:34:22 GMT
Cache-Control: max-age=0
Content-Encoding: gzip

从中可以看出
1.HTTP RESPONSE使用了gzip压缩,这可以大大减少流量,帮我们省钱.
2.HTTP REQUEST中的User-Agent信息说明浏览器是移动终端的,如果服务器能根据此信息判别浏览器,则可以提供定制的页面,利于手机浏览器显示.

背景就介绍到这里,那么这些网站的表现都怎么样呢?不要走开,休息之后马上回来...

Saturday, January 13, 2007

使用System.Web.Mail名称空间连接需要验证的SMTP服务器

只需对增加相应标识即可:
// 使用SmtpMail对象发送邮件MailMessage mailObj = new MailMessage();

mailObj.Fields.Add(
"http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1"); //设置需要验证
mailObj.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", username); //用户名
mailObj.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", password); //密码

注意:Fields属性只在 .NET Framework 1.1 版本以后有效。

2007/01/12 .NET Framework 2.0好像都不用这样做了。