cluck 1.0.1
The cluster lock service.
ticket.cpp
Go to the documentation of this file.
1// Copyright (c) 2016-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/cluck
4// contact@m2osw.com
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19
20// self
21//
22#include "ticket.h"
23
24#include "cluckd.h"
25
26
27// cluck
28//
29#include <cluck/exception.h>
30#include <cluck/names.h>
31
32
33// advgetopt
34//
35#include <advgetopt/validator_integer.h>
36
37
38// snapdev
39//
40#include <snapdev/hexadecimal_string.h>
41#include <snapdev/string_replace_many.h>
42#include <snapdev/tokenize_string.h>
43
44
45// snaplogger
46//
47#include <snaplogger/message.h>
48
49
50// last include
51//
52#include <snapdev/poison.h>
53
54
55
56namespace cluck_daemon
57{
58
59
60
357 cluckd * c
359 , std::string const & object_name
360 , ed::dispatcher_match::tag_t tag
361 , std::string const & entering_key
362 , cluck::timeout_t obtention_timeout
363 , cluck::timeout_t lock_duration
364 , std::string const & server_name
365 , std::string const & service_name)
366 : f_cluckd(c)
367 , f_messenger(messenger)
368 , f_object_name(object_name)
369 , f_tag(tag)
370 , f_obtention_timeout(obtention_timeout)
371 , f_lock_duration(std::clamp(
372 lock_duration
373 , cluck::CLUCK_UNLOCK_MINIMUM_TIMEOUT
374 , cluck::CLUCK_MAXIMUM_TIMEOUT))
375 , f_server_name(server_name)
376 , f_service_name(service_name)
377 , f_owner(f_cluckd->get_server_name())
378 , f_entering_key(entering_key)
379{
381
382 // TODO: see how to not say "attempting a lock" when we are deserializing
383 // an existing lock.
384 SNAP_LOG_TRACE
385 << "Attempting to lock \""
387 << "\" ("
388 << f_tag
389 << ") on \""
391 << "\" for \""
393 << '/'
395 << "\" (timeout: "
397 << ")."
398 << SNAP_LOG_SEND;
399}
400
401
417bool ticket::send_message_to_leaders(ed::message & msg)
418{
419 // finish the message initialization
420 //
421 msg.set_service(cluck::g_name_cluck_service_name);
422 msg.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
423 msg.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
424
426 if(leader != nullptr)
427 {
428 // there are at least two leaders
429 //
430 int count(0);
431 msg.set_server(leader->get_name());
432 if(f_messenger->send_message(msg))
433 {
434 ++count;
435 }
436
437 // check for a third leader
438 //
439 leader = f_cluckd->get_leader_b();
440 if(leader != nullptr)
441 {
442 msg.set_server(leader->get_name());
443 if(f_messenger->send_message(msg))
444 {
445 ++count;
446 }
447 }
448
449 // we have to wait for at least one reply if we were able to send
450 // at least one message
451 //
452 return count > 0;
453 }
454
455 // there is only one leader (ourselves)
456 //
457 // call the one_leader() function to verify that this is indeed correct
458 // otherwise we would mess up the algorithm
459 //
460 return one_leader();
461}
462
463
472{
473 // TODO implement the special case when there is only 1 leader
474 // (on the other hand, that should be rather rare)
475 //computer::pointer_t leader(f_cluckd->get_leader_a());
476 //if(leader == nullptr)
477 //{
478 // -- do the necessary to obtain the lock --
479 // return;
480 //}
481
482 ed::message entering_message;
483 entering_message.set_command(cluck::g_name_cluck_cmd_lock_entering);
484 entering_message.add_parameter(cluck::g_name_cluck_param_key, f_entering_key);
485 entering_message.add_parameter(cluck::g_name_cluck_param_timeout, f_obtention_timeout);
486 entering_message.add_parameter(cluck::g_name_cluck_param_duration, f_lock_duration);
488 {
489 entering_message.add_parameter(cluck::g_name_cluck_param_unlock_duration, f_unlock_duration);
490 }
491 entering_message.add_parameter(cluck::g_name_cluck_param_source, f_server_name + "/" + f_service_name);
492 entering_message.add_parameter(cluck::g_name_cluck_param_serial, f_serial);
493 if(send_message_to_leaders(entering_message))
494 {
495 if(one_leader())
496 {
497 // there are no other leaders, make sure the algorithm progresses
498 //
499 entered();
500 }
501 }
502}
503
504
522{
523 // is this ticket concerned?
524 //
526 {
527 // with 2 or 3 leaders, quorum is obtain with one
528 // single acknowledgement
529 //
530 f_get_max_ticket = true;
531
532 // calculate this instance max. ticket number
533 //
535
536 ed::message get_max_ticket_message;
537 get_max_ticket_message.set_command(cluck::g_name_cluck_cmd_get_max_ticket);
538 get_max_ticket_message.add_parameter(cluck::g_name_cluck_param_key, f_entering_key);
539 if(send_message_to_leaders(get_max_ticket_message))
540 {
541 if(one_leader())
542 {
543 // there are no other leaders, make sure the algorithm progresses
544 //
546 }
547 }
548 }
549}
550
551
566void ticket::max_ticket(ticket_id_t new_max_ticket)
567{
568 if(!f_added_ticket)
569 {
570 if(new_max_ticket > f_our_ticket)
571 {
572 f_our_ticket = new_max_ticket;
573 }
574
575 ++f_our_ticket;
577 {
578 // f_out_ticket is a 32 bit number, this can happen only if you
579 // created over 4 billion locks back to back--i.e. created a new
580 // one before the previous one was released; or put in a different
581 // way: the list of tickets with that "object name" never went
582 // back to being empty for that long...
583 //
584 throw cluck::out_of_range("ticket::max_ticket() tried to generate the next ticket and got a wrapping around number.");
585 }
586
587 add_ticket();
588 }
589}
590
591
602{
603 // we expect exactly one call to this function
604 //
606 {
607 throw cluck::logic_error("ticket::add_ticket() called more than once."); // LCOV_EXCL_LINE
608 }
609 f_added_ticket = true;
610
611 //
612 // WARNING: the ticket key MUST be properly sorted by:
613 //
614 // ticket number
615 // server name
616 // client pid
617 //
618 // The client PID does not need to be sorted numerically, just be sorted
619 // so one client is before the other.
620 //
621 // However, the ticket number MUST be numerically sorted. For this reason,
622 // since the key is a string, we must add introducing zeroes.
623 //
624 f_ticket_key = snapdev::int_to_hex(f_our_ticket, false, 8)
625 + '/'
627
628 f_cluckd->set_ticket(f_object_name, f_ticket_key, shared_from_this());
629
630 ed::message add_ticket_message;
631 add_ticket_message.set_command(cluck::g_name_cluck_cmd_add_ticket);
632 add_ticket_message.add_parameter(cluck::g_name_cluck_param_key, f_ticket_key);
633 add_ticket_message.add_parameter(cluck::g_name_cluck_param_timeout, f_obtention_timeout);
634 if(send_message_to_leaders(add_ticket_message))
635 {
636 if(one_leader())
637 {
639 }
640 }
641}
642
643
657void ticket::ticket_added(key_map_t const & still_entering)
658{
660 {
661 // when we have 2 or 3 leaders, quorum is obtain with one
662 // single acknowledgement
663 //
665
666 f_still_entering = still_entering;
667
668 // okay, the ticket was added on all cluck daemons
669 // now we can forget about the entering flag
670 // (equivalent to setting it to false)
671 //
672 ed::message exiting_message;
673 exiting_message.set_command(cluck::g_name_cluck_cmd_lock_exiting);
674 exiting_message.add_parameter(cluck::g_name_cluck_param_key, f_entering_key);
675 snapdev::NOT_USED(send_message_to_leaders(exiting_message));
676
677 f_cluckd->lock_exiting(exiting_message);
678 }
679}
680
681
694void ticket::remove_entering(std::string const & key)
695{
697 && !f_ticket_ready)
698 {
699 auto it(f_still_entering.find(key));
700 if(it != f_still_entering.end())
701 {
702 f_still_entering.erase(it);
703
704 // just like the quorum computation, we compute the
705 // remaining list of entering tickets dynamically at
706 // the time we check the value
707 //
708 for(auto key_entering(f_still_entering.begin()); key_entering != f_still_entering.end(); )
709 {
710 if(key_entering->second->timed_out())
711 {
712 key_entering = f_still_entering.erase(key_entering);
713 }
714 else
715 {
716 ++key_entering;
717 }
718 }
719
720 // once all removed, our ticket is ready!
721 //
722 if(f_still_entering.empty())
723 {
724 f_ticket_ready = true;
725
726 // let the other two leaders know that the ticket is ready
727 //
728 ed::message ticket_ready_message;
729 ticket_ready_message.set_command(cluck::g_name_cluck_cmd_ticket_ready);
730 ticket_ready_message.add_parameter(cluck::g_name_cluck_param_key, f_ticket_key);
731 snapdev::NOT_USED(send_message_to_leaders(ticket_ready_message));
732 }
733 }
734 }
735}
736
737
751{
753 && !f_locked
755 {
756 ed::message activate_lock_message;
757 activate_lock_message.set_command(cluck::g_name_cluck_cmd_activate_lock);
758 activate_lock_message.add_parameter(cluck::g_name_cluck_param_key, f_ticket_key);
759 if(send_message_to_leaders(activate_lock_message))
760 {
761 if(one_leader())
762 {
764 }
765 }
766 }
767}
768
769
780{
782 && !f_locked
784 {
785 f_locked = true;
786 f_lock_timeout_date = snapdev::now() + f_lock_duration;
788
790 {
791 ed::message locked_message;
792 locked_message.set_command(cluck::g_name_cluck_cmd_locked);
793 locked_message.set_server(f_server_name);
794 locked_message.set_service(f_service_name);
795 locked_message.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
796 locked_message.add_parameter(cluck::g_name_cluck_param_timeout_date, f_lock_timeout_date);
797 locked_message.add_parameter(cluck::g_name_cluck_param_unlocked_date, f_unlocked_timeout_date);
798 locked_message.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
799 f_messenger->send_message(locked_message);
800 }
801 }
802}
803
804
814{
815 SNAP_LOG_TRACE
816 << "Unlock on \""
818 << "\" ("
819 << f_tag
820 << ") with key \""
822 << "\"."
823 << SNAP_LOG_SEND;
824
825 ed::message drop_ticket_message;
826 drop_ticket_message.set_command(cluck::g_name_cluck_cmd_drop_ticket);
827 drop_ticket_message.add_parameter(
828 cluck::g_name_cluck_param_key
830 send_message_to_leaders(drop_ticket_message);
831
833 {
835
836 //if(f_owner == f_cluckd->get_server_name()) -- this can happen with any leader so we have to send the UNLOCKED
837 // the other leaders won't call this function they receive DROP_TICKET
838 // instead and as mentioned in the TODO below, we should get a QUORUM
839 // instead...
840 {
841 // we can immediately say it got unlocked...
842 //
843 // TODO: this is true ONLY if you lock the same object no more than
844 // once within a session, which is not unlikely false (it is
845 // true for what I can remember of Snap!, but long term this
846 // is not safe.) Like the LOCK, we need a quorum and then
847 // send the UNLOCK... At this point, I'm not too sure how
848 // we implement such because the drop_ticket function ends
849 // up deleting the ticket from memory and thus no counting
850 // can happen after that... (i.e. we need a special case
851 // of the receiver for the UNLOCK, argh!)
852 //
853 ed::message unlocked_message;
854 unlocked_message.set_command(cluck::g_name_cluck_cmd_unlocked);
855 unlocked_message.set_server(f_server_name);
856 unlocked_message.set_service(f_service_name);
857 unlocked_message.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
858 unlocked_message.add_parameter(cluck::g_name_cluck_param_unlocked_date, snapdev::now());
859 unlocked_message.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
860 f_messenger->send_message(unlocked_message);
861 }
862 }
863}
864
865
897void ticket::lock_failed(std::string const & reason)
898{
899 enum send_msg_t
900 {
901 SEND_MSG_NONE,
902 SEND_MSG_UNLOCKING,
903 SEND_MSG_UNLOCKED,
904 SEND_MSG_FAILED,
905 };
906
907 send_msg_t send(SEND_MSG_NONE);
908
909 switch(f_lock_failed)
910 {
912 // send that message at most once
913 //
915
916 if(f_locked)
917 {
918 // now we have to extend the lock timeout to make sure that
919 // the UNLOCKING has a chance to be acknowledged
920 //
922 if(timed_out())
923 {
924 // this case is logical here, but I don't think it can
925 // happen because the f_locked is true and thus the only
926 // value we can use is f_lock_timeout_date and we just
927 // increased that value by at least 3 seconds
928 //
929 send = SEND_MSG_UNLOCKED; // LCOV_EXCL_LINE
930 }
931 else
932 {
933 send = SEND_MSG_UNLOCKING;
934 }
935 }
936 else
937 {
938 send = SEND_MSG_FAILED;
939 }
940 break;
941
944
945 if(f_locked)
946 {
947 send = SEND_MSG_UNLOCKED;
948 }
949 break;
950
952 // we already sent all the possible messages
953 break;
954
955 }
956
957 // we want the f_lock_failed and f_lock_timeout_date set before returning
958 //
960 {
961 return;
962 }
963
964 switch(send)
965 {
966 case SEND_MSG_NONE:
967 // don't send another message
968 break;
969
970 case SEND_MSG_UNLOCKING:
971 {
972 // if we were locked and reach here, then the lock
973 // timed out while locked but the unlock timeout was
974 // not yet reached so just send an UNLOCKING message
975 //
976 SNAP_LOG_IMPORTANT
977 << "Lock on \""
979 << "\" ("
980 << f_tag
981 << ") with key \""
983 << "\" timed out its lock allowed time."
984 << SNAP_LOG_SEND;
985
986 ed::message lock_failed_message;
987 lock_failed_message.set_command(cluck::g_name_cluck_cmd_unlocking);
988 lock_failed_message.set_server(f_server_name);
989 lock_failed_message.set_service(f_service_name);
990 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
991 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
992 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_timedout);
993 f_messenger->send_message(lock_failed_message);
994 }
995 break;
996
997 case SEND_MSG_UNLOCKED:
998 {
999 // if we were locked and/or unlocking and we reach here,
1000 // then the lock completely timed out and we immediately
1001 // completely unlock with an UNLOCKED message
1002 //
1003 // IMPORTANT: that means the service should stop using the
1004 // shared resources but there is absoltely no
1005 // guarantee about that; however, this situation
1006 // should only occur when a service somehow does
1007 // not properly UNLOCK its lock
1008 //
1009 SNAP_LOG_IMPORTANT
1010 << "Lock on \""
1011 << f_object_name
1012 << "\" ("
1013 << f_tag
1014 << ") with key \""
1016 << "\" timed out its unlocking allowed time."
1017 << SNAP_LOG_SEND;
1018
1019 ed::message lock_failed_message;
1020 lock_failed_message.set_command(cluck::g_name_cluck_cmd_unlocked);
1021 lock_failed_message.set_server(f_server_name);
1022 lock_failed_message.set_service(f_service_name);
1023 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
1024 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
1025 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_timedout);
1026 f_messenger->send_message(lock_failed_message);
1027 }
1028 break;
1029
1030 case SEND_MSG_FAILED:
1031 {
1032 SNAP_LOG_IMPORTANT
1033 << "Lock on \""
1034 << f_object_name
1035 << "\" ("
1036 << f_tag
1037 << ") with key \""
1039 << "\" failed."
1040 << SNAP_LOG_SEND;
1041
1042 ed::message lock_failed_message;
1043 lock_failed_message.set_command(cluck::g_name_cluck_cmd_lock_failed);
1044 lock_failed_message.set_server(f_server_name);
1045 lock_failed_message.set_service(f_service_name);
1046 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name, f_object_name);
1047 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag, f_tag);
1048 lock_failed_message.add_parameter(cluck::g_name_cluck_param_key, f_entering_key);
1049 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_failed);
1050 lock_failed_message.add_parameter(cluck::g_name_cluck_param_description,
1051 "ticket failed before or after the lock was obtained ("
1052 + reason
1053 + ")");
1054 f_messenger->send_message(lock_failed_message);
1055 }
1056 break;
1057
1058 }
1059}
1060
1061
1073void ticket::set_owner(std::string const & owner)
1074{
1075 f_owner = owner;
1076}
1077
1078
1096std::string const & ticket::get_owner() const
1097{
1098 return f_owner;
1099}
1100
1101
1115{
1116 std::vector<std::string> segments;
1117 if(snapdev::tokenize_string(segments, f_entering_key, "/") != 2)
1118 {
1119 throw cluck::invalid_parameter(
1120 "ticket::get_client_pid() split f_entering_key \""
1122 + "\" and did not get exactly two segments.");
1123 }
1124 std::int64_t value;
1125 advgetopt::validator_integer::convert_string(segments[1], value);
1126 return static_cast<pid_t>(value);
1127}
1128
1129
1148{
1149 f_serial = serial;
1150}
1151
1152
1161{
1162 return f_serial;
1163}
1164
1165
1189{
1190 if(duration == cluck::CLUCK_DEFAULT_TIMEOUT)
1191 {
1192 duration = f_lock_duration;
1193 }
1194
1195 f_unlock_duration = std::clamp(
1196 duration
1199}
1200
1201
1217
1218
1228{
1229 f_ticket_ready = true;
1230}
1231
1232
1247{
1249 || f_added_ticket)
1250 {
1251 throw cluck::logic_error("ticket::set_ticket_number() called with "
1252 + std::to_string(number)
1253 + " when f_our_ticket is already set to "
1254 + std::to_string(f_our_ticket)
1255 + ".");
1256 }
1257 f_added_ticket = true;
1258
1259 f_our_ticket = number;
1260 f_ticket_key = snapdev::int_to_hex(f_our_ticket, false, 8)
1261 + '/'
1263}
1264
1265
1282
1283
1291{
1292 return f_locked;
1293}
1294
1295
1306{
1307 return f_cluckd->get_computer_count() == 1;
1308}
1309
1310
1323
1324
1354{
1355 if(timeout < cluck::timeout_t())
1356 {
1357 timeout = cluck::timeout_t();
1358 }
1359
1360 if(timeout < f_obtention_timeout)
1361 {
1362 f_alive_timeout = timeout;
1363 }
1364 else
1365 {
1366 // use the obtention timeout if smaller because that was the
1367 // first premise that the client asked about
1368 //
1370 }
1371}
1372
1373
1385
1386
1402
1403
1429{
1431 {
1432 return f_alive_timeout;
1433 }
1434
1435 if(f_locked)
1436 {
1437 return f_lock_timeout_date;
1438 }
1439
1440 return f_obtention_timeout;
1441}
1442
1443
1464{
1465 return get_current_timeout_date() <= snapdev::now();
1466}
1467
1468
1476std::string const & ticket::get_object_name() const
1477{
1478 return f_object_name;
1479}
1480
1481
1489ed::dispatcher_match::tag_t ticket::get_tag() const
1490{
1491 return f_tag;
1492}
1493
1494
1506std::string const & ticket::get_server_name() const
1507{
1508 return f_server_name;
1509}
1510
1511
1523std::string const & ticket::get_service_name() const
1524{
1525 return f_service_name;
1526}
1527
1528
1540std::string const & ticket::get_entering_key() const
1541{
1542 return f_entering_key;
1543}
1544
1545
1561std::string const & ticket::get_ticket_key() const
1562{
1563 return f_ticket_key;
1564}
1565
1566
1583std::string ticket::serialize() const
1584{
1585 std::map<std::string, std::string> data;
1586
1587 data["object_name"] = f_object_name;
1588 data["tag"] = std::to_string(static_cast<int>(f_tag));
1589 data["obtention_timeout"] = f_obtention_timeout.to_timestamp(true);
1590 //data["alive_timeout"] = f_alive_timeout.to_timestamp(true); -- we do not want to transfer this one
1591 data["lock_duration"] = f_lock_duration.to_timestamp(true);
1592 data["unlock_duration"] = f_unlock_duration.to_timestamp(true);
1593 data["server_name"] = f_server_name;
1594 data["service_name"] = f_service_name;
1595 data["owner"] = f_owner;
1596 if(f_serial != NO_SERIAL)
1597 {
1598 data["serial"] = std::to_string(f_serial);
1599 }
1600 data["entering_key"] = f_entering_key;
1601 data["get_max_ticket"] = f_get_max_ticket ? "true" : "false";
1602 data["our_ticket"] = std::to_string(f_our_ticket);
1603 data["added_ticket"] = f_added_ticket ? "true" : "false";
1604 data["ticket_key"] = f_ticket_key;
1605 data["added_ticket_quorum"] = f_added_ticket_quorum ? "true" : "false";
1606
1607 // this is a map
1608 //data["still_entering"] = f_still_entering;
1609 //ticket::key_map_t f_still_entering = key_map_t();
1610
1611 data["ticket_ready"] = f_ticket_ready ? "true" : "false";
1612 data["locked"] = f_locked ? "true" : "false";
1613 data["lock_timeout_date"] = f_lock_timeout_date.to_timestamp(true);
1614
1615 switch(f_lock_failed)
1616 {
1618 data["lock_failed"] = "none";
1619 break;
1620
1622 data["lock_failed"] = "lock";
1623 break;
1624
1626 data["lock_failed"] = "unlocking";
1627 break;
1628
1629 }
1630
1631 std::string result;
1632 for(auto & it : data)
1633 {
1634 result += it.first;
1635 result += '=';
1636 // make sure the value does not include any '|'
1637 result += snapdev::string_replace_many(it.second, {{"|", "%7C"}});
1638 result += '|';
1639 }
1640 result.pop_back();
1641
1642 return result;
1643}
1644
1645
1659void ticket::unserialize(std::string const & data)
1660{
1661 std::vector<std::string> vars;
1662 snapdev::NOT_USED(snapdev::tokenize_string(vars, data, "|"));
1663 for(auto const & d : vars)
1664 {
1665 std::string::size_type const pos(d.find('='));
1666 std::string const name(d.substr(0, pos));
1667 std::string const value(d.substr(pos + 1));
1668 switch(name[0])
1669 {
1670 case 'a':
1671 if(name == "added_ticket")
1672 {
1673 f_added_ticket = f_added_ticket || value == "true";
1674 }
1675 else if(name == "added_ticket_quorum")
1676 {
1677 f_added_ticket_quorum = f_added_ticket_quorum || value == "true";
1678 }
1679 //else if(name == "alive_timeout") -- we do not transfer this one (not required, and could actually cause problems)
1680 //{
1681 // f_alive_timeout = cluck::timeout_t(value);
1682 //}
1683 break;
1684
1685 case 'e':
1686 if(name == "entering_key")
1687 {
1688#ifdef _DEBUG
1689 if(f_entering_key != value)
1690 {
1691 // LCOV_EXCL_START
1692 throw cluck::logic_error(
1693 "ticket::unserialize() not unserializing entering key \""
1694 + value
1695 + "\" over itself \""
1696 + f_entering_key
1697 + "\" (entering key mismatch).");
1698 // LCOV_EXCL_STOP
1699 }
1700#endif
1701 f_entering_key = value;
1702 }
1703 break;
1704
1705 case 'g':
1706 if(name == "get_max_ticket")
1707 {
1708 f_get_max_ticket = f_get_max_ticket || value == "true";
1709 }
1710 break;
1711
1712 case 'l':
1713 if(name == "lock_duration")
1714 {
1715 f_lock_duration = cluck::timeout_t(value);
1716 }
1717 else if(name == "locked")
1718 {
1719 f_locked = f_locked || value == "true";
1720 }
1721 else if(name == "lock_timeout_date")
1722 {
1723 // the time may be larger because of an UNLOCK so we keep
1724 // the largest value
1725 //
1726 cluck::timeout_t const timeout_date(value);
1727 if(timeout_date > f_lock_timeout_date)
1728 {
1729 f_lock_timeout_date = timeout_date;
1730 }
1731 }
1732 else if(name == "lock_failed")
1733 {
1734 // in this case, we avoid reducing the error level
1735 //
1736 if(value == "unlocking")
1737 {
1738 f_lock_failed = lock_failure_t::LOCK_FAILURE_UNLOCKING;
1739 }
1740 else if(value == "lock" && f_lock_failed == lock_failure_t::LOCK_FAILURE_NONE)
1741 {
1742 f_lock_failed = lock_failure_t::LOCK_FAILURE_LOCK;
1743 }
1744 }
1745 break;
1746
1747 case 'o':
1748 if(name == "object_name")
1749 {
1750#ifdef _DEBUG
1751 if(f_object_name != value)
1752 {
1753 // LCOV_EXCL_START
1754 throw cluck::logic_error(
1755 "ticket::unserialize() not unserializing object name \""
1756 + value
1757 + "\" over itself \""
1758 + f_object_name
1759 + "\" (object name mismatch).");
1760 // LCOV_EXCL_STOP
1761 }
1762#endif
1763 f_object_name = value;
1764 }
1765 else if(name == "obtention_timeout")
1766 {
1767 f_obtention_timeout = cluck::timeout_t(value);
1768 }
1769 else if(name == "owner")
1770 {
1771 f_owner = value;
1772 }
1773 else if(name == "our_ticket")
1774 {
1775 std::int64_t v;
1776 advgetopt::validator_integer::convert_string(value, v);
1777 f_our_ticket = v;
1778 }
1779 break;
1780
1781 case 's':
1782 if(name == "server_name")
1783 {
1784 f_server_name = value;
1785 }
1786 else if(name == "service_name")
1787 {
1788 f_service_name = value;
1789 }
1790 else if(name == "serial")
1791 {
1792 std::int64_t v;
1793 advgetopt::validator_integer::convert_string(value, v);
1794 f_serial = v;
1795 }
1796 break;
1797
1798 case 't':
1799 if(name == "tag")
1800 {
1801 std::int64_t v;
1802 advgetopt::validator_integer::convert_string(value, v);
1803 f_tag = v;
1804 }
1805 else if(name == "ticket_key")
1806 {
1807 f_ticket_key = value;
1808 }
1809 else if(name == "ticket_ready")
1810 {
1811 f_ticket_ready = f_ticket_ready || value == "true";
1812 }
1813 break;
1814
1815 case 'u':
1816 if(name == "unlock_duration")
1817 {
1818 f_unlock_duration = cluck::timeout_t(value);
1819 }
1820 break;
1821
1822 }
1823 }
1824}
1825
1826
1827
1828} // namespace cluck_daemon
1829// vim: ts=4 sw=4 et
Class handling intercomputer locking.
Definition cluckd.h:48
std::string const & get_server_name() const
Get the name of the server we are running on.
Definition cluckd.cpp:461
void lock_exiting(ed::message &msg)
Used to simulate a LOCK_EXITING message.
Definition cluckd.cpp:1960
computer::pointer_t get_leader_b() const
Get pointer to leader B.
Definition cluckd.cpp:748
ticket::ticket_id_t get_last_ticket(std::string const &lock_name)
Determine the last ticket defined in this cluck daemon.
Definition cluckd.cpp:1871
int get_computer_count() const
Return the number of known computers running cluckd.
Definition cluckd.cpp:445
ticket::key_map_t const get_entering_tickets(std::string const &lock_name)
Get a reference to the list of entering tickets.
Definition cluckd.cpp:1930
void set_ticket(std::string const &object_name, std::string const &key, ticket::pointer_t ticket)
Set the ticket.
Definition cluckd.cpp:1910
computer::pointer_t get_leader_a() const
Get pointer to leader A.
Definition cluckd.cpp:701
std::shared_ptr< computer > pointer_t
Definition computer.h:49
Handle messages from the communicatord.
Definition messenger.h:36
std::shared_ptr< messenger > pointer_t
Definition messenger.h:38
void ticket_added(key_map_t const &entering)
Called whenever a TICKET_ADDED is received.
Definition ticket.cpp:657
cluck::timeout_t f_unlock_duration
Definition ticket.h:133
key_map_t f_still_entering
Definition ticket.h:153
void set_owner(std::string const &owner)
Define whether this ticket is the owner of that lock.
Definition ticket.cpp:1073
void set_serial(serial_t owner)
Give the lock a serial number for some form of unicity.
Definition ticket.cpp:1147
std::string const & get_object_name() const
Retrieve the object name of this ticket.
Definition ticket.cpp:1476
void entering()
Enter the mode that lets us retrieve our ticket number.
Definition ticket.cpp:471
cluck::timeout_t get_unlock_duration() const
Get unlock duration.
Definition ticket.cpp:1213
void set_alive_timeout(cluck::timeout_t timeout)
Define a time when the ticket times out while waiting.
Definition ticket.cpp:1353
std::string const & get_service_name() const
Retrieve the service name of this ticket.
Definition ticket.cpp:1523
cluck::timeout_t f_lock_timeout_date
Definition ticket.h:162
static serial_t const NO_SERIAL
Definition ticket.h:52
bool timed_out() const
Check whether this ticket timed out.
Definition ticket.cpp:1463
cluck::timeout_t get_lock_timeout_date() const
Get the lock timeout date.
Definition ticket.cpp:1398
cluck::timeout_t f_obtention_timeout
Definition ticket.h:130
std::string const & get_owner() const
Return the name of this ticket's owner.
Definition ticket.cpp:1096
void entered()
Tell this entering that we received a LOCKENTERED message.
Definition ticket.cpp:521
cluckd * f_cluckd
Definition ticket.h:123
std::string const & get_entering_key() const
Retrieve a reference to the entering key of this ticket.
Definition ticket.cpp:1540
std::string f_ticket_key
Definition ticket.h:148
cluck::timeout_t get_obtention_timeout() const
Get the obtention timeout date.
Definition ticket.cpp:1319
static ticket_id_t const NO_TICKET
Definition ticket.h:53
messenger::pointer_t f_messenger
Definition ticket.h:127
cluck::timeout_t f_lock_duration
Definition ticket.h:132
std::string serialize() const
Serialize a ticket to send it over to another leader.
Definition ticket.cpp:1583
std::string f_owner
Definition ticket.h:136
ticket_id_t get_ticket_number() const
Return the ticket number of this ticket.
Definition ticket.cpp:1278
ed::dispatcher_match::tag_t get_tag() const
Retrieve the tag of this ticket.
Definition ticket.cpp:1489
ticket(cluckd *c, messenger::pointer_t messenger, std::string const &lock_name, ed::dispatcher_match::tag_t tag, std::string const &entering_key, cluck::timeout_t obtention_timeout, cluck::timeout_t lock_duration, std::string const &server_name, std::string const &service_name)
Initialize a ticket object.
Definition ticket.cpp:356
void set_unlock_duration(cluck::timeout_t duration)
Change the unlock duration to the specified value.
Definition ticket.cpp:1188
bool f_added_ticket_quorum
Definition ticket.h:152
void activate_lock()
Check whether this ticket can be activated and do so if so.
Definition ticket.cpp:750
std::string f_object_name
Definition ticket.h:128
serial_t f_serial
Definition ticket.h:137
cluck::timeout_t f_unlocked_timeout_date
Definition ticket.h:163
ed::dispatcher_match::tag_t f_tag
Definition ticket.h:129
std::string f_server_name
Definition ticket.h:134
std::string const & get_server_name() const
Retrieve the server name of this ticket.
Definition ticket.cpp:1506
pid_t get_client_pid() const
Retrieve the client process identifier.
Definition ticket.cpp:1114
lock_failure_t f_lock_failed
Definition ticket.h:167
std::uint32_t ticket_id_t
Definition ticket.h:50
bool send_message_to_leaders(ed::message &msg)
Send a message to the other two leaders.
Definition ticket.cpp:417
void drop_ticket()
We are done with the ticket.
Definition ticket.cpp:813
bool is_locked() const
Check whether this ticket is locked or not.
Definition ticket.cpp:1290
serial_t get_serial() const
Return the serial number of this ticket.
Definition ticket.cpp:1160
void lock_failed(std::string const &reason)
Let the service that wanted this lock know that it failed.
Definition ticket.cpp:897
void lock_activated()
Check whether this ticket can be activated and do so if so.
Definition ticket.cpp:779
cluck::timeout_t get_current_timeout_date() const
Get the current lock timeout date.
Definition ticket.cpp:1428
std::string const & get_ticket_key() const
Retrieve a reference to the ticket key.
Definition ticket.cpp:1561
std::string f_service_name
Definition ticket.h:135
void max_ticket(ticket_id_t new_max_ticket)
Called whenever a MAX_TICKET is received.
Definition ticket.cpp:566
ticket_id_t f_our_ticket
Definition ticket.h:146
void set_ticket_number(ticket_id_t number)
Set the ticket number.
Definition ticket.cpp:1246
void remove_entering(std::string const &key)
Call any time time an entering flag is reset.
Definition ticket.cpp:694
void add_ticket()
Send the ADD_TICKET message.
Definition ticket.cpp:601
void set_ready()
Mark the ticket as being ready.
Definition ticket.cpp:1227
bool one_leader() const
Check whether the system only has one leader.
Definition ticket.cpp:1305
std::int32_t serial_t
Definition ticket.h:49
cluck::timeout_t f_alive_timeout
Definition ticket.h:131
std::map< std::string, pointer_t > key_map_t
Definition ticket.h:47
cluck::timeout_t get_lock_duration() const
Retrieve the lock duration.
Definition ticket.cpp:1381
std::string f_entering_key
Definition ticket.h:141
Daemon handling inter-computer locking.
timeout_t CLUCK_DEFAULT_TIMEOUT
Definition cluck.h:82
snapdev::timespec_ex timeout_t
A timeout delay.
Definition cluck.h:80
timeout_t CLUCK_UNLOCK_MINIMUM_TIMEOUT
Definition cluck.h:89
timeout_t CLUCK_MAXIMUM_TIMEOUT
Definition cluck.h:84

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.