1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
18 #include "include/types.h"
19 #include "common/Clock.h"
20 #include "msg/msg_types.h"
21 #include "include/rados/librados.hpp"
23 #include "test/librados/test_cxx.h"
24 #include "gtest/gtest.h"
26 using namespace librados
;
28 #include "cls/lock/cls_lock_client.h"
29 #include "cls/lock/cls_lock_ops.h"
31 using namespace rados::cls::lock
;
33 void lock_info(IoCtx
*ioctx
, string
& oid
, string
& name
, map
<locker_id_t
, locker_info_t
>& lockers
,
34 ClsLockType
*assert_type
, string
*assert_tag
)
36 ClsLockType lock_type
= LOCK_NONE
;
39 ASSERT_EQ(0, get_lock_info(ioctx
, oid
, name
, &lockers
, &lock_type
, &tag
));
40 cout
<< "lock: " << name
<< std::endl
;
41 cout
<< " lock_type: " << cls_lock_type_str(lock_type
) << std::endl
;
42 cout
<< " tag: " << tag
<< std::endl
;
43 cout
<< " lockers:" << std::endl
;
46 ASSERT_EQ(*assert_type
, lock_type
);
50 ASSERT_EQ(*assert_tag
, tag
);
53 map
<locker_id_t
, locker_info_t
>::iterator liter
;
54 for (liter
= lockers
.begin(); liter
!= lockers
.end(); ++liter
) {
55 const locker_id_t
& locker
= liter
->first
;
56 cout
<< " " << locker
.locker
<< " expiration=" << liter
->second
.expiration
57 << " addr=" << liter
->second
.addr
<< " cookie=" << locker
.cookie
<< std::endl
;
61 void lock_info(IoCtx
*ioctx
, string
& oid
, string
& name
, map
<locker_id_t
, locker_info_t
>& lockers
)
63 lock_info(ioctx
, oid
, name
, lockers
, NULL
, NULL
);
66 TEST(ClsLock
, TestMultiLocking
) {
68 std::string pool_name
= get_temp_pool_name();
69 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
71 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
72 ClsLockType lock_type_shared
= LOCK_SHARED
;
73 ClsLockType lock_type_exclusive
= LOCK_EXCLUSIVE
;
78 ASSERT_EQ("", connect_cluster_pp(cluster2
));
79 cluster2
.ioctx_create(pool_name
.c_str(), ioctx2
);
83 string lock_name
= "mylock";
85 ASSERT_EQ(0, ioctx
.write(oid
, bl
, bl
.length(), 0));
88 // we set the duration, so the log output contains a locker with a
89 // non-zero expiration time
90 l
.set_duration(utime_t(120, 0));
92 /* test lock object */
94 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
96 /* test exclusive lock */
97 ASSERT_EQ(-EEXIST
, l
.lock_exclusive(&ioctx
, oid
));
99 /* test idempotency */
100 l
.set_may_renew(true);
101 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
103 l
.set_may_renew(false);
105 /* test second client */
107 ASSERT_EQ(-EBUSY
, l2
.lock_exclusive(&ioctx2
, oid
));
108 ASSERT_EQ(-EBUSY
, l2
.lock_shared(&ioctx2
, oid
));
111 ASSERT_EQ(0, list_locks(&ioctx
, oid
, &locks
));
113 ASSERT_EQ(1, (int)locks
.size());
114 list
<string
>::iterator iter
= locks
.begin();
115 map
<locker_id_t
, locker_info_t
> lockers
;
116 lock_info(&ioctx
, oid
, *iter
, lockers
, &lock_type_exclusive
, NULL
);
118 ASSERT_EQ(1, (int)lockers
.size());
121 ASSERT_EQ(0, l
.unlock(&ioctx
, oid
));
123 ASSERT_EQ(0, list_locks(&ioctx
, oid
, &locks
));
125 /* test shared lock */
126 ASSERT_EQ(0, l2
.lock_shared(&ioctx2
, oid
));
127 ASSERT_EQ(0, l
.lock_shared(&ioctx
, oid
));
130 ASSERT_EQ(0, list_locks(&ioctx
, oid
, &locks
));
131 ASSERT_EQ(1, (int)locks
.size());
132 iter
= locks
.begin();
133 lock_info(&ioctx
, oid
, *iter
, lockers
, &lock_type_shared
, NULL
);
134 ASSERT_EQ(2, (int)lockers
.size());
136 /* test break locks */
137 entity_name_t name
= entity_name_t::CLIENT(cluster
.get_instance_id());
138 entity_name_t name2
= entity_name_t::CLIENT(cluster2
.get_instance_id());
140 l2
.break_lock(&ioctx2
, oid
, name
);
141 lock_info(&ioctx
, oid
, *iter
, lockers
);
142 ASSERT_EQ(1, (int)lockers
.size());
143 map
<locker_id_t
, locker_info_t
>::iterator liter
= lockers
.begin();
144 const locker_id_t
& id
= liter
->first
;
145 ASSERT_EQ(name2
, id
.locker
);
148 Lock
l_tag(lock_name
);
149 l_tag
.set_tag("non-default tag");
150 ASSERT_EQ(-EBUSY
, l_tag
.lock_shared(&ioctx
, oid
));
153 /* test modify description */
154 string description
= "new description";
155 l
.set_description(description
);
156 ASSERT_EQ(0, l
.lock_shared(&ioctx
, oid
));
158 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
161 TEST(ClsLock
, TestMeta
) {
163 std::string pool_name
= get_temp_pool_name();
164 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
166 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
171 ASSERT_EQ("", connect_cluster_pp(cluster2
));
172 cluster2
.ioctx_create(pool_name
.c_str(), ioctx2
);
176 string lock_name
= "mylock";
178 ASSERT_EQ(0, ioctx
.write(oid
, bl
, bl
.length(), 0));
181 ASSERT_EQ(0, l
.lock_shared(&ioctx
, oid
));
184 Lock
l_tag(lock_name
);
185 l_tag
.set_tag("non-default tag");
186 ASSERT_EQ(-EBUSY
, l_tag
.lock_shared(&ioctx2
, oid
));
189 ASSERT_EQ(0, l
.unlock(&ioctx
, oid
));
191 /* test description */
193 string description
= "new description";
194 l2
.set_description(description
);
195 ASSERT_EQ(0, l2
.lock_shared(&ioctx2
, oid
));
197 map
<locker_id_t
, locker_info_t
> lockers
;
198 lock_info(&ioctx
, oid
, lock_name
, lockers
, NULL
, NULL
);
199 ASSERT_EQ(1, (int)lockers
.size());
201 map
<locker_id_t
, locker_info_t
>::iterator iter
= lockers
.begin();
202 locker_info_t locker
= iter
->second
;
203 ASSERT_EQ("new description", locker
.description
);
205 ASSERT_EQ(0, l2
.unlock(&ioctx2
, oid
));
208 string new_tag
= "new_tag";
210 l
.set_may_renew(true);
211 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
212 lock_info(&ioctx
, oid
, lock_name
, lockers
, NULL
, &new_tag
);
213 ASSERT_EQ(1, (int)lockers
.size());
215 ASSERT_EQ(-EBUSY
, l
.lock_exclusive(&ioctx
, oid
));
217 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
219 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
222 TEST(ClsLock
, TestCookie
) {
224 std::string pool_name
= get_temp_pool_name();
225 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
227 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
230 string lock_name
= "mylock";
233 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
236 string cookie
= "new cookie";
237 l
.set_cookie(cookie
);
238 ASSERT_EQ(-EBUSY
, l
.lock_exclusive(&ioctx
, oid
));
239 ASSERT_EQ(-ENOENT
, l
.unlock(&ioctx
, oid
));
241 ASSERT_EQ(0, l
.unlock(&ioctx
, oid
));
243 map
<locker_id_t
, locker_info_t
> lockers
;
244 lock_info(&ioctx
, oid
, lock_name
, lockers
);
245 ASSERT_EQ(0, (int)lockers
.size());
247 l
.set_cookie(cookie
);
248 ASSERT_EQ(0, l
.lock_shared(&ioctx
, oid
));
250 ASSERT_EQ(0, l
.lock_shared(&ioctx
, oid
));
252 lock_info(&ioctx
, oid
, lock_name
, lockers
);
253 ASSERT_EQ(2, (int)lockers
.size());
255 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
258 TEST(ClsLock
, TestMultipleLocks
) {
260 std::string pool_name
= get_temp_pool_name();
261 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
263 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
267 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
270 ASSERT_EQ(0, l2
.lock_exclusive(&ioctx
, oid
));
273 ASSERT_EQ(0, list_locks(&ioctx
, oid
, &locks
));
275 ASSERT_EQ(2, (int)locks
.size());
277 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
280 TEST(ClsLock
, TestLockDuration
) {
282 std::string pool_name
= get_temp_pool_name();
283 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
285 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
291 utime_t start
= ceph_clock_now();
292 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
293 int r
= l
.lock_exclusive(&ioctx
, oid
);
295 // it's possible to get success if we were just really slow...
296 ASSERT_TRUE(ceph_clock_now() > start
+ dur
);
298 ASSERT_EQ(-EEXIST
, r
);
302 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
304 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
307 TEST(ClsLock
, TestAssertLocked
) {
309 std::string pool_name
= get_temp_pool_name();
310 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
312 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
316 ASSERT_EQ(0, l
.lock_exclusive(&ioctx
, oid
));
318 librados::ObjectWriteOperation op1
;
319 l
.assert_locked_exclusive(&op1
);
320 ASSERT_EQ(0, ioctx
.operate(oid
, &op1
));
322 librados::ObjectWriteOperation op2
;
323 l
.assert_locked_shared(&op2
);
324 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op2
));
327 librados::ObjectWriteOperation op3
;
328 l
.assert_locked_exclusive(&op3
);
329 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op3
));
332 l
.set_cookie("cookie");
333 librados::ObjectWriteOperation op4
;
334 l
.assert_locked_exclusive(&op4
);
335 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op4
));
338 ASSERT_EQ(0, l
.unlock(&ioctx
, oid
));
340 librados::ObjectWriteOperation op5
;
341 l
.assert_locked_exclusive(&op5
);
342 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op5
));
344 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
347 TEST(ClsLock
, TestSetCookie
) {
349 std::string pool_name
= get_temp_pool_name();
350 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
352 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
355 string name
= "name";
357 string cookie
= "cookie";
358 string new_cookie
= "new cookie";
359 librados::ObjectWriteOperation op1
;
360 set_cookie(&op1
, name
, LOCK_SHARED
, cookie
, tag
, new_cookie
);
361 ASSERT_EQ(-ENOENT
, ioctx
.operate(oid
, &op1
));
363 librados::ObjectWriteOperation op2
;
364 lock(&op2
, name
, LOCK_SHARED
, cookie
, tag
, "", utime_t
{}, 0);
365 ASSERT_EQ(0, ioctx
.operate(oid
, &op2
));
367 librados::ObjectWriteOperation op3
;
368 lock(&op3
, name
, LOCK_SHARED
, "cookie 2", tag
, "", utime_t
{}, 0);
369 ASSERT_EQ(0, ioctx
.operate(oid
, &op3
));
371 librados::ObjectWriteOperation op4
;
372 set_cookie(&op4
, name
, LOCK_SHARED
, cookie
, tag
, cookie
);
373 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op4
));
375 librados::ObjectWriteOperation op5
;
376 set_cookie(&op5
, name
, LOCK_SHARED
, cookie
, "wrong tag", new_cookie
);
377 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op5
));
379 librados::ObjectWriteOperation op6
;
380 set_cookie(&op6
, name
, LOCK_SHARED
, "wrong cookie", tag
, new_cookie
);
381 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op6
));
383 librados::ObjectWriteOperation op7
;
384 set_cookie(&op7
, name
, LOCK_EXCLUSIVE
, cookie
, tag
, new_cookie
);
385 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op7
));
387 librados::ObjectWriteOperation op8
;
388 set_cookie(&op8
, name
, LOCK_SHARED
, cookie
, tag
, "cookie 2");
389 ASSERT_EQ(-EBUSY
, ioctx
.operate(oid
, &op8
));
391 librados::ObjectWriteOperation op9
;
392 set_cookie(&op9
, name
, LOCK_SHARED
, cookie
, tag
, new_cookie
);
393 ASSERT_EQ(0, ioctx
.operate(oid
, &op9
));
395 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
398 TEST(ClsLock
, TestRenew
) {
400 std::string pool_name
= get_temp_pool_name();
401 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
403 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
407 string oid1
= "foo1";
408 string lock_name1
= "mylock1";
410 ASSERT_EQ(0, ioctx
.write(oid1
, bl
, bl
.length(), 0));
413 utime_t
lock_duration1(5, 0);
414 l1
.set_duration(lock_duration1
);
416 ASSERT_EQ(0, l1
.lock_exclusive(&ioctx
, oid1
));
417 l1
.set_may_renew(true);
419 ASSERT_EQ(0, l1
.lock_exclusive(&ioctx
, oid1
));
421 ASSERT_EQ(0, l1
.lock_exclusive(&ioctx
, oid1
)) <<
422 "when a cls_lock is set to may_renew, a relock after expiration "
424 ASSERT_EQ(0, l1
.unlock(&ioctx
, oid1
));
426 // ***********************************************
428 string oid2
= "foo2";
429 string lock_name2
= "mylock2";
431 ASSERT_EQ(0, ioctx
.write(oid2
, bl
, bl
.length(), 0));
434 utime_t
lock_duration2(5, 0);
435 l2
.set_duration(lock_duration2
);
437 ASSERT_EQ(0, l2
.lock_exclusive(&ioctx
, oid2
));
438 l2
.set_must_renew(true);
440 ASSERT_EQ(0, l2
.lock_exclusive(&ioctx
, oid2
));
442 ASSERT_EQ(-ENOENT
, l2
.lock_exclusive(&ioctx
, oid2
)) <<
443 "when a cls_lock is set to must_renew, a relock after expiration "
445 ASSERT_EQ(-ENOENT
, l2
.unlock(&ioctx
, oid2
));
447 // ***********************************************
449 string oid3
= "foo3";
450 string lock_name3
= "mylock3";
452 ASSERT_EQ(0, ioctx
.write(oid3
, bl
, bl
.length(), 0));
455 l3
.set_duration(utime_t(5, 0));
456 l3
.set_must_renew(true);
458 ASSERT_EQ(-ENOENT
, l3
.lock_exclusive(&ioctx
, oid3
)) <<
459 "unable to create a lock with must_renew";
461 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
464 TEST(ClsLock
, TestExclusiveEphemeralBasic
) {
466 std::string pool_name
= get_temp_pool_name();
467 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
469 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
473 string oid1
= "foo1";
474 string oid2
= "foo2";
475 string lock_name1
= "mylock1";
476 string lock_name2
= "mylock2";
479 l1
.set_duration(utime_t(5, 0));
484 l1
.set_may_renew(true);
485 ASSERT_EQ(0, l1
.lock_exclusive_ephemeral(&ioctx
, oid1
));
486 ASSERT_EQ(0, ioctx
.stat(oid1
, &size
, &mod_time
));
488 ASSERT_EQ(0, l1
.unlock(&ioctx
, oid1
));
489 ASSERT_EQ(-ENOENT
, ioctx
.stat(oid1
, &size
, &mod_time
));
491 // ***********************************************
494 utime_t
lock_duration2(5, 0);
495 l2
.set_duration(utime_t(5, 0));
497 ASSERT_EQ(0, l2
.lock_exclusive(&ioctx
, oid2
));
498 ASSERT_EQ(0, ioctx
.stat(oid2
, &size
, &mod_time
));
500 ASSERT_EQ(0, l2
.unlock(&ioctx
, oid2
));
501 ASSERT_EQ(0, ioctx
.stat(oid2
, &size
, &mod_time
));
503 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
507 TEST(ClsLock
, TestExclusiveEphemeralStealEphemeral
) {
509 std::string pool_name
= get_temp_pool_name();
510 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
512 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
516 string oid1
= "foo1";
517 string lock_name1
= "mylock1";
520 l1
.set_duration(utime_t(3, 0));
522 ASSERT_EQ(0, l1
.lock_exclusive_ephemeral(&ioctx
, oid1
));
525 // l1 is expired, l2 can take; l2 is also exclusive_ephemeral
527 l2
.set_duration(utime_t(3, 0));
528 ASSERT_EQ(0, l2
.lock_exclusive_ephemeral(&ioctx
, oid1
));
530 ASSERT_EQ(0, l2
.unlock(&ioctx
, oid1
));
532 // l2 cannot unlock its expired lock
533 ASSERT_EQ(-ENOENT
, l1
.unlock(&ioctx
, oid1
));
535 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));
539 TEST(ClsLock
, TestExclusiveEphemeralStealExclusive
) {
541 std::string pool_name
= get_temp_pool_name();
542 ASSERT_EQ("", create_one_pool_pp(pool_name
, cluster
));
544 cluster
.ioctx_create(pool_name
.c_str(), ioctx
);
548 string oid1
= "foo1";
549 string lock_name1
= "mylock1";
552 l1
.set_duration(utime_t(3, 0));
554 ASSERT_EQ(0, l1
.lock_exclusive_ephemeral(&ioctx
, oid1
));
557 // l1 is expired, l2 can take; l2 is exclusive (but not ephemeral)
559 l2
.set_duration(utime_t(3, 0));
560 ASSERT_EQ(0, l2
.lock_exclusive(&ioctx
, oid1
));
562 ASSERT_EQ(0, l2
.unlock(&ioctx
, oid1
));
564 // l2 cannot unlock its expired lock
565 ASSERT_EQ(-ENOENT
, l1
.unlock(&ioctx
, oid1
));
567 ASSERT_EQ(0, destroy_one_pool_pp(pool_name
, cluster
));