<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Oracle Berkeley DB 中国研发团队的博客 &#187; davidzhao</title>
	<atom:link href="http://www.bdbchina.com/author/davidzhao/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.bdbchina.com</link>
	<description>Oracle Berkeley DB 中国研发团队的博客</description>
	<lastBuildDate>Thu, 15 Dec 2011 10:35:52 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Berkeley DB 批量插入更新与删除用法示例</title>
		<link>http://www.bdbchina.com/2010/03/berkeley-db-%e6%89%b9%e9%87%8f%e6%8f%92%e5%85%a5%e6%9b%b4%e6%96%b0%e4%b8%8e%e5%88%a0%e9%99%a4%e7%94%a8%e6%b3%95%e7%a4%ba%e4%be%8b/</link>
		<comments>http://www.bdbchina.com/2010/03/berkeley-db-%e6%89%b9%e9%87%8f%e6%8f%92%e5%85%a5%e6%9b%b4%e6%96%b0%e4%b8%8e%e5%88%a0%e9%99%a4%e7%94%a8%e6%b3%95%e7%a4%ba%e4%be%8b/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 08:33:00 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=815</guid>
		<description><![CDATA[示例讲述Berkeley DB批量插入和删除的用法。]]></description>
			<content:encoded><![CDATA[<p>在Berkeley DB 4.8之前，我们可以执行的唯一的批量数据库操作是批量读取。<br />
从Berkeley DB 4.8开始，Berkeley DB支持批量插入/更新/删除，并且用法也与批量读取相似。<br />
批量插入/更新/删除对Berkeley DB的更新性能提升非常大，是一个值得认真学习的新功能。<br />
本文就以一个示例程序展示批量插入和批量删除的用法。<br />
<span id="more-815"></span><br />
/* 批量插入示例函数。*/<br />
void *<br />
run_bulk_insert()<br />
{<br />
int raw_key[NUM_KEY_INT];<br />
char raw_data[DATA_SIZE];<br />
DBT key, data;<br />
DB_ENV *envp;<br />
DB *dbp;<br />
DB_TXN *tid;<br />
int *insert_load;<br />
int insert_count, id, i, ret, op_flag;<br />
double tmp;</p>
<p>char *key_buf, *data_buf;<br />
void *p;<br />
int j;</p>
<p>/* Initialize structs and arrays */<br />
memset(raw_key, 0, KEY_SIZE);<br />
memset(raw_data, 0, DATA_SIZE);<br />
memset(&amp;key, 0, sizeof(DBT));<br />
memset(&amp;data, 0, sizeof(DBT));<br />
tid = NULL;</p>
<p>/* Initialize bulk insertion buffers */<br />
key_buf = malloc(KEY_SIZE * bulk_size * 2);<br />
data_buf = malloc(DATA_SIZE * bulk_size * 2);<br />
memset(key_buf, 0, KEY_SIZE * bulk_size * 2);<br />
memset(data_buf, 0, DATA_SIZE * bulk_size * 2);</p>
<p>/*<br />
* 初始化Bulk buffer.使用批量操作(bulk operations) 也就是<br />
* 批量插入/删除/更新/读取的时候，必须使用用户提供的内存。<br />
* 所以需要设置DBT对象的flags为DB_DBT_USERMEM，并且设置ulen成员而不是size成员。<br />
*/<br />
key.data = key_buf;<br />
key.ulen = KEY_SIZE * bulk_size * 2;<br />
key.flags = DB_DBT_USERMEM;<br />
data.data = data_buf;<br />
data.ulen = DATA_SIZE * bulk_size * 2;<br />
data.flags = DB_DBT_USERMEM;</p>
<p>op_flag = DB_MULTIPLE;/* 这个flag给put/get/del 表示执行批量插入/更新/读取/删除。 */</p>
<p>/*<br />
* 填充一个bulk buffer DBT 对象. 先调用DB_MULTIPLE_WRITE_INIT初始化该<br />
* DBT。必须传入一个工作指针p和data buffer DBT 对象。<br />
*/<br />
DB_MULTIPLE_WRITE_INIT(p, &amp;data);<br />
for (i = 0; i &lt; bulk_size; i++) {<br />
/*<br />
* 调用DB_MULTIPLE_WRITE_NEXT这个宏来向bulk buffer当中插入数据。<br />
* 需要确保bulk buffer足够大，否则会出现内存访问越界错误。<br />
*<br />
* 各参数说明：<br />
* p: 是这个宏内部使用的工作变量，由DB_MULTIPLE_WRITE_INIT初始化，并且必须在此处一直使用。<br />
* data: 是data buffer DBT对象。<br />
* raw_data: 是一个数据项所在的内存地址。你需要把你要装入的数据项传入这个参数。每个数据项<br />
* 可以含有任意长度的字节，长度限制是一个DBT的总长度限制，也就是2的32次方。<br />
* DATA_SIZE: 是本次宏调用的数据项长度。本例当中所有数据项长度相同，只是特例。完全可以<br />
* 使用变长的数据项。<br />
*<br />
* 循环结束后填充完成，这个data buffer当中有bulk_size个data，<br />
*/<br />
DB_MULTIPLE_WRITE_NEXT(p, &amp;data, raw_data, DATA_SIZE);<br />
}</p>
<p>/*<br />
* 批量插入insert_count条key/data pairs, 每一批插入bulk_size条key/data pairs.<br />
* 本例当中我们只准备了一批数据，所以最终插入的数据是重复的，不过这不影响示例本身。<br />
*/<br />
for (i = 0; i &lt; insert_count / bulk_size; ) {<br />
/*<br />
* 填充key buffer。填好后，这个key buffer当中有bulk_size个key，<br />
* 并且第i个key与data buffer 当中的第i个data做为一对key/data pair<br />
* 被插入数据库当中(i = 0, 1, 2, &#8230; bulk_size).<br />
*/<br />
DB_MULTIPLE_WRITE_INIT(p, &amp;key);<br />
for (j = i * bulk_size; j &lt; (i + 1) * bulk_size; j++) {<br />
raw_key[0] = insert_load[j];<br />
/* 在循环当中使用DB_MULTIPLE_WRITE_NEXT依次插入每条data到data buffer当中。<br />
* 循环结束后填充完成。*/<br />
DB_MULTIPLE_WRITE_NEXT(p, &amp;key, raw_key, KEY_SIZE);<br />
}</p>
<p>/* 启动事务准备批量插入。 */<br />
if ((ret = envp-&gt;txn_begin(envp, NULL, &amp;tid, 0)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[insert] DB_ENV-&gt;txn_begin&#8221;);<br />
exit(EXIT_FAILURE);<br />
}</p>
<p>/*<br />
* 执行批量插入。key和data DBT 对象分别是key buffer和data buffer,<br />
* 其中必然含有相同书目的key和data items,key buffer当中的第i个<br />
* key item与data buffer当中的第i个data item 作为一个Key/data pair<br />
* 被插入数据库中。(i = 0, 1, 2, &#8230; bulk_size).<br />
*/<br />
switch(ret = dbp-&gt;put(dbp, tid, &amp;key, &amp;data, op_flag)) {<br />
case 0: /* 批量插入操作成功，提交事务。*/<br />
if ((ret = tid-&gt;commit(tid, 0)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[insert] DB_TXN-&gt;commit&#8221;);<br />
exit(EXIT_FAILURE);<br />
}<br />
break;<br />
case DB_LOCK_DEADLOCK:<br />
/* 如果数据库操作发生死锁，那么必须abort事务。然后，可以选择重新执行该操作。*/<br />
if ((ret = tid-&gt;abort(tid)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[insert] DB_TXN-&gt;abort&#8221;);<br />
exit(EXIT_FAILURE);<br />
}<br />
continue;<br />
default:<br />
envp-&gt;err(envp, ret, &#8220;[insert] DB-&gt;put ([%d]%d)&#8221;, i, insert_load[i]);<br />
exit(EXIT_FAILURE);<br />
}</p>
<p>i++;<br />
}</p>
<p>(void)free(key_buf);<br />
(void)free(data_buf);</p>
<p>return (NULL);<br />
}</p>
<p>/* 批量插入示例函数。*/<br />
void *<br />
run_bulk_delete()<br />
{<br />
int raw_key[NUM_KEY_INT];<br />
DBT key;<br />
DB_ENV *envp;<br />
DB *dbp;<br />
DB_TXN *tid;<br />
int *delete_load;<br />
int delete_count, id, i, ret, op_flag;<br />
double tmp;</p>
<p>char *key_buf;<br />
void *p;<br />
int j;</p>
<p>/* Initialize structs and arrays */<br />
memset(raw_key, 0, KEY_SIZE);<br />
memset(&amp;key, 0, sizeof(DBT));<br />
tid = NULL;</p>
<p>/*<br />
* 初始化批量删除使用的key buffer。由于批量删除不需要data，<br />
* 所以只需要初始化和填充key buffer。我们同样需要使用自己分配的内存。<br />
*/<br />
key_buf = malloc(KEY_SIZE * bulk_size * 2);<br />
memset(key_buf, 0, KEY_SIZE * bulk_size * 2);</p>
<p>/* 初始化key buffer DBT 对象，设置正确的flags和ulen成员。 */<br />
key.data = key_buf;<br />
key.ulen = KEY_SIZE * bulk_size * 2;<br />
key.flags = DB_DBT_USERMEM;<br />
op_flag = DB_MULTIPLE; /* 批量删除同样需要这个flag。*/</p>
<p>/*<br />
* 批量删除所有的数据。每一批删除由key buffer DBT 当中的key<br />
* 指定的bulk_size条key/data pair. 这两个宏的详细用法见上文。<br />
*/<br />
for (i = 0; i &lt; delete_count / bulk_size; ) {<br />
/* 为批量删除初始化并填充一个key buffer DBT 对象。 */<br />
DB_MULTIPLE_WRITE_INIT(p, &amp;key);<br />
for (j = i * bulk_size; j &lt; (i + 1) * bulk_size; j++) {<br />
raw_key[0] = delete_load[j];<br />
DB_MULTIPLE_WRITE_NEXT(p, &amp;key, raw_key, KEY_SIZE);<br />
}<br />
/* 启动事务。*/<br />
if ((ret = envp-&gt;txn_begin(envp, NULL, &amp;tid, 0)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[delete] DB_ENV-&gt;txn_begin&#8221;);<br />
exit(EXIT_FAILURE);<br />
}</p>
<p>/*<br />
* 执行批量删除。key buffer DBT<br />
* 当中的bulk_size条key指定的key/data pairs会被从数据库当中删除。<br />
*/<br />
switch(ret = dbp-&gt;del(dbp, tid, &amp;key, op_flag)) {<br />
case 0: /* 批量删除操作成功，提交事务。*/<br />
if ((ret = tid-&gt;commit(tid, 0)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[delete] DB_TXN-&gt;commit&#8221;);<br />
exit(EXIT_FAILURE);<br />
}<br />
break;<br />
case DB_LOCK_DEADLOCK:<br />
/* 如果数据库操作发生死锁，那么必须abort事务。然后，可以选择重新执行该操作。*/<br />
if ((ret = tid-&gt;abort(tid)) != 0) {<br />
envp-&gt;err(envp, ret, &#8220;[delete] DB_TXN-&gt;abort&#8221;);<br />
exit(EXIT_FAILURE);<br />
}<br />
continue;<br />
default:<br />
envp-&gt;err(envp, ret, &#8220;[delete] DB-&gt;del ([%d]%d)&#8221;, i, delete_load[i]);<br />
exit(EXIT_FAILURE);<br />
}<br />
i++;<br />
}</p>
<p>(void)free(key_buf);</p>
<p>return (NULL);<br />
}<!--more--></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2010/03/berkeley-db-%e6%89%b9%e9%87%8f%e6%8f%92%e5%85%a5%e6%9b%b4%e6%96%b0%e4%b8%8e%e5%88%a0%e9%99%a4%e7%94%a8%e6%b3%95%e7%a4%ba%e4%be%8b/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Thread Local Storage Platform Issues and Dbstl&#8217;s Solutions</title>
		<link>http://www.bdbchina.com/2009/12/thread-local-storage-platform-issues-and-dbstls-solutions/</link>
		<comments>http://www.bdbchina.com/2009/12/thread-local-storage-platform-issues-and-dbstls-solutions/#comments</comments>
		<pubDate>Wed, 16 Dec 2009 08:12:06 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=772</guid>
		<description><![CDATA[This article talks about making use of thread local storage on various platforms.]]></description>
			<content:encoded><![CDATA[<p>1. Overview</p>
<p>Thread local storage(tls) is a feature provided by most modern operating<br />
systems(OS) that allow multiple threads within a process to have its own &#8220;global&#8221;<br />
data, but the scope of the &#8220;global&#8221; data is restricted within a thread<br />
itself, i.e. tls variables are thread wide global variables.</p>
<p>TLS can be useful if we want to store global data which are grouped by<br />
threads, each thread only accesses its own piece of data, and have no loss of<br />
concurrency. Without tls, we would have to store such data in a global data<br />
structure (process wide) and use locks to sychronize access to it.</p>
<p><span id="more-772"></span><br />
2. Two Ways to Support TLS</p>
<p>The support to tls feature includes two ways: compiler keyword support and<br />
thread library API support.</p>
<p>2.1 Compiler keyword support:</p>
<p>If we declare an integer variable P a tls variable on Windows, we can do it like<br />
this:</p>
<p>__declspec(thread) int P;</p>
<p>Or if we want to define a static data member S of class C as a tls variable so<br />
that each thread access its own copy of S when it accesses C::S, we will<br />
declare it like this:</p>
<p>class C {<br />
public:<br />
static __declspec(thread) int S;<br />
// The rest follow&#8230;<br />
};</p>
<p>template &lt;typename T&gt;<br />
class CT {<br />
public:<br />
static __declspec(thread) T* ST;<br />
// The rest follow&#8230;<br />
};</p>
<p>And define it like this:</p>
<p>__declspec(thread) int C::S = 0;</p>
<p>template &lt;typename T&gt;<br />
__declspec(thread) T* CT::ST = NULL;</p>
<p>2.2 Portability</p>
<p>Unfortunately, different compilers and OS have different keywords, for MSVC<br />
it is __declspec(thread); for gcc and icpc (intel c++ compiler), it&#8217;s __thread;<br />
and some other compilers like those on HPUX use __declspec(__thread).</p>
<p>So it takes some effort to<br />
write portable tls code that can run on multiple platforms using multiple<br />
compilers &#8212; we need to provide the correct tls keyword for the target<br />
platform and compiler.</p>
<p>A more terrible issue that makes writing such portable code harder is that<br />
in the above CT&lt;T&gt;::ST example, some<br />
compilers like icpc require the tls keyword only appear in the declaration,<br />
if CT&lt;T&gt;::ST&#8217;s definition also has the tls keyword, icpc can&#8217;t build the code.<br />
And having only a tls declaration is enough to make CT&lt;T&gt;::ST a tls variable.<br />
But icpc require tls keyword appear in C::S&#8217;s definition, strange enough.</p>
<p>However, some other compilers like the older ( &lt; 4.0) versions of gcc require<br />
the tls keyword<br />
appear in both the declaration and definition, otherwise although the build<br />
succeeds, the variable CT&lt;T&gt;::ST or C::S is not handled as a tls variable,<br />
thus multiple thread access will have races and result in undefined behaviors;</p>
<p>Newer versions of gcc will take CT&lt;T&gt;::ST and C::S as a tls variable once it&#8217;s<br />
declared as tls, whether or not the definition has tls keywords. This is also<br />
true for icpc if the class is not templated. Since icpc is trying to mimic gcc<br />
as much as possible, I think it&#8217;s icpc&#8217;s bug that it can&#8217;t accept a tls<br />
keyword in CT&lt;T&gt;::ST&#8217;s definition.</p>
<p>A portable implementation in dbstl implementation is to detect appropriate tls declaration and<br />
definition keywords when configuring, using the m4 scripts. We will iterate<br />
through the (&#8220;__declspec(thread)&#8221;, &#8220;__thread&#8221;, &#8220;__declspec(__thread)&#8221;, &#8220;&#8221;)<br />
array for a combination that builds the class C and CT, and exclude the<br />
(&#8220;&#8221;, &#8220;&#8221;) combination of course. Also note that the &#8220;&#8221; must be placed at last<br />
otherwise if the compiler is a older version of gcc, the tls keyword in<br />
definition will be blank, and we will get a non-tls variable instead; when we<br />
apply this technique, we will find icpc end up with no appropriate tls<br />
keyword combinations.</p>
<p>2.3 Thread API Support</p>
<p>Another way to use tls is through thread API. For example, in pthread, we<br />
first create a tls key, then use this key to get/set the thread-specific copy<br />
of the tls variable assocaited with the key for each thread.<br />
We create the tls key via the pthread_key_create call.<br />
And in order to avoid multiple initializations to the same key, we use<br />
pthread_once to make sure the pthread_key_create is called only once.</p>
<p>When we have the tls key, we can call pthread_setspecific to write to this<br />
thread&#8217;s tls variable, or call pthread_getspecific to read this<br />
thread&#8217;s tls variable.</p>
<p>Some platforms only has the thread API support, like Mac OSX; For others if they<br />
don&#8217;t have pthread API, or other thread library that support tls, they will<br />
need to have a tls keyword to support this feature.</p>
<p>2.4 TLS in Dbstl Implementation</p>
<p>In dbstl, we always prepared both choices, and make the choice when<br />
configuring &#8212; if the platform and compiler has tls keyword support, we use it;<br />
otherwise we use pthread tls API; If neither pthread API nor tls keywords<br />
are available, this platform won&#8217;t be able to use dbstl in multiple threads.</p>
<p>See the $(DB)/stl/dbstl_resource_manager.h/cpp for example code.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/12/thread-local-storage-platform-issues-and-dbstls-solutions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Berkeley DB示例程序详解(3.1)</title>
		<link>http://www.bdbchina.com/2009/10/berkeley-db%e7%a4%ba%e4%be%8b%e7%a8%8b%e5%ba%8f%e8%af%a6%e8%a7%a331/</link>
		<comments>http://www.bdbchina.com/2009/10/berkeley-db%e7%a4%ba%e4%be%8b%e7%a8%8b%e5%ba%8f%e8%af%a6%e8%a7%a331/#comments</comments>
		<pubDate>Sun, 04 Oct 2009 08:42:07 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[HA]]></category>
		<category><![CDATA[replication]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=616</guid>
		<description><![CDATA[/*
 * 这个示例程序演示了使用Berkeley DB的replication功能的方法。
 *
 * Berkeley DB提供了一套基本API和一套replication manager API来使用
 * 它的replication功能。
 * 前者有更大的灵活性，用户可以选择各种网络协议来实现数据传输，并且使用各种
 * 线程库来进行多线程编程。同时由于使用最基本的API， 用户可以定制选举和
 * replication系统的内部消息处理方式，非常灵活。所以这也意味着用户需要更多
 * 的代码来使用Berkeley DB replication功能，开发难度相对后者较大；
 * 后者基于TCP/IP协议和pthread线程库(Windows上面使用win32线程库)，并且按照通常
 * 的需求，对选举和内部消息处理进行了通用的处理，并且通过让用户配置
 * 策略(policy)和参数的方式提供一定的灵活性和可定制性。
 *
 * 本程序基于Berkeley DB的replication manager。
 *
 *
 * 名词术语：
 *
 * site：一个(数据库环境, IP地址, 端口(port))的三元组。所以，多个site可以处于
 * 不同的计算机上面，也可以在同一个计算机上面，只要配置了不同的端口并使用不同
 * 的数据库环境目录。
 *
 * replication group：由若干个site组成的集合，其中有且只有一个site 可以写入
 * 数据到数据库当中， 称为master；其余的site只可以从中读取数据，称为replica。
 *
 * master：接受读写请求。如果写入数据，那么这些写入的数据不仅会更新master
 * 自己的数据库，还会通过Berkeley DB的
 * replication功能，被传播到replication group当中的replica上面。一个
 * replication group通过选举或者显式指定的方式，得到master。
 *
 * replica：接受读请求。从master写入的数据会自动传播到每一个replica上面，从而
 * 将replica上面的数据更新。任何一个replica在选举的时候，都有可能成为新的master
 *
 * 一个replication group作为一个整体来看的话，它比一个单机有
 * 更大的数据读写能力，因为所有的site都可以提供数据，自然读数据的吞吐量更大；
 * 并且master可以被配置为只写入数据而不必理会读数据请求，由replica提供数据，
 * 这样，写入的吞吐量也更大。
 *
 * replication group比单机的另一个优势，就是高可靠性(high availability)，因为，
 * 其中如果任何一个site脱离（断电，网络故障，应用程序错误等），这个组都可以
 * 继续服务：如果一个replica脱离，那么
 * 还有其他replica可以提供数据，并且这个脱离的replica可以重新加入该组，其数据
 * 被自动更新到最新；如果master脱离，那么该组的所有replica举行选举，从中选
 * 出新的master。
 */
int
main(argc, argv)
 int [...]]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * 这个示例程序演示了使用Berkeley DB的replication功能的方法。<br />
 *<br />
 * Berkeley DB提供了一套基本API和一套replication manager API来使用<br />
 * 它的replication功能。<br />
 * 前者有更大的灵活性，用户可以选择各种网络协议来实现数据传输，并且使用各种<br />
 * 线程库来进行多线程编程。同时由于使用最基本的API， 用户可以定制选举和<br />
 * replication系统的内部消息处理方式，非常灵活。所以这也意味着用户需要更多<br />
 * 的代码来使用Berkeley DB replication功能，开发难度相对后者较大；<br />
 * 后者基于TCP/IP协议和pthread线程库(Windows上面使用win32线程库)，并且按照通常<br />
 * 的需求，对选举和内部消息处理进行了通用的处理，并且通过让用户配置<br />
 * 策略(policy)和参数的方式提供一定的灵活性和可定制性。<br />
 *<br />
 * 本程序基于Berkeley DB的replication manager。<br />
 *<span id="more-616"></span><br />
 *<br />
 * 名词术语：<br />
 *<br />
 * site：一个(数据库环境, IP地址, 端口(port))的三元组。所以，多个site可以处于<br />
 * 不同的计算机上面，也可以在同一个计算机上面，只要配置了不同的端口并使用不同<br />
 * 的数据库环境目录。<br />
 *<br />
 * replication group：由若干个site组成的集合，其中有且只有一个site 可以写入<br />
 * 数据到数据库当中， 称为master；其余的site只可以从中读取数据，称为replica。<br />
 *<br />
 * master：接受读写请求。如果写入数据，那么这些写入的数据不仅会更新master<br />
 * 自己的数据库，还会通过Berkeley DB的<br />
 * replication功能，被传播到replication group当中的replica上面。一个<br />
 * replication group通过选举或者显式指定的方式，得到master。<br />
 *<br />
 * replica：接受读请求。从master写入的数据会自动传播到每一个replica上面，从而<br />
 * 将replica上面的数据更新。任何一个replica在选举的时候，都有可能成为新的master<br />
 *<br />
 * 一个replication group作为一个整体来看的话，它比一个单机有<br />
 * 更大的数据读写能力，因为所有的site都可以提供数据，自然读数据的吞吐量更大；<br />
 * 并且master可以被配置为只写入数据而不必理会读数据请求，由replica提供数据，<br />
 * 这样，写入的吞吐量也更大。<br />
 *<br />
 * replication group比单机的另一个优势，就是高可靠性(high availability)，因为，<br />
 * 其中如果任何一个site脱离（断电，网络故障，应用程序错误等），这个组都可以<br />
 * 继续服务：如果一个replica脱离，那么<br />
 * 还有其他replica可以提供数据，并且这个脱离的replica可以重新加入该组，其数据<br />
 * 被自动更新到最新；如果master脱离，那么该组的所有replica举行选举，从中选<br />
 * 出新的master。<br />
 */<br />
int<br />
main(argc, argv)<br />
 int argc;<br />
 char *argv[];<br />
{<br />
 DB_ENV *dbenv;<br />
 SETUP_DATA setup_info;<br />
 repsite_t *site_list;<br />
 APP_DATA my_app_data;<br />
 thread_t ckp_thr, lga_thr;<br />
 supthr_args sup_args;<br />
 u_int32_t start_policy;<br />
 int i, ret, t_ret;</p>
<p> memset(&amp;setup_info, 0, sizeof(SETUP_DATA));<br />
 setup_info.progname = progname;<br />
 memset(&amp;my_app_data, 0, sizeof(APP_DATA));<br />
 dbenv = NULL;<br />
 ret = 0;<br />
 /*<br />
  * 默认使用选举的方法选出master。替代方案就是指定某一个site是master，<br />
  * 其他site是replica。<br />
  */<br />
 start_policy = DB_REP_ELECTION;</p>
<p> /* 创建一个拥有replication功能的数据库环境，但还不打开。 */<br />
 if ((ret = create_env(progname, &amp;dbenv)) != 0)<br />
  goto err;<br />
 dbenv-&gt;app_private = &amp;my_app_data;</p>
<p> /*<br />
  * 配置replication消息处理回调函数。<br />
  * 有任何replication消息，Berkeley DB都会调用该函数来处理。<br />
  */<br />
 (void)dbenv-&gt;set_event_notify(dbenv, event_callback);</p>
<p> /* Parse command line and perform common replication setup. */<br />
 if ((ret = common_rep_setup(dbenv, argc, argv, &amp;setup_info)) != 0)<br />
  goto err;</p>
<p> /*<br />
  * Perform repmgr-specific setup based on command line options.<br />
  * 如果显式指定了master，那么就使用DB_REP_MASTER把本site设置为master，<br />
  * 并用DB_REP_CLIENT把其他site设置为replica。<br />
  */<br />
 if (setup_info.role == MASTER)<br />
  start_policy = DB_REP_MASTER;<br />
 else if (setup_info.role == CLIENT)<br />
  start_policy = DB_REP_CLIENT;<br />
 /*<br />
  * 设置本地site信息，也就是IP地址和端口信息。因为site是一个<br />
  * （数据库环境，IP地址，port）的三元组，而数据库环境我们已经创建好<br />
  * 了。<br />
  */<br />
 if ((ret = dbenv-&gt;repmgr_set_local_site(dbenv, setup_info.self.host,<br />
     setup_info.self.port, 0)) != 0) {<br />
  fprintf(stderr, &#8220;Could not set listen address (%d).\n&#8221;, ret);<br />
  goto err;<br />
 }<br />
 site_list = setup_info.site_list;<br />
 /*<br />
  * 添加其他site的信息到本site。如果显式指定master，那么一个replica<br />
  * 一定要知道master的信息；如果选举，那么每一个site要知道其他所有<br />
  * site的信息。<br />
  *<br />
  * 两个site可以成为peer关系，通过在下面的函数中设置<br />
  * DB_REPMGR_PEER来指定。一个replica可以被它的peer更新到最新，这样<br />
  * 就为master去掉了更新过时的replica的负担。<br />
  */<br />
 for (i = 0; i &lt; setup_info.remotesites; i++) {<br />
  if ((ret = dbenv-&gt;repmgr_add_remote_site(dbenv,<br />
      site_list[i].host, site_list[i].port, NULL,<br />
      site_list[i].peer ? DB_REPMGR_PEER : 0)) != 0) {<br />
   dbenv-&gt;err(dbenv, ret,<br />
       &#8220;Could not add site %s:%d&#8221;, site_list[i].host,<br />
       (int)site_list[i].port);<br />
   goto err;<br />
  }<br />
 }</p>
<p> /*<br />
  * Configure heartbeat timeouts so that repmgr monitors the<br />
  * health of the TCP connection.  Master sites broadcast a heartbeat<br />
  * at the frequency specified by the DB_REP_HEARTBEAT_SEND timeout.<br />
  * Client sites wait for message activity the length of the<br />
  * DB_REP_HEARTBEAT_MONITOR timeout before concluding that the<br />
  * connection to the master is lost.  The DB_REP_HEARTBEAT_MONITOR<br />
  * timeout should be longer than the DB_REP_HEARTBEAT_SEND timeout.<br />
  */<br />
 if ((ret = dbenv-&gt;rep_set_timeout(dbenv, DB_REP_HEARTBEAT_SEND,<br />
     5000000)) != 0)<br />
  dbenv-&gt;err(dbenv, ret,<br />
      &#8220;Could not set heartbeat send timeout.\n&#8221;);<br />
 if ((ret = dbenv-&gt;rep_set_timeout(dbenv, DB_REP_HEARTBEAT_MONITOR,<br />
     10000000)) != 0)<br />
  dbenv-&gt;err(dbenv, ret,<br />
      &#8220;Could not set heartbeat monitor timeout.\n&#8221;);</p>
<p> /*<br />
  * The following repmgr features may also be useful to your<br />
  * application.  See Berkeley DB documentation for more details.<br />
  *  &#8211; Two-site strict majority rule &#8211; In a two-site replication<br />
  *    group, require both sites to be available to elect a new<br />
  *    master.<br />
  *  &#8211; Timeouts &#8211; Customize the amount of time repmgr waits<br />
  *    for such things as waiting for acknowledgements or attempting<br />
  *    to reconnect to other sites.<br />
  *  &#8211; Site list &#8211; return a list of sites currently known to repmgr.<br />
  */<br />
 /* 打开数据库环境。*/<br />
 if ((ret = env_init(dbenv, setup_info.home)) != 0)<br />
  goto err;</p>
<p> /* Start checkpoint and log archive threads. */<br />
 sup_args.dbenv = dbenv;<br />
 sup_args.shared = &amp;my_app_data.shared_data;<br />
 if ((ret = start_support_threads(dbenv, &amp;sup_args, &amp;ckp_thr,<br />
     &amp;lga_thr)) != 0)<br />
  goto err;</p>
<p> if ((ret = dbenv-&gt;repmgr_start(dbenv, 3, start_policy)) != 0)<br />
  goto err;</p>
<p> if ((ret = doloop(dbenv, &amp;my_app_data.shared_data)) != 0) {<br />
  dbenv-&gt;err(dbenv, ret, &#8220;Client failed&#8221;);<br />
  goto err;<br />
 }</p>
<p> /* Finish checkpoint and log archive threads. */<br />
 if ((ret = finish_support_threads(&amp;ckp_thr, &amp;lga_thr)) != 0)<br />
  goto err;</p>
<p> /*<br />
  * We have used the DB_TXN_NOSYNC environment flag for improved<br />
  * performance without the usual sacrifice of transactional durability,<br />
  * as discussed in the &#8220;Transactional guarantees&#8221; page of the Reference<br />
  * Guide: if one replication site crashes, we can expect the data to<br />
  * exist at another site.  However, in case we shut down all sites<br />
  * gracefully, we push out the end of the log here so that the most<br />
  * recent transactions don&#8217;t mysteriously disappear.<br />
  */<br />
 if ((ret = dbenv-&gt;log_flush(dbenv, NULL)) != 0) {<br />
  dbenv-&gt;err(dbenv, ret, &#8220;log_flush&#8221;);<br />
  goto err;<br />
 }</p>
<p>err:<br />
 if (dbenv != NULL &amp;&amp;<br />
     (t_ret = dbenv-&gt;close(dbenv, 0)) != 0) {<br />
  fprintf(stderr, &#8220;failure closing env: %s (%d)\n&#8221;,<br />
      db_strerror(t_ret), t_ret);<br />
  if (ret == 0)<br />
   ret = t_ret;<br />
 }</p>
<p> return (ret);<br />
}</p>
<p>/*<br />
 * 事件通知函数。当使用replication manager的时候，该事件已经被<br />
 * replication manager 适当地处理好了，所以在这个回调函数当中，<br />
 * 我们只需要简单地更新状态或者打印消息即可。<br />
 */<br />
static void<br />
event_callback(dbenv, which, info)<br />
 DB_ENV *dbenv;<br />
 u_int32_t which;<br />
 void *info;<br />
{<br />
 APP_DATA *app = dbenv-&gt;app_private;<br />
 SHARED_DATA *shared = &amp;app-&gt;shared_data;</p>
<p> info = NULL;    /* Currently unused. */</p>
<p> switch (which) {<br />
 case DB_EVENT_REP_CLIENT:<br />
  shared-&gt;is_master = 0;<br />
  shared-&gt;in_client_sync = 1;<br />
  break;</p>
<p> case DB_EVENT_REP_MASTER:<br />
  shared-&gt;is_master = 1;<br />
  shared-&gt;in_client_sync = 0;<br />
  break;</p>
<p> case DB_EVENT_REP_NEWMASTER:<br />
  shared-&gt;in_client_sync = 1;<br />
  break;</p>
<p> case DB_EVENT_REP_PERM_FAILED:<br />
  /*<br />
   * Did not get enough acks to guarantee transaction<br />
   * durability based on the configured ack policy.  This<br />
   * transaction will be flushed to the master site&#8217;s<br />
   * local disk storage for durability.<br />
   */<br />
  printf(<br />
    &#8220;Insufficient acknowledgements to guarantee transaction durability.\n&#8221;);<br />
  break;</p>
<p> case DB_EVENT_REP_STARTUPDONE:<br />
  shared-&gt;in_client_sync = 0;<br />
  break;</p>
<p> default:<br />
  dbenv-&gt;errx(dbenv, &#8220;ignoring event %d&#8221;, which);<br />
 }<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/10/berkeley-db%e7%a4%ba%e4%be%8b%e7%a8%8b%e5%ba%8f%e8%af%a6%e8%a7%a331/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Berkeley DB示例程序详解(3.2)</title>
		<link>http://www.bdbchina.com/2009/10/612/</link>
		<comments>http://www.bdbchina.com/2009/10/612/#comments</comments>
		<pubDate>Sun, 04 Oct 2009 08:36:30 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[HA]]></category>
		<category><![CDATA[replication]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=612</guid>
		<description><![CDATA[Berkeley DB示例程序详解]]></description>
			<content:encoded><![CDATA[<p>接上篇：Berkeley DB示例程序详解(3.1)<br />
/*<br />
* Perform command line parsing and common replication setup for the repmgr<br />
* and base replication example programs.<br />
*/<br />
int<br />
common_rep_setup(dbenv, argc, argv, setup_info)<br />
DB_ENV *dbenv;<br />
int argc;<br />
char *argv[];<br />
SETUP_DATA *setup_info;<span id="more-612"></span><br />
{<br />
repsite_t site;<br />
extern char *optarg;<br />
char ch, *portstr;<br />
int ack_policy, got_self, is_repmgr, maxsites, priority, ret;</p>
<p>got_self = is_repmgr = maxsites = ret = site.peer = 0;</p>
<p>/*<br />
* 站点的优先级。在选举的时候，如果多个站点具有最新的日志，那么<br />
* 优先级最高的站点成为master。所以，应该把后备做master的站点<br />
* 设置更高的优先级。<br />
*/<br />
priority = 100;</p>
<p>/*<br />
* 持久消息(permanent message, i.e. commit/abort/checkpoint日志)<br />
* 确认策略。repmgr要求replica根据持久消息做相同的动作<br />
* (commit/abort/checkpoint)，然后对持久消息进行确认。repmgr等待一定<br />
* 数目的replica确认，这个数目字这里设置。<br />
*/<br />
ack_policy = DB_REPMGR_ACKS_QUORUM;<br />
setup_info-&gt;role = UNKNOWN;<br />
if (strncmp(setup_info-&gt;progname, &#8220;ex_rep_mgr&#8221;, 10) == 0)<br />
is_repmgr = 1;</p>
<p>/*<br />
* Replication setup calls that are only needed if a command<br />
* line option is specified are made within this while/switch<br />
* statement.  Replication setup calls that should be made<br />
* whether or not a command line option is specified are after<br />
* this while/switch statement.<br />
*/<br />
while ((ch = getopt(argc, argv, &#8220;a:bCh:l:Mn:p:R:r:v&#8221;)) != EOF) {<br />
switch (ch) {<br />
case &#8216;a&#8217;:<br />
if (!is_repmgr)<br />
usage(is_repmgr, setup_info-&gt;progname);<br />
if (strncmp(optarg, &#8220;all&#8221;, 3) == 0)<br />
ack_policy = DB_REPMGR_ACKS_ALL;<br />
/* repmgr等待所有站点的持久消息确认。*/<br />
else if (strncmp(optarg, &#8220;quorum&#8221;, 6) != 0)<br />
usage(is_repmgr, setup_info-&gt;progname);<br />
break;<br />
case &#8216;b&#8217;:<br />
/*<br />
* 批量传输设置。在bulk buffer满了或者有了持久消息<br />
* (permanent message)的时候才传输所有的消息。<br />
* Configure bulk transfer to send groups of records<br />
* to clients in a single network transfer.  This is<br />
* useful for master sites and clients participating<br />
* in client-to-client synchronization.<br />
*/<br />
if ((ret = dbenv-&gt;rep_set_config(dbenv,<br />
DB_REP_CONF_BULK, 1)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not configure bulk transfer.\n&#8221;);<br />
goto err;<br />
}<br />
break;<br />
case &#8216;C&#8217;:<br />
setup_info-&gt;role = CLIENT;<br />
break;<br />
case &#8216;h&#8217;:<br />
setup_info-&gt;home = optarg;<br />
break;<br />
case &#8216;l&#8217;:<br />
setup_info-&gt;self.host = strtok(optarg, &#8220;:&#8221;);<br />
if ((portstr = strtok(NULL, &#8220;:&#8221;)) == NULL) {<br />
fprintf(stderr, &#8220;Bad host specification.\n&#8221;);<br />
goto err;<br />
}<br />
setup_info-&gt;self.port = (unsigned short)atoi(portstr);<br />
setup_info-&gt;self.peer = 0;<br />
got_self = 1;<br />
break;<br />
case &#8216;M&#8217;:<br />
setup_info-&gt;role = MASTER;<br />
break;<br />
case &#8216;n&#8217;:<br />
setup_info-&gt;nsites = atoi(optarg);<br />
/*<br />
* For repmgr, set the total number of sites in the<br />
* replication group.  This is used by repmgr internal<br />
* election processing.  For base replication, nsites<br />
* is simply passed back to main for use in its<br />
* communications and election processing.<br />
*/<br />
if (is_repmgr &amp;&amp; setup_info-&gt;nsites &gt; 0 &amp;&amp;<br />
(ret = dbenv-&gt;rep_set_nsites(dbenv,<br />
setup_info-&gt;nsites)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not set nsites.\n&#8221;);<br />
goto err;<br />
}<br />
break;<br />
case &#8216;p&#8217;:<br />
priority = atoi(optarg);<br />
break;<br />
case &#8216;R&#8217;:<br />
if (!is_repmgr)<br />
usage(is_repmgr, setup_info-&gt;progname);<br />
site.peer = 1; /* FALLTHROUGH */<br />
case &#8216;r&#8217;:<br />
site.host = optarg;<br />
site.host = strtok(site.host, &#8220;:&#8221;);<br />
if ((portstr = strtok(NULL, &#8220;:&#8221;)) == NULL) {<br />
fprintf(stderr, &#8220;Bad host specification.\n&#8221;);<br />
goto err;<br />
}<br />
site.port = (unsigned short)atoi(portstr);<br />
if (setup_info-&gt;site_list == NULL ||<br />
setup_info-&gt;remotesites &gt;= maxsites) {<br />
maxsites = maxsites == 0 ? 10 : 2 * maxsites;<br />
if ((setup_info-&gt;site_list =<br />
realloc(setup_info-&gt;site_list,<br />
maxsites * sizeof(repsite_t))) == NULL) {<br />
fprintf(stderr, &#8220;System error %s\n&#8221;,<br />
strerror(errno));<br />
goto err;<br />
}<br />
}<br />
(setup_info-&gt;site_list)[(setup_info-&gt;remotesites)++] =<br />
site;<br />
site.peer = 0;<br />
break;<br />
case &#8216;v&#8217;:<br />
if ((ret = dbenv-&gt;set_verbose(dbenv,<br />
DB_VERB_REPLICATION, 1)) != 0)<br />
goto err;<br />
break;<br />
case &#8216;?&#8217;:<br />
default:<br />
usage(is_repmgr, setup_info-&gt;progname);<br />
}<br />
}</p>
<p>/* Error check command line. */<br />
if (!got_self || setup_info-&gt;home == NULL)<br />
usage(is_repmgr, setup_info-&gt;progname);<br />
if (!is_repmgr &amp;&amp; setup_info-&gt;role == UNKNOWN) {<br />
fprintf(stderr, &#8220;Must specify -M or -C.\n&#8221;);<br />
goto err;<br />
}</p>
<p>/*<br />
* Set replication group election priority for this environment.<br />
* An election first selects the site with the most recent log<br />
* records as the new master.  If multiple sites have the most<br />
* recent log records, the site with the highest priority value<br />
* is selected as master.<br />
*/<br />
if ((ret = dbenv-&gt;rep_set_priority(dbenv, priority)) != 0) {<br />
dbenv-&gt;err(dbenv, ret, &#8220;Could not set priority.\n&#8221;);<br />
goto err;<br />
}</p>
<p>/*<br />
* 设置持久消息确认策略。<br />
* For repmgr, set the policy that determines how master and client<br />
* sites handle acknowledgement of replication messages needed for<br />
* permanent records.  The default policy of &#8220;quorum&#8221; requires only<br />
* a quorum of electable peers sufficient to ensure a permanent<br />
* record remains durable if an election is held.  The &#8220;all&#8221; option<br />
* requires all clients to acknowledge a permanent replication<br />
* message instead.<br />
*/<br />
if (is_repmgr &amp;&amp;<br />
(ret = dbenv-&gt;repmgr_set_ack_policy(dbenv, ack_policy)) != 0) {<br />
dbenv-&gt;err(dbenv, ret, &#8220;Could not set ack policy.\n&#8221;);<br />
goto err;<br />
}</p>
<p>/*<br />
* Set the threshold for the minimum and maximum time the client<br />
* waits before requesting retransmission of a missing message.<br />
* Base these values on the performance and load characteristics<br />
* of the master and client host platforms as well as the round<br />
* trip message time.<br />
* 设置丢失的日志消息的重传时限。如果Berkeley DB发现有日志消息丢失，<br />
* 比如它发现连续收到的两条日志消息并不连续，它会在等待20秒后请求<br />
* 重传这条日志消息，如果仍然没有收到，它会加倍等待的时间，直到该<br />
* 等待时间达到500秒。<br />
*<br />
* 在使用repmgr的时候，由于使用了TCP/IP协议，不可能丢失消息，<br />
* 所以可以不设置；如果使用不可靠的传输协议比如udp，那么可以通过<br />
* 设置这个时限，确保能够收到丢失的日志消息。也就是说，Berkeley DB<br />
* 内部实现了一个确保不可靠传输协议可靠地传输数据的协议，这样如果<br />
* 我们使用UDP协议来传输Berkeley DB replication消息，那么丢包和<br />
* 包错序不会影响Berkeley DB replication 的正常工作。<br />
*/<br />
if ((ret = dbenv-&gt;rep_set_request(dbenv, 20000, 500000)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not set client_retransmission defaults.\n&#8221;);<br />
goto err;<br />
}</p>
<p>/*<br />
* Configure deadlock detection to ensure that any deadlocks<br />
* are broken by having one of the conflicting lock requests<br />
* rejected. DB_LOCK_DEFAULT uses the lock policy specified<br />
* at environment creation time or DB_LOCK_RANDOM if none was<br />
* specified.<br />
*/<br />
if ((ret = dbenv-&gt;set_lk_detect(dbenv, DB_LOCK_DEFAULT)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not configure deadlock detection.\n&#8221;);<br />
goto err;<br />
}</p>
<p>/* The following base replication features may also be useful to your<br />
* application. See Berkeley DB documentation for more details.<br />
*   &#8211; Master leases: Provide stricter consistency for data reads<br />
*     on a master site.<br />
*   &#8211; Timeouts: Customize the amount of time Berkeley DB waits<br />
*     for such things as an election to be concluded or a master<br />
*     lease to be granted.<br />
*   &#8211; Delayed client synchronization: Manage the master site&#8217;s<br />
*     resources by spreading out resource-intensive client<br />
*     synchronizations.<br />
*   &#8211; Blocked client operations: Return immediately with an error<br />
*     instead of waiting indefinitely if a client operation is<br />
*     blocked by an ongoing client synchronization.<br />
*/</p>
<p>err:<br />
return (ret);<br />
}</p>
<p>static int<br />
print_stocks(dbp)<br />
DB *dbp;<br />
{<br />
DBC *dbc;<br />
DBT key, data;<br />
#define MAXKEYSIZE 10<br />
#define MAXDATASIZE 20<br />
char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];<br />
int ret, t_ret;<br />
u_int32_t keysize, datasize;</p>
<p>/* 读取数据，打印数据库当中所有记录。这个函数可以在master和每一个<br />
* replica上面执行。*/<br />
if ((ret = dbp-&gt;cursor(dbp, NULL, &amp;dbc, 0)) != 0) {<br />
dbp-&gt;err(dbp, ret, &#8220;can&#8217;t open cursor&#8221;);<br />
return (ret);<br />
}</p>
<p>memset(&amp;key, 0, sizeof(key));<br />
memset(&amp;data, 0, sizeof(data));</p>
<p>printf(&#8220;\tSymbol\tPrice\n&#8221;);<br />
printf(&#8220;\t======\t=====\n&#8221;);<br />
/* 无论master上面还是replica上面，都是用完全相同的方法读取数据。*/<br />
for (ret = dbc-&gt;get(dbc, &amp;key, &amp;data, DB_FIRST);<br />
ret == 0;<br />
ret = dbc-&gt;get(dbc, &amp;key, &amp;data, DB_NEXT)) {<br />
keysize = key.size &gt; MAXKEYSIZE ? MAXKEYSIZE : key.size;<br />
memcpy(keybuf, key.data, keysize);<br />
keybuf[keysize] = &#8216;\0&#8242;;</p>
<p>datasize = data.size &gt;= MAXDATASIZE ? MAXDATASIZE : data.size;<br />
memcpy(databuf, data.data, datasize);<br />
databuf[datasize] = &#8216;\0&#8242;;</p>
<p>printf(&#8220;\t%s\t%s\n&#8221;, keybuf, databuf);<br />
}<br />
printf(&#8220;\n&#8221;);<br />
fflush(stdout);</p>
<p>if ((t_ret = dbc-&gt;close(dbc)) != 0 &amp;&amp; ret == 0)<br />
ret = t_ret;</p>
<p>switch (ret) {<br />
case 0:<br />
case DB_NOTFOUND:<br />
case DB_LOCK_DEADLOCK:<br />
return (0);<br />
default:<br />
return (ret);<br />
}<br />
}</p>
<p>/* Start checkpoint and log archive support threads. */<br />
int<br />
start_support_threads(dbenv, sup_args, ckp_thr, lga_thr)<br />
DB_ENV *dbenv;<br />
supthr_args *sup_args;<br />
thread_t *ckp_thr;<br />
thread_t *lga_thr;<br />
{<br />
int ret;</p>
<p>ret = 0;<br />
if ((ret = thread_create(ckp_thr, NULL, checkpoint_thread,<br />
sup_args)) != 0) {<br />
dbenv-&gt;errx(dbenv, &#8220;can&#8217;t create checkpoint thread&#8221;);<br />
goto err;<br />
}<br />
if ((ret = thread_create(lga_thr, NULL, log_archive_thread,<br />
sup_args)) != 0)<br />
dbenv-&gt;errx(dbenv, &#8220;can&#8217;t create log archive thread&#8221;);<br />
err:<br />
return (ret);</p>
<p>}</p>
<p>/* Wait for checkpoint and log archive support threads to finish. */<br />
int<br />
finish_support_threads(ckp_thr, lga_thr)<br />
thread_t *ckp_thr;<br />
thread_t *lga_thr;<br />
{<br />
void *ctstatus, *ltstatus;<br />
int ret;</p>
<p>ret = 0;<br />
if (thread_join(*lga_thr, &amp;ltstatus) ||<br />
thread_join(*ckp_thr, &amp;ctstatus)) {<br />
ret = -1;<br />
goto err;<br />
}<br />
if ((uintptr_t)ltstatus != EXIT_SUCCESS ||<br />
(uintptr_t)ctstatus != EXIT_SUCCESS)<br />
ret = -1;<br />
err:<br />
return (ret);<br />
}</p>
<p>#define BUFSIZE 1024</p>
<p>int<br />
doloop(dbenv, shared_data)<br />
DB_ENV *dbenv;<br />
SHARED_DATA *shared_data;<br />
{<br />
DB *dbp;<br />
DBT key, data;<br />
char buf[BUFSIZE], *first, *price;<br />
u_int32_t flags;<br />
int ret;</p>
<p>dbp = NULL;<br />
ret = 0;<br />
memset(&amp;key, 0, sizeof(key));<br />
memset(&amp;data, 0, sizeof(data));</p>
<p>for (;;) {<br />
printf(&#8220;QUOTESERVER%s&gt; &#8220;,<br />
shared_data-&gt;is_master ? &#8220;&#8221; : &#8221; (read-only)&#8221;);<br />
fflush(stdout);</p>
<p>if (fgets(buf, sizeof(buf), stdin) == NULL)<br />
break;</p>
<p>#define DELIM &#8221; \t\n&#8221;<br />
if ((first = strtok(&amp;buf[0], DELIM)) == NULL) {<br />
/* Blank input line. */<br />
price = NULL;<br />
} else if ((price = strtok(NULL, DELIM)) == NULL) {<br />
/* Just one input token. */<br />
if (strncmp(buf, &#8220;exit&#8221;, 4) == 0 ||<br />
strncmp(buf, &#8220;quit&#8221;, 4) == 0) {<br />
/*<br />
* This makes the checkpoint and log<br />
* archive threads stop.<br />
*/<br />
shared_data-&gt;app_finished = 1;<br />
break;<br />
}<br />
dbenv-&gt;errx(dbenv, &#8220;Format: TICKER VALUE&#8221;);<br />
continue;<br />
} else {<br />
/* Normal two-token input line. */<br />
if (first != NULL &amp;&amp; !shared_data-&gt;is_master) {<br />
dbenv-&gt;errx(dbenv, &#8220;Can&#8217;t update at client&#8221;);<br />
continue;<br />
}<br />
}</p>
<p>if (dbp == NULL) {<br />
if ((ret = db_create(&amp;dbp, dbenv, 0)) != 0)<br />
return (ret);</p>
<p>flags = DB_AUTO_COMMIT;<br />
/*<br />
* Open database with DB_CREATE only if this is<br />
* a master database.  A client database uses<br />
* polling to attempt to open the database without<br />
* DB_CREATE until it is successful. 由于数据库<br />
* 的创建也是一个写入操作，所以，replica不应该自<br />
* 己创建数据库，而是定期检查是否有数据库被创建<br />
* 好。当master创建了数据库后，这个日志消息会到<br />
* 达所有的replica上面，于是Berkeley DB的<br />
* replication功能自动创建好这个数据库。<br />
*<br />
* This DB_CREATE polling logic can be simplified<br />
* under some circumstances.  For example, if the<br />
* application can be sure a database is already<br />
* there, it would never need to open it with<br />
* DB_CREATE.<br />
*/<br />
if (shared_data-&gt;is_master)<br />
flags |= DB_CREATE;<br />
if ((ret = dbp-&gt;open(dbp,<br />
NULL, DATABASE, NULL, DB_BTREE, flags, 0)) != 0) {<br />
if (ret == ENOENT) {<br />
printf(<br />
&#8220;No stock database yet available.\n&#8221;);<br />
if ((ret = dbp-&gt;close(dbp, 0)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;DB-&gt;close&#8221;);<br />
goto err;<br />
}<br />
dbp = NULL;<br />
continue;<br />
}<br />
if (ret == DB_REP_HANDLE_DEAD ||<br />
ret == DB_LOCK_DEADLOCK) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;please retry the operation&#8221;);<br />
dbp-&gt;close(dbp, DB_NOSYNC);<br />
dbp = NULL;<br />
continue;<br />
}<br />
dbenv-&gt;err(dbenv, ret, &#8220;DB-&gt;open&#8221;);<br />
goto err;<br />
}<br />
}</p>
<p>if (first == NULL) {<br />
/*<br />
* If this is a client in the middle of<br />
* synchronizing with the master, the client data<br />
* is possibly stale and won&#8217;t be displayed until<br />
* client synchronization is finished.  It is also<br />
* possible to display the stale data if this is<br />
* acceptable to the application.<br />
*/<br />
if (shared_data-&gt;in_client_sync)<br />
printf(<br />
&#8220;Cannot read data during client synchronization &#8211; please try again.\n&#8221;);<br />
else<br />
switch ((ret = print_stocks(dbp))) {<br />
case 0:<br />
break;<br />
case DB_REP_HANDLE_DEAD:<br />
(void)dbp-&gt;close(dbp, DB_NOSYNC);<br />
dbp = NULL;<br />
break;<br />
default:<br />
dbp-&gt;err(dbp, ret,<br />
&#8220;Error traversing data&#8221;);<br />
goto err;<br />
}<br />
} else {<br />
key.data = first;<br />
key.size = (u_int32_t)strlen(first);</p>
<p>data.data = price;<br />
data.size = (u_int32_t)strlen(price);</p>
<p>if ((ret = dbp-&gt;put(dbp,<br />
NULL, &amp;key, &amp;data, DB_AUTO_COMMIT)) != 0) {<br />
dbp-&gt;err(dbp, ret, &#8220;DB-&gt;put&#8221;);<br />
goto err;<br />
}<br />
}<br />
}</p>
<p>err: if (dbp != NULL)<br />
(void)dbp-&gt;close(dbp, DB_NOSYNC);<br />
return (ret);<br />
}</p>
<p>int<br />
create_env(progname, dbenvp)<br />
const char *progname;<br />
DB_ENV **dbenvp;<br />
{<br />
DB_ENV *dbenv;<br />
int ret;</p>
<p>if ((ret = db_env_create(&amp;dbenv, 0)) != 0) {<br />
fprintf(stderr, &#8220;can&#8217;t create env handle: %s\n&#8221;,<br />
db_strerror(ret));<br />
return (ret);<br />
}</p>
<p>dbenv-&gt;set_errfile(dbenv, stderr);<br />
dbenv-&gt;set_errpfx(dbenv, progname);</p>
<p>*dbenvp = dbenv;<br />
return (0);<br />
}</p>
<p>/* Open and configure an environment. */<br />
int<br />
env_init(dbenv, home)<br />
DB_ENV *dbenv;<br />
const char *home;<br />
{<br />
u_int32_t flags;<br />
int ret;</p>
<p>(void)dbenv-&gt;set_cachesize(dbenv, 0, CACHESIZE, 0);<br />
(void)dbenv-&gt;set_flags(dbenv, DB_TXN_NOSYNC, 1);</p>
<p>flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |<br />
DB_INIT_REP | DB_INIT_TXN | DB_RECOVER | DB_THREAD;<br />
if ((ret = dbenv-&gt;open(dbenv, home, flags, 0)) != 0)<br />
dbenv-&gt;err(dbenv, ret, &#8220;can&#8217;t open environment&#8221;);<br />
return (ret);<br />
}</p>
<p>/*<br />
* In this application, we specify all communication via the command line.  In<br />
* a real application, we would expect that information about the other sites<br />
* in the system would be maintained in some sort of configuration file.  The<br />
* critical part of this interface is that we assume at startup that we can<br />
* find out<br />
* 1) what host/port we wish to listen on for connections,<br />
* 2) a (possibly empty) list of other sites we should attempt to connect<br />
* to; and<br />
* 3) what our Berkeley DB home environment is.<br />
*<br />
* These pieces of information are expressed by the following flags.<br />
* -a all|quorum (optional; repmgr only, a stands for ack policy)<br />
* -b (optional, b stands for bulk)<br />
* -C or -M start up as client or master (optional for repmgr, required<br />
*      for base example)<br />
* -h home directory (required)<br />
* -l host:port (required; l stands for local)<br />
* -n nsites (optional; number of sites in replication group; defaults to 0<br />
* in which case we try to dynamically compute the number of sites in<br />
* the replication group)<br />
* -p priority (optional: defaults to 100)<br />
* -r host:port (optional; r stands for remote; any number of these may be<br />
* specified)<br />
* -R host:port (optional; repmgr only, remote peer)<br />
* -v (optional; v stands for verbose)<br />
*/<br />
void<br />
usage(is_repmgr, progname)<br />
const int is_repmgr;<br />
const char *progname;<br />
{<br />
fprintf(stderr, &#8220;usage: %s &#8220;, progname);<br />
if (is_repmgr)<br />
fprintf(stderr, &#8220;[-CM]-h home -l host:port[-r host:port]%s%s&#8221;,<br />
&#8220;[-R host:port][-a all|quorum][-b][-n nsites]&#8220;,<br />
&#8220;[-p priority][-v]\n&#8221;);<br />
else<br />
fprintf(stderr, &#8220;-CM -h home -l host:port[-r host:port]%s&#8221;,<br />
&#8220;[-b][-n nsites][-p priority][-v]\n&#8221;);<br />
exit(EXIT_FAILURE);<br />
}</p>
<p>/*<br />
* This is a very simple thread that performs checkpoints at a fixed<br />
* time interval.  For a master site, the time interval is one minute<br />
* plus the duration of the checkpoint_delay timeout (30 seconds by<br />
* default.)  For a client site, the time interval is one minute.<br />
*/<br />
void *<br />
checkpoint_thread(args)<br />
void *args;<br />
{<br />
DB_ENV *dbenv;<br />
SHARED_DATA *shared;<br />
supthr_args *ca;<br />
int i, ret;</p>
<p>ca = (supthr_args *)args;<br />
dbenv = ca-&gt;dbenv;<br />
shared = ca-&gt;shared;</p>
<p>for (;;) {<br />
/*<br />
* Wait for one minute, polling once per second to see if<br />
* application has finished.  When application has finished,<br />
* terminate this thread.<br />
*/<br />
for (i = 0; i &lt; 60; i++) {<br />
sleep(1);<br />
if (shared-&gt;app_finished == 1)<br />
return ((void *)EXIT_SUCCESS);<br />
}</p>
<p>/* Perform a checkpoint. */<br />
if ((ret = dbenv-&gt;txn_checkpoint(dbenv, 0, 0, 0)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not perform checkpoint.\n&#8221;);<br />
return ((void *)EXIT_FAILURE);<br />
}<br />
}<br />
}</p>
<p>/*<br />
* This is a simple log archive thread.  Once per minute, it removes all but<br />
* the most recent 3 logs that are safe to remove according to a call to<br />
* DB_ENV-&gt;log_archive().<br />
*<br />
* Log cleanup is needed to conserve disk space, but aggressive log cleanup<br />
* can cause more frequent client initializations if a client lags too far<br />
* behind the current master.  This can happen in the event of a slow client,<br />
* a network partition, or a new master that has not kept as many logs as the<br />
* previous master.<br />
*<br />
* The approach in this routine balances the need to mitigate against a<br />
* lagging client by keeping a few more of the most recent unneeded logs<br />
* with the need to conserve disk space by regularly cleaning up log files.<br />
* Use of automatic log removal (DB_ENV-&gt;log_set_config() DB_LOG_AUTO_REMOVE<br />
* flag) is not recommended for replication due to the risk of frequent<br />
* client initializations.<br />
*/<br />
void *<br />
log_archive_thread(args)<br />
void *args;<br />
{<br />
DB_ENV *dbenv;<br />
SHARED_DATA *shared;<br />
char **begin, **list;<br />
supthr_args *la;<br />
int i, listlen, logs_to_keep, minlog, ret;</p>
<p>la = (supthr_args *)args;<br />
dbenv = la-&gt;dbenv;<br />
shared = la-&gt;shared;<br />
logs_to_keep = 3;</p>
<p>for (;;) {<br />
/*<br />
* Wait for one minute, polling once per second to see if<br />
* application has finished.  When application has finished,<br />
* terminate this thread.<br />
*/<br />
for (i = 0; i &lt; 60; i++) {<br />
sleep(1);<br />
if (shared-&gt;app_finished == 1)<br />
return ((void *)EXIT_SUCCESS);<br />
}</p>
<p>/* Get the list of unneeded log files. */<br />
if ((ret = dbenv-&gt;log_archive(dbenv, &amp;list, DB_ARCH_ABS))<br />
!= 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;Could not get log archive list.&#8221;);<br />
return ((void *)EXIT_FAILURE);<br />
}<br />
if (list != NULL) {<br />
listlen = 0;<br />
/* Get the number of logs in the list. */<br />
for (begin = list; *begin != NULL; begin++, listlen++);<br />
/*<br />
* Remove all but the logs_to_keep most recent<br />
* unneeded log files.<br />
*/<br />
minlog = listlen &#8211; logs_to_keep;<br />
for (begin = list, i= 0; i &lt; minlog; list++, i++) {<br />
if ((ret = unlink(*list)) != 0) {<br />
dbenv-&gt;err(dbenv, ret,<br />
&#8220;logclean: remove %s&#8221;, *list);<br />
dbenv-&gt;errx(dbenv,<br />
&#8220;logclean: Error remove %s&#8221;, *list);<br />
free(begin);<br />
return ((void *)EXIT_FAILURE);<br />
}<br />
}<br />
free(begin);<br />
}<br />
}<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/10/612/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Berkeley DB 4.8 的 dbstl API</title>
		<link>http://www.bdbchina.com/2009/09/berkeley-db-48-%e7%9a%84-dbstl-api/</link>
		<comments>http://www.bdbchina.com/2009/09/berkeley-db-48-%e7%9a%84-dbstl-api/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 07:45:28 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=610</guid>
		<description><![CDATA[dbstl 简介]]></description>
			<content:encoded><![CDATA[<p>Berkeley DB 4.8 刚刚发布，我的心情格外激动。不仅仅是因为这个版本<br />
是Berkeley DB新增了很多新功能，在很多方面有不小的增强，更因为在<br />
众多的新功能中，有主要由我设计开发的STL API。 作为增强Berkeley DB的<br />
易用性或者叫做用户友好性的重要组成部分，STL API是Berkeley DB4.8<br />
的主要的新特性之一。<br />
<span id="more-610"></span><br />
Berkeley DB的STL API也叫做dbstl或者DB STL, 它的作用是让C++程序员更加容易地<br />
使用Berkeley DB嵌入式数据库。如果你用过Berkeley DB的C/C++ API, 你就会<br />
发现，C++ API是对C API的非常简单的封装，并没有体现出C++语言在软件<br />
设计方面的优势，它的易用性并不好。</p>
<p>STL API 就是为了增强Berkeley DB<br />
的易用性而设计的。使用Berkeley DB STL API, 你就好像在使用C++ STL类库<br />
一样：你操纵的是容器和iterator, 向容器中插入数据，数据就插入到了<br />
Berkeley DB数据库中;使用iterator遍历容器，就可以遍历数据库中的数据。<br />
而且你不需要与Dbt, Dbc, DbTxn等类型的对象打交道，不需要每次存储或者<br />
读取数据，都封装到一个Dbt对象(也叫做marshal)，或者从Dbt对象中读取出<br />
数据(也叫做unmarshal)。你只需要像使用C++ STL容器类和<br />
iterator那样使用dbstl，就可以插入/删除/更新/查询 Berkeley DB数据库；<br />
同时，你也不需要手动关闭cursor, transaction,<br />
database, environment，还要保证正确的关闭顺序 &#8212; 它们的生命周期由<br />
dbstl自动正确地管理。</p>
<p>dbstl被设计成几乎完全符合C++ STL规范，所以你可以像使用C++ STL<br />
的容器和iterator那样使用 dbstl. 比如它的iterator符合C++ STL 的<br />
规定，这样，c++ STL的算法就可以通过STL API<br />
操纵Berkeley DB中的数据; c++ STL 的 container adapters, 诸如std::stack,<br />
std::queue和std::priority_queue可以使用dbstl::db_vector 容器，这样<br />
我们就可以非常轻易地用Berkeley DB数据库建立栈，队列，优先级队列。事实上，<br />
如果你有一段使用了C++ STL的代码，你只需要替换容器类模板的名称，<br />
以及其他一些微小的改动，你就可以使用dbstl了。</p>
<p>Dbstl的性能开销很小，而且有较好的可移植性。只要一个平台支持Berkeley DB，<br />
并且拥有一个符合ISO C++标准的编译器，就一定可以使用dbstl. gcc3.4.4及以上，<br />
intel c++ compiler 9及以上，以及msvc8，都是经验正支持dbstl的编译器。而且<br />
dbstl通过了MS的STL测试包，以及SGI STL的测试包, 也就是说，它是符合C++ STL<br />
标准的，你完全可以当dbstl是一个标准的c++ STL类库来使用它。</p>
<p>DB STL 的典型用例:<br />
1. 当你的程序操纵着很大量的数据，无法容纳在内存当中的时候。此时C++ STL<br />
会导致频繁的操作系统页交换，使得整台机器性能显著下降；如果使用dbstl,那么<br />
由于Berkeley DB的缓存管理功能，只有用到的数据被保留在内存当中，<br />
所以机器的全局性能不会受到任何影响。</p>
<p>2. 你希望使用一个熟悉的接口来使用Berkeley DB.<br />
dbstl为Berkeley DB提供了一个熟悉的接口，因为几乎每一个c++程序员都接触过STL类库。<br />
Berkeley DB特有的繁琐的DBT, DBC, DB_TXN等等的操作都被隐藏起来了。</p>
<p>3. 你需要拥有事务语义的数据结构。dbstl可选的提供了事务的ACID属性，并且支持全部的<br />
C++ STL 容器功能。</p>
<p>4. 并发的访问控制<br />
目前几乎所有的C++ STL实现只支持并发访问一个容器类的不同的容器对象。当你需要并发<br />
地访问同一个容器对象时候，都需要显式的mutex保护。而使用dbstl的话，你就可以并发<br />
地在多线程当中访问同一个容器对象，甚至并发地在多个进程当中通过各个进程当中的<br />
容器对象操纵同一个数据库，并发访问的灵活性大大增加。</p>
<p>5. 对象持久化<br />
dbstl允许你的程序在数据库中存储对象，并且在你的程序的多次运行之间存储和复活对象。<br />
而且在用户提供适当的回调函数后，dbstl可以存储任意复杂的对象。</p>
<p>作为dbstl的主要设计和开发者， 毫不夸张地说，目前我是这个世界上最熟悉dbstl的人了，呵呵。<br />
欢迎使用Berkeley DB的这个新功能，如果遇到问题可以在这里问我，或者到<br />
Oracle Technology Network上面的Berkeley DB版面提问。</p>
<p>附注：<br />
dbstl的参考文档: http://www.oracle.com/technology/documentation/berkeley-db/db/programmer_reference/index.html  的第 7 章</p>
<p>dbstl 的类/函数 文档： http://www.oracle.com/technology/documentation/berkeley-db/db/api_reference/STL/frame_main.html</p>
<p>dbstl示例代码： Berkeley DB source root/examples_stl里面有若干个示例程序；在 Berkeley DB source root/test_stl/base 里面，有针对dbstl的所有功能点的测试代码，可以作为学习使用dbstl的功能的示例代码。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/09/berkeley-db-48-%e7%9a%84-dbstl-api/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>An Example of Mercurial Being a Distributed Version Control System</title>
		<link>http://www.bdbchina.com/2009/07/an-example-of-mercurial-being-a-distributed-version-control-system/</link>
		<comments>http://www.bdbchina.com/2009/07/an-example-of-mercurial-being-a-distributed-version-control-system/#comments</comments>
		<pubDate>Thu, 30 Jul 2009 05:09:23 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[mercurial]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=460</guid>
		<description><![CDATA[This article talks about some interesting features of mercurial.]]></description>
			<content:encoded><![CDATA[<p>We internally use mercurial as the source version control system.<br />
Mercurial is a very useful and powerful source control system, the difference<br />
from cvs/svn is that, mercurial is a distributed source control system, see<br />
its official doc for more information about this feature.<span id="more-460"></span></p>
<p>Today I ran into a very interesting situation: I have a hg repository R on a server S,<br />
and I cloned two copies from it, one is also on S, let&#8217;s call it B; The other on<br />
my PC, let&#8217;s call it C. And I didn&#8217;t know that I actually cloned C from B, rather<br />
than R, as how people usually worked. I could do so because mercurial is a distributed<br />
version control system, each copy of repository has enough and all information about<br />
the entire repository of the group.</p>
<p>However, I could do hg pull/hg commit/hg push in C, though I have been doing so to<br />
B rather than R, and it is OK, as explained above. And I sometime later discovered<br />
this situation when I did &#8220;hg pull -u&#8221; on B, believing B&#8217;s working copy would be<br />
updated from R since I had already pushed to R from C, but B wasn&#8217;t updated.<br />
Then I realized my repo address in C may be wrong, and yes, it was not R but B.<br />
Then, I did &#8220;hg commit&#8221; followed by &#8220;hg push&#8221; in B, the two actions pushed all<br />
changes which were committed by C into B, into R this time. And since B already had the<br />
changes in its local repo, I can do a &#8220;hg update&#8221; to update its working source,<br />
so that now B&#8217;s working source also have the latest code.</p>
<p>And then I update C&#8217;s repo address to use R instead, then all C&#8217;s commits goes to R, and<br />
I can do &#8220;hg pull -u&#8221; in B to update B&#8217;s local repo and working copy, as<br />
people normally work.</p>
<p>That is to say, any mercurial repository can be a central repo, for other<br />
repos to do hg clone, hg commit or hg push; each local repo is equal to the central repo,<br />
all repos including the &#8220;central&#8221; repo are working as identical and equal peers,<br />
rather than working in a &#8220;client/server&#8221; mode like cvs, where clients can&#8217;t be<br />
a server. Actually this &#8220;central&#8221; repo is only put there for ease of use in a group, the<br />
group can work well if that &#8220;central&#8221; repo was some group member&#8217;s local repo;</p>
<p>I think the above situation is a vivid example of mercurial&#8217;s being a distributed<br />
source control system.</p>
<p>PS:</p>
<p>1. Another important thing to note:</p>
<p>Although we can apply a patch generated from &#8220;hg diff&#8221; using the GNU patch utility, instead<br />
of using hg import &#8211;no-commit, hg import &#8211;no-commit sometimes can not be replaced<br />
by the GNU patch utility. For example, when a patch involves a change made by<br />
&#8220;hg add&#8221;, &#8220;hg remove&#8221; or &#8220;hg rename&#8221; commands.</p>
<p>When applying such a patch to the working source code, we must use &#8220;hg import &#8211;no-commit&#8221;,<br />
otherwise, the change may not appear in the working source directory.</p>
<p>2. We need to set the following to our local repo&#8217;s .hg/hgrc file:</p>
<p>[diff]<br />
git = 1</p>
<p>Without the above setting, when you do &#8220;hg rename F&#8221;, the changes generated simply<br />
consists of the changes which would be generated by &#8220;hg remove F&#8221; followed by &#8220;hg add F&#8221;.<br />
Such a change is not only lengthy, but also confusing.</p>
<p>With the above setting, the &#8220;hg rename&#8221; command won&#8217;t be generating such stupid changeset,<br />
it simply marks the file F&#8217;s name has changed, only two lines in the changeset.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/07/an-example-of-mercurial-being-a-distributed-version-control-system/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>完全使用Linux工作</title>
		<link>http://www.bdbchina.com/2009/07/%e5%ae%8c%e5%85%a8%e4%bd%bf%e7%94%a8linux%e5%b7%a5%e4%bd%9c/</link>
		<comments>http://www.bdbchina.com/2009/07/%e5%ae%8c%e5%85%a8%e4%bd%bf%e7%94%a8linux%e5%b7%a5%e4%bd%9c/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 03:13:02 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[生活圆桌]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=351</guid>
		<description><![CDATA[工作后很久以来，我一直在Linux环境下工作，除了偶尔的需要在Windows 上面使用MSVC编译运行一些测试和示例程序之外。不过我是在Windows XP上面使用cygwin来模拟Linux环境的，外加ssh到一个Linux服务器做一部分工作。Cygwin几乎可以完全地模拟Linux环境，但是偶尔还是会感到不方便。而我之前没有在laptop上面安装和使用 Linux是因为我偶尔需要使用qq，以及有些网站不用IE无法正确地使用。 而现在，这两个障碍都排除了：Tencent推出了WebQQ(别笑我out哈，貌似 很多人已经用这个N个月了)，虽然没有视频语音等功能，但是我也不会用到那些功能，而且基本功能都已经具备，比Linux上面那些多协议的兼容性的聊天工具，比如pidgin好用多了。而代替IE的，是IEs4Linux，这个工具基于Wine，而Wine能够使得为Windows平台而build的应用程序可以直接在Linux上面运行，是一个非常棒的运行环境。我个人认为它比虚拟机更好，因为使用虚拟机的话，我首先要安装还要启动一个 进程，另外速度也较慢。而Wine并不需要启动一个进程来运行Windows程序，它是一个静态的运行时环境，几乎没有额外运行时开销。
事实上IEs4Linux应该存在了很长一段时间了，我应该早点去找到它的，有一种相见恨晚的感觉！这个世界上有如此多的有才能而又乐于贡献的程序员，以至于当我们有需求的时候，似乎总能够找到解决方案，真是太好了！
这样，我可以在Linux上面使用qq和IE了，也就意味着我可以完全不使用Windows了，除了下面两种情形：
1. 需要使用MSVC。
2. 需要使用淘宝等在线支付工具，以及其他银行的安全控件。因为毕竟IEs4Linux没有法律保障，呵呵。
总之，98%的用例已经可以由我的Linux满足了，而且我的工作效率还会大大提升，确实是一个不错的选择！
]]></description>
			<content:encoded><![CDATA[<p>工作后很久以来，我一直在Linux环境下工作，除了偶尔的需要在Windows 上面使用MSVC编译运行一些测试和示例程序之外。不过我是在Windows XP上面使用cygwin来模拟Linux环境的，外加ssh到一个Linux服务器做一部分工作。Cygwin几乎可以完全地模拟Linux环境，但是偶尔还是会感到不方便。而我之前没有在laptop上面安装和使用 Linux是因为我偶尔需要使用qq，以及有些网站不用IE无法正确地使用。<span id="more-351"></span> 而现在，这两个障碍都排除了：Tencent推出了WebQQ(别笑我out哈，貌似 很多人已经用这个N个月了)，虽然没有视频语音等功能，但是我也不会用到那些功能，而且基本功能都已经具备，比Linux上面那些多协议的兼容性的聊天工具，比如pidgin好用多了。而代替IE的，是IEs4Linux，这个工具基于Wine，而Wine能够使得为Windows平台而build的应用程序可以直接在Linux上面运行，是一个非常棒的运行环境。我个人认为它比虚拟机更好，因为使用虚拟机的话，我首先要安装还要启动一个 进程，另外速度也较慢。而Wine并不需要启动一个进程来运行Windows程序，它是一个静态的运行时环境，几乎没有额外运行时开销。</p>
<p>事实上IEs4Linux应该存在了很长一段时间了，我应该早点去找到它的，有一种相见恨晚的感觉！这个世界上有如此多的有才能而又乐于贡献的程序员，以至于当我们有需求的时候，似乎总能够找到解决方案，真是太好了！</p>
<p>这样，我可以在Linux上面使用qq和IE了，也就意味着我可以完全不使用Windows了，除了下面两种情形：<br />
1. 需要使用MSVC。<br />
2. 需要使用淘宝等在线支付工具，以及其他银行的安全控件。因为毕竟IEs4Linux没有法律保障，呵呵。</p>
<p>总之，98%的用例已经可以由我的Linux满足了，而且我的工作效率还会大大提升，确实是一个不错的选择！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/07/%e5%ae%8c%e5%85%a8%e4%bd%bf%e7%94%a8linux%e5%b7%a5%e4%bd%9c/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Berkeley DB Performance Test</title>
		<link>http://www.bdbchina.com/2009/07/berkeley-db-performance-test/</link>
		<comments>http://www.bdbchina.com/2009/07/berkeley-db-performance-test/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 03:10:51 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[bdb]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=349</guid>
		<description><![CDATA[各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。而现在确实没时间翻译过来了，还望大家理解，谢谢！
In this article I&#8217;d like to talk about the caveats and how-to&#8217;s when doing performance test with Berkeley DB, when the data volume is huge. For legal reasons I can not publish the result of my test without further approval, so I decided not to do so.
I. Context
I need to insert 10 billion key/data pairs [...]]]></description>
			<content:encoded><![CDATA[<p>各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。而现在确实没时间翻译过来了，还望大家理解，谢谢！</p>
<p>In this article I&#8217;d like to talk about the caveats and how-to&#8217;s when doing performance test with Berkeley DB, when the data volume is huge. For legal reasons I can not publish the result of my test without further approval, so I decided not to do so.</p>
<p>I. Context</p>
<p>I need to insert 10 billion key/data pairs to a btree database, each key item is 768 bytes, with no duplicate keys, and keys are inserted increasing only; each data item varies between KB/2 to 1KB. Thus each key/data pair varies between 1.25KB to 2KB.</p>
<p><span id="more-349"></span><br />
After insertion, I search for some keys, some keys are in the db, others not, to find out the average time to insert a key/data pair, and to find a key/data pair. I use a very powerful Linux 64bit machine, which has 4 processers, each processor is a 3.2GB intel Xeon, 8GB memory and 8TB of storage.</p>
<p>II. Things to Note</p>
<p>The specially huge data volumn means a lot in this test, there are many things to note:</p>
<p>1. Integer overflow.<br />
Loop variables will overflow if we insert so many key/data pairs in a loop, so split the job into pieces, making sure each piece of job won&#8217;t overflow an 32bit integer. If you used signed loop variables, they can go negative before reaching the limit value, and thus falls into an endless loop. In such a use case, we should be very careful about all integer variables being overflown.</p>
<p>Another example: If using 32bit integer as index keys, they are also overflown, causing not as many key/data pairs inserted as assumed, because later key/data pairs with keys already present in db will overwrite previous ones.</p>
<p>So we must use DB_SEQUENCE to generate 64bit sequential keys.</p>
<p>2. Random integer generator (rand() C function) loses randomness.<br />
The RAND_MAX is only 64K, and when over 32K random integers are generated, the randomness fades, and I observed some none-randomness in my test code.</p>
<p>3. Huge key/data pair.</p>
<p>Context:<br />
Each key/data pair can be as large as 2KB, and there can be millions of pages.</p>
<p>Problem:<br />
A lot of overflow pages can be generated if using default page size.<br />
Solution:<br />
Set page size to 64k, so that each internal node can hold most number of keys, thus, the internal space waste, the number of times to read more internal nodes during search and the number of overflown pages is minimized. Concurrency is not harmed since each key/data pair is so big.</p>
<p>Problem:<br />
Stack space insufficient.<br />
Solution:<br />
Do not allocate huge buffer (more than several MB) on stack, allocate it on heap, otherwise the stack may not be big enough.</p>
<p>4. Berkeley DB configuration</p>
<p>a. Do not use logs, otherwise, we will have to store another copy of the dataset, which is too much in this case as we are inserting 10 billion records each can be 3KB/4 to 2KB size.</p>
<p>b. Need a huge cache, otherwise the internal nodes won&#8217;t fit into the cache, btree search would be too slow.</p>
<p>c. When cache size is set to 6G on seneca, if using DB_PRIVATE, it is very slow, cpu usage is always less than 10%, because virtual memory (backed by disk) is used; when turned to use file mapping, cpu usage can be over 70%(later observation shows that cpu usage falls back to less than 10% after several millions of key/data pair are inserted, only when key/data pairs are inserted in order, did the insertion became fast. This is practical though, since we can always find sequentially increasing keys to use, and we can rely on secondary keys to use the various real keys.), the program is much faster.</p>
<p>d. May need to start multiple processes and insert simultaneously. And may need to split the cache into multiple files on some platforms which limit the maximum file size.</p>
<p>e. Use partitioned database, so that each database file is not so huge. If using multiple processes at the same time, each process inserting in one db file, concurrency can be promoted a lot. Though here I only used one process.</p>
<p>5. When changed to use ordered (increasing) keys, the insertion is much faster, as expected, because search becomes a very cheap operation in this case. In such a huge dataset, always use predictable sequentially increasing keys, like sequence numbers(1, 2, 3, &#8230;). For example we want to insert many Person objects, each of which has an &#8216;ID&#8217; field, though we can not get Person objects ordered by &#8216;ID&#8217;, then we should use a sequence as the key for primary db containing Person objects, and create secondary databases each of which uses one of the properties of Person. The &#8216;ID&#8217; secondary db is much smaller because the &#8216;data&#8217; item of a key/data pair in it is only a sequence number, thus much easier and faster to find the Person object by its ID, and can insert very fast at the same time.</p>
<p>6. When the search phase begins, the pages are gradually loaded into cache, but since the keys were randomly chosen, the search is not fast. A lot of internal btree pages are being swapped in/out of the cache.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/07/berkeley-db-performance-test/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>gcc4.4 issues</title>
		<link>http://www.bdbchina.com/2009/07/gcc44-issues/</link>
		<comments>http://www.bdbchina.com/2009/07/gcc44-issues/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 03:06:38 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[Berkeley DB]]></category>
		<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[bdb]]></category>
		<category><![CDATA[gcc]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=346</guid>
		<description><![CDATA[各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。 而现在确实没时间翻译过来了，还望大家理解，谢谢！
 
If your code builds well using gcc4.3 and below, it may not build with gcc4.4, which was released in April 2009. 
 
Following are some of the changes that violates c/c++ standard:
 
1. gcc4.4 does not by default #include stdio.h, or stdlib.h, no header files are by default included, all header files of [...]]]></description>
			<content:encoded><![CDATA[<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: SimSun; color: #333333;" lang="ZH-CN">各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。</span></span><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;" lang="ZH-CN"> </span></span><span class="apple-style-span"><span style="font-family: SimSun; color: #333333;" lang="ZH-CN">而现在确实没时间翻译过来了，还望大家理解，谢谢！</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">If your code builds well using gcc4.3 and below, it may not build with gcc4.4, which was released in April 2009. </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">Following are some of the changes that violates c/c++ standard:</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">1. gcc4.4 does not by default #include stdio.h, or stdlib.h, no header files are by default included, all header files of standard c/c++ libraries need to be explicitly included.</span></span></p>
<p><span id="more-346"></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">2. More warning checks, e.g. if/else partitions. </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">This code snippet: </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">if (a)</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">if (b)</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"><span> </span><span> </span>do something here;</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">else</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"><span> </span><span> </span>do something else here;</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">The above will cause warnings, instead you need this:</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">if (a) {</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">if (b)</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"><span> </span><span> </span>do something here;</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">else</span></span></p>
<p class="MsoNormal" style="text-indent: 29.25pt;"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"><span> </span><span> </span>do something else here;</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;">}</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-size: 10pt; font-family: &quot;Courier New&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">3. Stricter enum checks.</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">Basically we can not rely on the rules to evaluate enum members to integers which were true before; we can not use the integer values of enum members, we can only use the literal enum members. For example, we can not use them to compare with integral values, or members of another enum type. We can only compare with other enum members of<span> </span>the same type for equality.</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">4. Use standard ansi c++ include</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">For C library header files, #include &lt;cstdio&gt;; rather than #include &lt;stdio.h&gt;; </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">And also need to &#8220;use namespace std;&#8221; because all C functions are put into the std namespace.</span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;"> </span></span></p>
<p class="MsoNormal"><span class="apple-style-span"><span style="font-family: &quot;Lucida Grande&quot;; color: #333333;">This item is not required now, but since it is standard c++ we should follow it and abandon the old fashion in case some day later our code fails to build with latest c++ compiler.</span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/07/gcc44-issues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++ Template Corner Cases</title>
		<link>http://www.bdbchina.com/2009/07/c-template-corner-cases/</link>
		<comments>http://www.bdbchina.com/2009/07/c-template-corner-cases/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 03:04:19 +0000</pubDate>
		<dc:creator>davidzhao</dc:creator>
				<category><![CDATA[David Zhao]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://www.bdbchina.com/?p=344</guid>
		<description><![CDATA[各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。而现在确实没时间翻译过来了，还望大家理解，谢谢！
Following are some corner cases of C++ template features. A lot of the text is simply extracted from &#8220;C++ Templates: The Complete Guide&#8221;, with some of my personal understanding. These features are trivial and easily neglected, but you should have some impression to them in case you run into troubles caused by the neglect.
I made [...]]]></description>
			<content:encoded><![CDATA[<p>各位读者，很抱歉这篇文章是英文的，我当初做笔记的时候，写成英文了，这样才可以在同事之间交流。而现在确实没时间翻译过来了，还望大家理解，谢谢！</p>
<p>Following are some corner cases of C++ template features. A lot of the text is simply extracted from &#8220;C++ Templates: The Complete Guide&#8221;, with some of my personal understanding. These features are trivial and easily neglected, but you should have some impression to them in case you run into troubles caused by the neglect.</p>
<p>I made my notes in English, and I don&#8217;t bother to translate them into Chinese, forgive my laziness. <img src='http://www.bdbchina.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>0. use &#8220;typename&#8221; to extract type defined within a type parameter, like this: typename T::iterator itr; otherwise the &#8216;itr&#8217; is treated as a data member of T.</p>
<p>1. zero initialization<br />
When using a var x of type T, we MUST initialize it like this: T x = T(); so that when T is a primitive type, x can also be initialized with 0, or NULL (when T is a pointer type). Of course we must make sure<br />
when T is a class type, it has a default constructor. Simply using T x; can&#8217;t initialize x when T is a primitive type.<span id="more-344"></span></p>
<p>2. .template</p>
<p>The .template Construct<br />
A very similar problem was discovered after the introduction of typename. Consider the following example using the standard bitset type:</p>
<p>template&lt;int N&gt;<br />
void printBitset (std::bitset&lt;N&gt; const&amp; bs)<br />
{<br />
std::cout &lt;&lt; bs.template to_string&lt;char,char_traits&lt;char&gt;,<br />
allocator&lt;char&gt; &gt;();<br />
}<br />
The strange construct in this example is .template. Without that extra use of template, the compiler does not know that the less-than token (&lt;) that follows is not really &#8220;less than&#8221; but the beginning of a template argument list. Note that this is a problem only if the construct before the period depends on a template parameter. In our example, the parameter bs depends on the template parameter N.</p>
<p>3. char star string type at reference type parameter<br />
Suppose we have a string str: char str[32]; The type of &#8217;str&#8217; symbol is &#8220;a char string&#8221; and &#8220;of 32 chars long&#8221;. similarly the literal &#8220;abc&#8221; &#8217;s type is: &#8221; a char string&#8221; and &#8220;of 3 chars long&#8221;,<br />
that is, the &#8220;type&#8221; information consists of both &#8220;base type&#8221;, which is &#8220;char&#8221;, and &#8220;length&#8221;, which is 32 here. So<br />
such a string is of different type to a char* pointer, such as char*p; , a type conversion is done if passing a string literal as argument to a function with char* formal parameter.</p>
<p>template &lt;typename T&gt;<br />
inline T const&amp; max (T const&amp; a, T const&amp; b)<br />
{<br />
return a &lt; b ? b : a;<br />
}</p>
<p>So when we use max(&#8220;abc&#8221;, &#8220;def&#8221;) to call above function, it is OK. but if we use max(&#8220;abc&#8221;, &#8220;defg&#8221;) to call it, it is wrong, because &#8220;abc&#8221; and &#8220;defg&#8221; are of different types &#8212; the length is different. And automatical type conversion is not done for reference types here.</p>
<p>This means that the above array &#8217;str&#8217;, and string literals like &#8220;abc&#8221;, is not of the same type as &#8220;char *pstr;&#8221;, as most people may believe. Actually it takes a conversion to convert &#8217;str&#8217; array or the string literals to a &#8216;char*&#8217; type. However, during template argument deduction array-to-pointer conversion (often called decay) occurs only if the parameter does not have a reference type. Thus we have the above issue. And if we don&#8217;t use reference in above code, like this:<br />
template &lt;typename T&gt;<br />
inline T max (T a, T b)<br />
{<br />
return a &lt; b ? b : a;<br />
}</p>
<p>Both max(&#8220;abc&#8221;, &#8220;def&#8221;) and max(&#8220;abc&#8221;, &#8220;defg&#8221;) can build OK, since a automatic conversion from string literal to char* is done, so finally we have the same type &#8216;char*&#8217; as T.</p>
<p>4. template template types<br />
After reading the whole lot of text in the book, I realized that this is really a very particular feature, too complicated and restricted to use widely.<br />
Though, since it is a very recently added new C++ template feature, it can be used in your configure script to test whether your compiler conforms to<br />
C++ language standard. The piece of code in the book can directly be used in your m4 file for configure to use.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.bdbchina.com/2009/07/c-template-corner-cases/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
Դ
