Skip to content

Commit 7a62dc5

Browse files
committed
{180887345} Fixing berkdb DB handle leak
Signed-off-by: Rivers Zhang <[email protected]>
1 parent ce5d0cd commit 7a62dc5

File tree

6 files changed

+114
-1
lines changed

6 files changed

+114
-1
lines changed

bdb/file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6612,7 +6612,7 @@ static int bdb_del_file(bdb_state_type *bdb_state, DB_TXN *tid, char *filename,
66126612
int rc;
66136613

66146614
if ((rc = db_create(&dbp, dbenv, 0)) == 0 &&
6615-
(rc = dbp->open(dbp, NULL, pname, NULL, DB_BTREE, 0, 0666)) == 0) {
6615+
(rc = dbp->open(dbp, NULL, pname, NULL, DB_BTREE, DB_CLR_UFID, 0666)) == 0) {
66166616
bdb_remove_fileid_pglogs(bdb_state, dbp->fileid);
66176617
dbp->close(dbp, DB_NOSYNC);
66186618
}

berkdb/build/db.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ struct txn_properties;
208208
#define DB_USE_ENVIRON 0x0000400 /* Use the environment. */
209209
#define DB_USE_ENVIRON_ROOT 0x0000800 /* Use the environment if root. */
210210
#define DB_RECOVERY_CKP 0x0001000 /* Do recovery checkpoint */
211+
#define DB_CLR_UFID 0x0002000 /* Clear open handle in ufid-hash */
211212

212213
/*
213214
* Common flags --

berkdb/db/db_open.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ __db_open(dbp, txn, fname, dname, type, flags, mode, meta_pgno)
7777
int ret;
7878
u_int32_t id;
7979

80+
DB_LSN dummy_lsn;
81+
ZERO_LSN(dummy_lsn);
82+
DB *ufid_dbp = NULL;
83+
int ufid_find_rc = 0;
84+
8085
dbenv = dbp->dbenv;
8186
id = TXN_INVALID;
8287

@@ -160,6 +165,19 @@ __db_open(dbp, txn, fname, dname, type, flags, mode, meta_pgno)
160165
meta_pgno = dbp->meta_pgno;
161166
}
162167

168+
if (LF_ISSET(DB_CLR_UFID)) {
169+
ufid_find_rc = __ufid_find_db(dbenv, txn, &ufid_dbp, dbp->fileid, &dummy_lsn);
170+
if (ufid_find_rc == 0 && ufid_dbp != NULL && F_ISSET(ufid_dbp, DB_AM_RECOVER)) {
171+
logmsg(LOGMSG_WARN, "%s: closing ufid hash open DB handle to %s\n", __func__, fname);
172+
ret = __db_close(ufid_dbp, txn, flags);
173+
if (ret != 0) {
174+
__db_err(dbenv, "__db_close(%s)", dbp->fname);
175+
}
176+
}
177+
/* don't fail */
178+
ret = 0;
179+
}
180+
163181
/*
164182
* If we created the file, set the truncate flag for the mpool. This
165183
* isn't for anything we've done, it's protection against stupid user
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ifeq ($(TESTSROOTDIR),)
2+
include ../testcase.mk
3+
else
4+
include $(TESTSROOTDIR)/testcase.mk
5+
endif
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
setattr CHECKPOINTTIME 9999999
2+
setattr MEMPTRICKLEPERCENT 10
3+
ufid_add_on_collect 1
4+
rep_skip_recovery on
5+
dtastripe 1

tests/berkdb_file_leaks.test/runit

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
bash -n "$0" | exit 1
3+
4+
# This test demonstrates a case where ufid_hash would hold an open
5+
# DB handle to an outdated btree file, after schema change, hence prevent
6+
# the file from being deleted.
7+
#
8+
# 1. send flush
9+
# 2. write to table
10+
# 3. bounce a replicant
11+
# 3.1. replicant will start matching from the LSN in step 1
12+
# 3.2. replicant will run recovery to apply transaction from step 2
13+
# 3.3. ufid-hash will need to open a DB handle on the btree file
14+
# 4. truncate table, this creates new btree file
15+
# 5. delfiles now will create "deleted" file on this replicant;
16+
# alternatively, if we upgrade master to this replicant and then run delfiles,
17+
# we'll get "DB_FILEOPEN: Rename or remove while file is open"
18+
19+
source ${TESTSROOTDIR}/tools/cluster_utils.sh
20+
21+
[ -z "${CLUSTER}" ] && { echo "Test requires a cluster"; exit 0; }
22+
23+
dbnm=$1
24+
25+
replicant=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default "select comdb2_host()"`
26+
master=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default "select host from comdb2_cluster where is_master='Y'"`
27+
28+
cdb2sql ${CDB2_OPTIONS} $dbnm default "CREATE TABLE t1 (i integer, b blob)"
29+
sleep 2 # wait for the checkpoint thread to do its 1st checkpoint
30+
cdb2sql $dbnm --host $master "EXEC PROCEDURE sys.cmd.send('bdb checkpoint')"
31+
sleep 2 # wait for the async checkpoint to finish
32+
33+
cdb2sql ${CDB2_OPTIONS} $dbnm default "TRUNCATE TABLE t1"
34+
cdb2sql $dbnm --host $master "EXEC PROCEDURE sys.cmd.send('bdb checkpoint')"
35+
sleep 2 # wait for the async checkpoint to finish
36+
37+
cdb2sql ${CDB2_OPTIONS} $dbnm default "INSERT INTO t1 VALUES(1, x'CDB2BABE')"
38+
sleep 2
39+
40+
echo "Restarting node $replicant ..."
41+
kill_restart_node $replicant 10 1
42+
cdb2sql $dbnm --host $replicant "SELECT 1"
43+
if [ $? -ne 0 ]; then
44+
echo 'db not up?' >&2
45+
exit 1
46+
fi
47+
48+
echo 'Before truncate ...'
49+
cdb2sql $dbnm --host $replicant "EXEC PROCEDURE sys.cmd.send('bdb cachestatall')" | grep -A1 blobs0
50+
cdb2sql $dbnm --host $master "TRUNCATE TABLE t1"
51+
echo 'After truncate ...'
52+
cdb2sql $dbnm --host $replicant "EXEC PROCEDURE sys.cmd.send('bdb cachestatall')" | grep -A1 blobs0
53+
54+
echo "Waiting for $replicant to become new master"
55+
while true; do
56+
is_replicant_new_master=`cdb2sql --tabs $dbnm --host $replicant "SELECT comdb2_host() = (select host from comdb2_cluster where is_master='Y')"`
57+
if [ "$is_replicant_new_master" = "1" ]; then
58+
master=$replicant
59+
break
60+
fi
61+
62+
master=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default "select host from comdb2_cluster where is_master='Y'"`
63+
cdb2sql $dbnm --host $master "EXEC PROCEDURE sys.cmd.send('upgrade $replicant')"
64+
sleep 5
65+
master=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default "select host from comdb2_cluster where is_master='Y'"`
66+
echo "Master swung to $master"
67+
68+
while true; do
69+
cdb2sql $dbnm --host $replicant "SELECT 1" >/dev/null
70+
if [ $? -eq 0 ]; then
71+
break;
72+
fi
73+
sleep 1
74+
done
75+
done
76+
77+
cdb2sql -tabs $dbnm --host $master "EXEC PROCEDURE sys.cmd.send('delfiles t1')" | grep 'DB_FILEOPEN: Rename or remove while file is open'
78+
if [ $? -eq 0 ]; then
79+
echo "DB handles leaked" >&2
80+
exit 1
81+
fi
82+
83+
echo "Success!"
84+
exit 0

0 commit comments

Comments
 (0)