1 #include "include/rados/librados.h"
2 #include "include/rados/rados_types.h"
3 #include "test/librados/test.h"
4 #include "test/librados/TestCase.h"
9 #include "gtest/gtest.h"
10 #include "include/encoding.h"
14 typedef RadosTestEC LibRadosWatchNotifyEC
;
21 static void watch_notify_test_cb(uint8_t opcode
, uint64_t ver
, void *arg
)
23 std::cout
<< __func__
<< std::endl
;
27 class LibRadosWatchNotify
: public RadosTest
32 std::set
<uint64_t> notify_cookies
;
33 rados_ioctx_t notify_io
;
34 const char *notify_oid
= nullptr;
37 static void watch_notify2_test_cb(void *arg
,
40 uint64_t notifier_gid
,
43 static void watch_notify2_test_errcb(void *arg
, uint64_t cookie
, int err
);
47 void LibRadosWatchNotify::watch_notify2_test_cb(void *arg
,
50 uint64_t notifier_gid
,
54 std::cout
<< __func__
<< " from " << notifier_gid
<< " notify_id " << notify_id
55 << " cookie " << cookie
<< std::endl
;
56 ceph_assert(notifier_gid
> 0);
57 auto thiz
= reinterpret_cast<LibRadosWatchNotify
*>(arg
);
59 thiz
->notify_cookies
.insert(cookie
);
60 thiz
->notify_bl
.clear();
61 thiz
->notify_bl
.append((char*)data
, data_len
);
64 rados_notify_ack(thiz
->notify_io
, thiz
->notify_oid
, notify_id
, cookie
,
68 void LibRadosWatchNotify::watch_notify2_test_errcb(void *arg
,
72 std::cout
<< __func__
<< " cookie " << cookie
<< " err " << err
<< std::endl
;
73 ceph_assert(cookie
> 1000);
74 auto thiz
= reinterpret_cast<LibRadosWatchNotify
*>(arg
);
76 thiz
->notify_err
= err
;
79 class WatchNotifyTestCtx2
;
83 #pragma GCC diagnostic ignored "-Wpragmas"
84 #pragma GCC diagnostic push
85 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
87 TEST_F(LibRadosWatchNotify
, WatchNotify
) {
88 ASSERT_NE(SEM_FAILED
, (sem
= sem_open("/test_watch_notify_sem", O_CREAT
, 0644, 0)));
90 memset(buf
, 0xcc, sizeof(buf
));
91 ASSERT_EQ(0, rados_write(ioctx
, "foo", buf
, sizeof(buf
), 0));
94 rados_watch(ioctx
, "foo", 0, &handle
, watch_notify_test_cb
, NULL
));
95 ASSERT_EQ(0, rados_notify(ioctx
, "foo", 0, NULL
, 0));
98 rados_unwatch(ioctx
, "foo", handle
);
102 rados_watch(ioctx
, "dne", 0, &handle
, watch_notify_test_cb
, NULL
));
107 TEST_F(LibRadosWatchNotifyEC
, WatchNotify
) {
108 ASSERT_NE(SEM_FAILED
, (sem
= sem_open("/test_watch_notify_sem", O_CREAT
, 0644, 0)));
110 memset(buf
, 0xcc, sizeof(buf
));
111 ASSERT_EQ(0, rados_write(ioctx
, "foo", buf
, sizeof(buf
), 0));
114 rados_watch(ioctx
, "foo", 0, &handle
, watch_notify_test_cb
, NULL
));
115 ASSERT_EQ(0, rados_notify(ioctx
, "foo", 0, NULL
, 0));
118 rados_unwatch(ioctx
, "foo", handle
);
122 #pragma GCC diagnostic pop
123 #pragma GCC diagnostic warning "-Wpragmas"
128 TEST_F(LibRadosWatchNotify
, Watch2Delete
) {
133 memset(buf
, 0xcc, sizeof(buf
));
134 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
137 rados_watch2(ioctx
, notify_oid
, &handle
,
138 watch_notify2_test_cb
,
139 watch_notify2_test_errcb
, this));
140 ASSERT_EQ(0, rados_remove(ioctx
, notify_oid
));
142 std::cout
<< "waiting up to " << left
<< " for disconnect notification ..."
144 while (notify_err
== 0 && --left
) {
147 ASSERT_TRUE(left
> 0);
148 ASSERT_EQ(-ENOTCONN
, notify_err
);
149 ASSERT_EQ(-ENOTCONN
, rados_watch_check(ioctx
, handle
));
150 rados_unwatch2(ioctx
, handle
);
151 rados_watch_flush(cluster
);
154 TEST_F(LibRadosWatchNotify
, AioWatchDelete
) {
159 memset(buf
, 0xcc, sizeof(buf
));
160 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
163 rados_completion_t comp
;
165 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
166 rados_aio_watch(ioctx
, notify_oid
, comp
, &handle
,
167 watch_notify2_test_cb
, watch_notify2_test_errcb
, this);
168 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
169 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
170 rados_aio_release(comp
);
171 ASSERT_EQ(0, rados_remove(ioctx
, notify_oid
));
173 std::cout
<< "waiting up to " << left
<< " for disconnect notification ..."
175 while (notify_err
== 0 && --left
) {
178 ASSERT_TRUE(left
> 0);
179 ASSERT_EQ(-ENOTCONN
, notify_err
);
180 ASSERT_EQ(-ENOTCONN
, rados_watch_check(ioctx
, handle
));
181 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
182 rados_aio_unwatch(ioctx
, handle
, comp
);
183 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
184 ASSERT_EQ(-ENOENT
, rados_aio_get_return_value(comp
));
185 rados_aio_release(comp
);
190 TEST_F(LibRadosWatchNotify
, WatchNotify2
) {
193 notify_cookies
.clear();
195 memset(buf
, 0xcc, sizeof(buf
));
196 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
199 rados_watch2(ioctx
, notify_oid
, &handle
,
200 watch_notify2_test_cb
,
201 watch_notify2_test_errcb
, this));
202 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
204 size_t reply_buf_len
;
205 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
207 &reply_buf
, &reply_buf_len
));
209 reply
.append(reply_buf
, reply_buf_len
);
210 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
211 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
212 auto reply_p
= reply
.cbegin();
213 decode(reply_map
, reply_p
);
214 decode(missed_map
, reply_p
);
215 ASSERT_EQ(1u, reply_map
.size());
216 ASSERT_EQ(0u, missed_map
.size());
217 ASSERT_EQ(1u, notify_cookies
.size());
218 ASSERT_EQ(1u, notify_cookies
.count(handle
));
219 ASSERT_EQ(5u, reply_map
.begin()->second
.length());
220 ASSERT_EQ(0, strncmp("reply", reply_map
.begin()->second
.c_str(), 5));
221 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
222 rados_buffer_free(reply_buf
);
224 // try it on a non-existent object ... our buffer pointers
225 // should get zeroed.
226 ASSERT_EQ(-ENOENT
, rados_notify2(ioctx
, "doesnotexist",
228 &reply_buf
, &reply_buf_len
));
229 ASSERT_EQ((char*)0, reply_buf
);
230 ASSERT_EQ(0u, reply_buf_len
);
232 rados_unwatch2(ioctx
, handle
);
233 rados_watch_flush(cluster
);
236 TEST_F(LibRadosWatchNotify
, AioWatchNotify2
) {
239 notify_cookies
.clear();
241 memset(buf
, 0xcc, sizeof(buf
));
242 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
244 rados_completion_t comp
;
246 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
247 rados_aio_watch(ioctx
, notify_oid
, comp
, &handle
,
248 watch_notify2_test_cb
, watch_notify2_test_errcb
, this);
249 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
250 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
251 rados_aio_release(comp
);
253 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
255 size_t reply_buf_len
;
256 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
258 &reply_buf
, &reply_buf_len
));
260 reply
.append(reply_buf
, reply_buf_len
);
261 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
262 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
263 auto reply_p
= reply
.cbegin();
264 decode(reply_map
, reply_p
);
265 decode(missed_map
, reply_p
);
266 ASSERT_EQ(1u, reply_map
.size());
267 ASSERT_EQ(0u, missed_map
.size());
268 ASSERT_EQ(1u, notify_cookies
.size());
269 ASSERT_EQ(1u, notify_cookies
.count(handle
));
270 ASSERT_EQ(5u, reply_map
.begin()->second
.length());
271 ASSERT_EQ(0, strncmp("reply", reply_map
.begin()->second
.c_str(), 5));
272 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
273 rados_buffer_free(reply_buf
);
275 // try it on a non-existent object ... our buffer pointers
276 // should get zeroed.
277 ASSERT_EQ(-ENOENT
, rados_notify2(ioctx
, "doesnotexist",
279 &reply_buf
, &reply_buf_len
));
280 ASSERT_EQ((char*)0, reply_buf
);
281 ASSERT_EQ(0u, reply_buf_len
);
283 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
284 rados_aio_unwatch(ioctx
, handle
, comp
);
285 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
286 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
287 rados_aio_release(comp
);
290 TEST_F(LibRadosWatchNotify
, AioNotify
) {
293 notify_cookies
.clear();
295 memset(buf
, 0xcc, sizeof(buf
));
296 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
299 rados_watch2(ioctx
, notify_oid
, &handle
,
300 watch_notify2_test_cb
,
301 watch_notify2_test_errcb
, this));
302 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
304 size_t reply_buf_len
;
305 rados_completion_t comp
;
306 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
307 ASSERT_EQ(0, rados_aio_notify(ioctx
, "foo", comp
, "notify", 6, 300000,
308 &reply_buf
, &reply_buf_len
));
309 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
310 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
311 rados_aio_release(comp
);
314 reply
.append(reply_buf
, reply_buf_len
);
315 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
316 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
317 auto reply_p
= reply
.cbegin();
318 decode(reply_map
, reply_p
);
319 decode(missed_map
, reply_p
);
320 ASSERT_EQ(1u, reply_map
.size());
321 ASSERT_EQ(0u, missed_map
.size());
322 ASSERT_EQ(1u, notify_cookies
.size());
323 ASSERT_EQ(1u, notify_cookies
.count(handle
));
324 ASSERT_EQ(5u, reply_map
.begin()->second
.length());
325 ASSERT_EQ(0, strncmp("reply", reply_map
.begin()->second
.c_str(), 5));
326 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
327 rados_buffer_free(reply_buf
);
329 // try it on a non-existent object ... our buffer pointers
330 // should get zeroed.
331 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
332 ASSERT_EQ(0, rados_aio_notify(ioctx
, "doesnotexist", comp
, "notify", 6,
333 300000, &reply_buf
, &reply_buf_len
));
334 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
335 ASSERT_EQ(-ENOENT
, rados_aio_get_return_value(comp
));
336 rados_aio_release(comp
);
337 ASSERT_EQ((char*)0, reply_buf
);
338 ASSERT_EQ(0u, reply_buf_len
);
340 rados_unwatch2(ioctx
, handle
);
341 rados_watch_flush(cluster
);
346 TEST_F(LibRadosWatchNotify
, WatchNotify2Multi
) {
349 notify_cookies
.clear();
351 memset(buf
, 0xcc, sizeof(buf
));
352 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
353 uint64_t handle1
, handle2
;
355 rados_watch2(ioctx
, notify_oid
, &handle1
,
356 watch_notify2_test_cb
,
357 watch_notify2_test_errcb
, this));
359 rados_watch2(ioctx
, notify_oid
, &handle2
,
360 watch_notify2_test_cb
,
361 watch_notify2_test_errcb
, this));
362 ASSERT_GT(rados_watch_check(ioctx
, handle1
), 0);
363 ASSERT_GT(rados_watch_check(ioctx
, handle2
), 0);
364 ASSERT_NE(handle1
, handle2
);
366 size_t reply_buf_len
;
367 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
369 &reply_buf
, &reply_buf_len
));
371 reply
.append(reply_buf
, reply_buf_len
);
372 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
373 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
374 auto reply_p
= reply
.cbegin();
375 decode(reply_map
, reply_p
);
376 decode(missed_map
, reply_p
);
377 ASSERT_EQ(2u, reply_map
.size());
378 ASSERT_EQ(5u, reply_map
.begin()->second
.length());
379 ASSERT_EQ(0u, missed_map
.size());
380 ASSERT_EQ(2u, notify_cookies
.size());
381 ASSERT_EQ(1u, notify_cookies
.count(handle1
));
382 ASSERT_EQ(1u, notify_cookies
.count(handle2
));
383 ASSERT_EQ(0, strncmp("reply", reply_map
.begin()->second
.c_str(), 5));
384 ASSERT_GT(rados_watch_check(ioctx
, handle1
), 0);
385 ASSERT_GT(rados_watch_check(ioctx
, handle2
), 0);
386 rados_buffer_free(reply_buf
);
387 rados_unwatch2(ioctx
, handle1
);
388 rados_unwatch2(ioctx
, handle2
);
389 rados_watch_flush(cluster
);
394 TEST_F(LibRadosWatchNotify
, WatchNotify2Timeout
) {
397 notify_sleep
= 3; // 3s
398 notify_cookies
.clear();
400 memset(buf
, 0xcc, sizeof(buf
));
401 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
404 rados_watch2(ioctx
, notify_oid
, &handle
,
405 watch_notify2_test_cb
,
406 watch_notify2_test_errcb
, this));
407 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
409 size_t reply_buf_len
;
410 ASSERT_EQ(-ETIMEDOUT
, rados_notify2(ioctx
, notify_oid
,
411 "notify", 6, 1000, // 1s
412 &reply_buf
, &reply_buf_len
));
413 ASSERT_EQ(1u, notify_cookies
.size());
416 reply
.append(reply_buf
, reply_buf_len
);
417 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
418 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
419 auto reply_p
= reply
.cbegin();
420 decode(reply_map
, reply_p
);
421 decode(missed_map
, reply_p
);
422 ASSERT_EQ(0u, reply_map
.size());
423 ASSERT_EQ(1u, missed_map
.size());
425 rados_buffer_free(reply_buf
);
427 // we should get the next notify, though!
429 notify_cookies
.clear();
430 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
431 "notify", 6, 300000, // 300s
432 &reply_buf
, &reply_buf_len
));
433 ASSERT_EQ(1u, notify_cookies
.size());
434 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
436 rados_unwatch2(ioctx
, handle
);
438 rados_completion_t comp
;
439 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
440 rados_aio_watch_flush(cluster
, comp
);
441 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
442 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
443 rados_aio_release(comp
);
444 rados_buffer_free(reply_buf
);
448 TEST_F(LibRadosWatchNotify
, Watch3Timeout
) {
451 notify_cookies
.clear();
454 memset(buf
, 0xcc, sizeof(buf
));
455 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
457 time_t start
= time(0);
458 const uint32_t timeout
= 4;
460 // make sure i timeout before the messenger reconnects to the OSD,
461 // it will resend a watch request on behalf of the client, and the
462 // timer of timeout on OSD side will be reset by the new request.
464 ASSERT_EQ(0, rados_conf_get(cluster
,
465 "ms_tcp_read_timeout",
466 conf
, sizeof(conf
)));
467 auto tcp_read_timeout
= std::stoll(conf
);
468 ASSERT_LT(timeout
, tcp_read_timeout
);
471 rados_watch3(ioctx
, notify_oid
, &handle
,
472 watch_notify2_test_cb
, watch_notify2_test_errcb
,
474 int age
= rados_watch_check(ioctx
, handle
);
475 time_t age_bound
= time(0) + 1 - start
;
476 ASSERT_LT(age
, age_bound
* 1000);
478 rados_conf_set(cluster
, "objecter_inject_no_watch_ping", "true");
479 // allow a long time here since an osd peering event will renew our
481 int left
= 16 * timeout
;
482 std::cout
<< "waiting up to " << left
<< " for osd to time us out ..."
484 while (notify_err
== 0 && --left
) {
488 rados_conf_set(cluster
, "objecter_inject_no_watch_ping", "false");
489 ASSERT_EQ(-ENOTCONN
, notify_err
);
490 ASSERT_EQ(-ENOTCONN
, rados_watch_check(ioctx
, handle
));
492 // a subsequent notify should not reach us
493 char *reply_buf
= nullptr;
494 size_t reply_buf_len
;
495 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
497 &reply_buf
, &reply_buf_len
));
500 reply
.append(reply_buf
, reply_buf_len
);
501 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
502 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
503 auto reply_p
= reply
.cbegin();
504 decode(reply_map
, reply_p
);
505 decode(missed_map
, reply_p
);
506 ASSERT_EQ(0u, reply_map
.size());
507 ASSERT_EQ(0u, missed_map
.size());
509 ASSERT_EQ(0u, notify_cookies
.size());
510 ASSERT_EQ(-ENOTCONN
, rados_watch_check(ioctx
, handle
));
511 rados_buffer_free(reply_buf
);
514 rados_unwatch2(ioctx
, handle
);
515 rados_watch_flush(cluster
);
519 rados_watch2(ioctx
, notify_oid
, &handle
,
520 watch_notify2_test_cb
,
521 watch_notify2_test_errcb
, this));
522 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
524 // and now a notify will work.
525 ASSERT_EQ(0, rados_notify2(ioctx
, notify_oid
,
527 &reply_buf
, &reply_buf_len
));
530 reply
.append(reply_buf
, reply_buf_len
);
531 std::map
<std::pair
<uint64_t,uint64_t>, bufferlist
> reply_map
;
532 std::set
<std::pair
<uint64_t,uint64_t> > missed_map
;
533 auto reply_p
= reply
.cbegin();
534 decode(reply_map
, reply_p
);
535 decode(missed_map
, reply_p
);
536 ASSERT_EQ(1u, reply_map
.size());
537 ASSERT_EQ(0u, missed_map
.size());
538 ASSERT_EQ(1u, notify_cookies
.count(handle
));
539 ASSERT_EQ(5u, reply_map
.begin()->second
.length());
540 ASSERT_EQ(0, strncmp("reply", reply_map
.begin()->second
.c_str(), 5));
542 ASSERT_EQ(1u, notify_cookies
.size());
543 ASSERT_GT(rados_watch_check(ioctx
, handle
), 0);
545 rados_buffer_free(reply_buf
);
546 rados_unwatch2(ioctx
, handle
);
547 rados_watch_flush(cluster
);
550 TEST_F(LibRadosWatchNotify
, AioWatchDelete2
) {
555 uint32_t timeout
= 3;
556 memset(buf
, 0xcc, sizeof(buf
));
557 ASSERT_EQ(0, rados_write(ioctx
, notify_oid
, buf
, sizeof(buf
), 0));
560 rados_completion_t comp
;
562 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
563 rados_aio_watch2(ioctx
, notify_oid
, comp
, &handle
,
564 watch_notify2_test_cb
, watch_notify2_test_errcb
, timeout
, this);
565 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
566 ASSERT_EQ(0, rados_aio_get_return_value(comp
));
567 rados_aio_release(comp
);
568 ASSERT_EQ(0, rados_remove(ioctx
, notify_oid
));
570 std::cout
<< "waiting up to " << left
<< " for disconnect notification ..."
572 while (notify_err
== 0 && --left
) {
575 ASSERT_TRUE(left
> 0);
576 ASSERT_EQ(-ENOTCONN
, notify_err
);
577 ASSERT_EQ(-ENOTCONN
, rados_watch_check(ioctx
, handle
));
578 ASSERT_EQ(0, rados_aio_create_completion(NULL
, NULL
, NULL
, &comp
));
579 rados_aio_unwatch(ioctx
, handle
, comp
);
580 ASSERT_EQ(0, rados_aio_wait_for_complete(comp
));
581 ASSERT_EQ(-ENOENT
, rados_aio_get_return_value(comp
));
582 rados_aio_release(comp
);