如何在 tdb 中使用多个编写器?

How do I use multiple writers with tdb?

我在 Linux 上使用 tdb 尝试熟悉 C 中的数据库管理。每 tdb's description

tdb is a Trivial database.

In concept, it is very much like GDBM, and BSD's DB except that it allows multiple simultaneous writers and uses locking internally to keep writers from trampling on each other. tdb is also extremely small. Interface

The interface is very similar to gdbm except for the following:

  • different open interface. The tdb_open call is more similar to a traditional open()
  • no tdbm_reorganise() function
  • no tdbm_sync() function. No operations are cached in the library anyway
  • added transactions support

A general rule for using tdb is that the caller frees any returned TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA return value called p. This is the same as gdbm.

现在我想制作一个小测试程序来测试对我的数据库的多个写连接。但是失败了。

#include <tdb.h>

int main(int argc, char** argv) {
   struct tdb_context * tdb = tdb_open("test.tdb", 0, 0,
           O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
   if(!tdb){
       printf("%s\n", tdb_errorstr(tdb));
   } else {
       printf("Database successfully opened!\n");
   }
   
   struct tdb_context * anothertdb = tdb_open("test.tdb", 0, 0,
           O_RDWR| O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);  //why does this fail?
   if(!anothertdb){
       printf("%s\n", tdb_errorstr(anothertdb));
   } else {
       printf("Another reader successfully opened!\n");
   }
   
   if(tdb_close(anothertdb)){
       printf("Error while closing database!\n");
   } else {
       printf("closing anothertdb-connection\n");
   }
   
   if(tdb_close(tdb)){
       printf("Error while closing database!\n");
   } else {
       printf("closing tdb-connection\n");
   }
return 0;
}

输出为:

Database successfully opened!

RUN FINISHED; Segmentation fault; core dumped; real time: 240ms; user: 0ms; system: 20ms

如果我只打开与数据库的单个 连接,则没有问题。如何在数据库上打开多个阅读器?

您遇到的第一个问题是在 tdb_open 失败后对 tdb_errorstring 的调用:

if(!anothertdb){
       printf("%s\n", tdb_errorstr(anothertdb));
}

这行不通可能并不明显,但有充分的理由怀疑它行不通,因为 anothertdb 肯定是 NULLtdb_errorstr 是调用,并且可以合理地假设 tdb_errorstr 的参数必须是指向 struct tdb_context 的有效指针。事实上,这是真的;实际上 tdb_errorstring 做的第一件事就是尝试取消引用它的参数以提取最后的错误代码。

简而言之,无法从 tdb_open 失败的结果中提取自定义错误字符串;您所能做的就是对来自 fopen()NULL return 做的事情:调用 perror() 希望 errno 将被设置到有意义的事情。如果这样做,您将看到错误消息而不是崩溃:

$ ./tdb_test 
Database successfully opened!
open 2: Device or resource busy

库似乎有办法将 tdb_open 错误发送到配置的日志系统,但默认配置的日志系统只是抛出错误调用而不做任何事情。我没有阅读文档以查看是否有关于如何配置记录器的描述;你可能想调查一下。

顺便说一句,我不是在捍卫这个 API 设计。只是报告它。

好的,现在开始您的实际问题:如何打开到 TDB 数据库的多个连接?答案是,你必须从多个进程中做到这一点。一个单独的进程可以打开任意多个 TDB,但它只能(一次)打开每个 TDB 一次。根据源代码中的评论,这是 fcntl() 锁工作方式的结果,这对我来说似乎是一个合理的解释。

因此,如果您想尝试 multiple-reader、multiple-writer 场景,您将不得不 运行 多个进程,或者 fork 多个 children来自 parent 进程。线程将不起作用,因为同一进程中的两个线程也无法打开同一个 TDB。每个进程打开一个

此外,如果您选择分叉多个 children,请记住您只需要在分叉后调用 tdb_open,因为如果您在 parent 中打开 TDB过程中,它仍将在 child.

中打开

最后一点,请养成将警告和错误消息写入 stderr 的习惯,这些消息通常不会被缓冲,这样当您执行以下操作时,错误消息更有可能变得可见输出它,而不是稍后当 stdio 库选择刷新 stdout 输出缓冲区时。这实际上不是问题,但它可能是,你最好使用 stderr 来达到这个目的(这是它的预期目的,因此得名)。