Joedb 9.1.4
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Server.cpp
Go to the documentation of this file.
3#include "joedb/get_pid.h"
7
8#define LOG(x) log([&](std::ostream &out){out << x;})
9#define LOGID(x) log([&](std::ostream &out){session->write_id(out) << x;})
10
11#include <asio/read.hpp>
12#include <asio/write.hpp>
13
14namespace joedb
15{
16 ////////////////////////////////////////////////////////////////////////////
17 std::chrono::milliseconds Server::get_time_stamp() const
18 ////////////////////////////////////////////////////////////////////////////
19 {
20 return std::chrono::duration_cast<std::chrono::milliseconds>
21 (
22 std::chrono::steady_clock::now() - start_time
23 );
24 }
25
26 ////////////////////////////////////////////////////////////////////////////
27 std::ostream &Server::Session::write_id(std::ostream &out) const
28 ////////////////////////////////////////////////////////////////////////////
29 {
30#if 0
31 out << server.get_time_stamp().count() << ' ';
32#endif
33
34 out << server.port << '(' << id << "): ";
35
36 return out;
37 }
38
39 ////////////////////////////////////////////////////////////////////////////
40 Server::Session::Session(Server &server, asio::ip::tcp::socket &&socket):
41 ////////////////////////////////////////////////////////////////////////////
42 id(++server.session_id),
43 server(server),
44 socket(std::move(socket)),
45 state(State::not_locking)
46 {
47 server.log([this](std::ostream &out)
48 {
49 write_id(out) << "created (remote endpoint: ";
50 out << this->socket.remote_endpoint() << ")\n";
51 });
52 server.sessions.insert(this);
53 server.write_status();
54 }
55
56 ////////////////////////////////////////////////////////////////////////////
57 Server::Session::~Session()
58 ////////////////////////////////////////////////////////////////////////////
59 {
60 try
61 {
62 server.sessions.erase(this);
63
64 if (state == State::locking)
65 {
66 server.log([this](std::ostream &out)
67 {
68 write_id(out) << "removing lock held by dying session.\n";
69 });
70
71 server.unlock(*this);
72 }
73
74 server.log([this](std::ostream &out)
75 {
76 write_id(out) << "deleted\n";
77 });
78
79 server.write_status();
80 }
81 catch (...)
82 {
83 // What should we do? maybe post the exception?
84 // This may not be a problem any more with coroutines.
85 }
86 }
87
88 ////////////////////////////////////////////////////////////////////////////
89 void Server::async_read
90 ////////////////////////////////////////////////////////////////////////////
91 (
92 std::shared_ptr<Session> session,
93 size_t offset,
94 size_t size,
95 Transfer_Handler handler
96 )
97 {
98 asio::async_read
99 (
100 session->socket,
101 asio::buffer(session->buffer.data + offset, size),
102 [this, handler, session](std::error_code e, size_t s)
103 {
104 (this->*handler)(session, e, s);
105 }
106 );
107 }
108
109 ////////////////////////////////////////////////////////////////////////////
110 void Server::write_status()
111 ////////////////////////////////////////////////////////////////////////////
112 {
113 log([this](std::ostream &out)
114 {
115 out << port;
116 out << ": pid = " << joedb::get_pid();
117 out << "; " << get_time_string_of_now();
118 out << "; sessions = " << sessions.size();
119 out << "; checkpoint = ";
120 out << client.get_checkpoint() << '\n';
121 });
122 }
123
124 ////////////////////////////////////////////////////////////////////////////
125 void Server::lock_dequeue()
126 ////////////////////////////////////////////////////////////////////////////
127 {
128 if (!locked && !lock_queue.empty())
129 {
130 locked = true;
131 const std::shared_ptr<Session> session = lock_queue.front();
132 lock_queue.pop();
133 LOGID("locking\n");
134
135 if (!client_lock)
136 {
137 if (is_readonly())
138 {
139 LOGID("Error: locking pull-only server\n");
140 session->buffer.data[0] = 'R';
141 }
142 else
143 {
144 JOEDB_ASSERT(push_client);
145 client_lock.emplace(*push_client); // ??? takes_time
146 }
147 }
148
149 if (session->state == Session::State::waiting_for_lock_to_pull)
150 start_pulling(session);
151
152 session->state = Session::State::locking;
153 refresh_lock_timeout(session);
154 }
155 }
156
157 ////////////////////////////////////////////////////////////////////////////
158 void Server::lock
159 ////////////////////////////////////////////////////////////////////////////
160 (
161 const std::shared_ptr<Session> session,
162 const Session::State state
163 )
164 {
165 if (session->state != Session::State::locking)
166 {
167 session->state = state;
168 lock_queue.emplace(session);
169 lock_dequeue();
170 }
171 else
172 LOGID("Error: locking an already locked session\n");
173 }
174
175 ////////////////////////////////////////////////////////////////////////////
176 void Server::unlock(Session &session)
177 ////////////////////////////////////////////////////////////////////////////
178 {
179 if (session.state == Session::State::locking)
180 {
181 log([&session](std::ostream &out)
182 {
183 session.write_id(out) << "unlocking\n";
184 });
185 session.state = Session::State::not_locking;
186 locked = false;
187 lock_timeout_timer.cancel();
188
189 lock_dequeue();
190 }
191 }
192
193 ////////////////////////////////////////////////////////////////////////////
194 void Server::lock_timeout_handler
195 ////////////////////////////////////////////////////////////////////////////
196 (
197 const std::shared_ptr<Session> session,
198 const std::error_code error
199 )
200 {
201 if (!error)
202 {
203 LOGID("timeout\n");
204
205 if (session->push_writer)
206 {
207 session->push_writer.reset();
208 session->push_status = 't';
209 }
210
211 unlock(*session);
212 }
213 }
214
215 ////////////////////////////////////////////////////////////////////////////
216 void Server::refresh_lock_timeout(const std::shared_ptr<Session> session)
217 ////////////////////////////////////////////////////////////////////////////
218 {
219 if (lock_timeout.count() > 0 && session->state == Session::State::locking)
220 {
221 lock_timeout_timer.expires_after(lock_timeout);
222 lock_timeout_timer.async_wait
223 (
224 [this, session](std::error_code e){lock_timeout_handler(session, e);}
225 );
226 }
227 }
228
229 ////////////////////////////////////////////////////////////////////////////
230 void Server::push_transfer_handler
231 ////////////////////////////////////////////////////////////////////////////
232 (
233 const std::shared_ptr<Session> session,
234 const std::error_code error,
235 const size_t bytes_transferred
236 )
237 {
238 if (!error)
239 {
240 if (session->push_writer)
241 session->push_writer->write(session->buffer.data, bytes_transferred); // ??? takes_time
242
243 session->push_remaining_size -= bytes_transferred;
244
245 if (session->progress_bar)
246 {
247 session->progress_bar->print_remaining
248 (
249 int64_t(session->push_remaining_size)
250 );
251 }
252
253 push_transfer(session);
254 }
255 }
256
257 ////////////////////////////////////////////////////////////////////////////
258 void Server::push_transfer(const std::shared_ptr<Session> session)
259 ////////////////////////////////////////////////////////////////////////////
260 {
261 if (session->push_remaining_size > 0)
262 {
263 refresh_lock_timeout(session);
264
265 async_read
266 (
267 session,
268 0,
269 std::min(session->push_remaining_size, session->buffer.size),
270 &Server::push_transfer_handler
271 );
272 }
273 else
274 {
275 if (session->push_writer)
276 {
277 if (client_lock)
278 {
279 client_lock->get_journal().set_position
280 (
281 session->push_writer->get_position()
282 );
283 client_lock->get_journal().default_checkpoint();
284
285 // ??? takes_time
286 if (share_client && session->unlock_after_push)
287 {
288 client_lock->push_unlock();
289 client_lock.reset();
290 }
291 else
292 client_lock->push();
293 }
294
295 session->push_writer.reset();
296 }
297
298 session->buffer.data[0] = session->push_status;
299
300 if (session->progress_bar)
301 session->progress_bar.reset();
302 else
303 LOG(" done. ");
304
305 LOG("Returning '" << session->push_status << "'\n");
306
307 write_buffer_and_next_command(session, 1);
308
309 if (session->unlock_after_push)
310 unlock(*session);
311
312 for (auto *other_session: sessions)
313 {
314 if
315 (
316 other_session->state == Session::State::waiting_for_push_to_pull &&
317 other_session->pull_checkpoint < client.get_checkpoint()
318 )
319 {
320 other_session->state = Session::State::not_locking;
321 start_pulling(other_session->shared_from_this());
322 }
323 }
324 }
325 }
326
327 ////////////////////////////////////////////////////////////////////////////
328 void Server::push_handler
329 ////////////////////////////////////////////////////////////////////////////
330 (
331 const std::shared_ptr<Session> session,
332 const std::error_code error,
333 const size_t bytes_transferred
334 )
335 {
336 if (!error)
337 {
338 session->buffer.index = 0;
339 const int64_t start = session->buffer.read<int64_t>();
340 const int64_t size = session->buffer.read<int64_t>();
341
342 if (locked && session->state != Session::State::locking)
343 {
344 LOGID("trying to push while someone else is locking\n");
345 }
346 else if (!locked)
347 {
348 LOGID("Taking the lock for push attempt.\n");
349 lock(session, Session::State::waiting_for_lock_to_push);
350 }
351
352 const bool conflict = (size != 0) &&
353 (
354 session->state != Session::State::locking ||
355 start != client.get_journal().get_checkpoint_position()
356 );
357
358 LOGID("pushing, start = " << start << ", size = " << size);
359
360 if (log_pointer && size > session->buffer.ssize)
361 session->progress_bar.emplace(size, *log_pointer);
362
363 if (is_readonly())
364 session->push_status = 'R';
365 else if (conflict)
366 session->push_status = 'C';
367 else
368 {
369 session->push_status = 'U';
370 if (client_lock)
371 {
372 session->push_writer.emplace
373 (
374 client_lock->get_journal().get_async_tail_writer()
375 );
376 }
377 }
378
379 session->push_remaining_size = size_t(size);
380
381 push_transfer(session);
382 }
383 }
384
385 ////////////////////////////////////////////////////////////////////////////
386 void Server::read_transfer_handler
387 ////////////////////////////////////////////////////////////////////////////
388 (
389 const std::shared_ptr<Session> session,
390 Async_Reader reader,
391 const std::error_code error,
392 const size_t bytes_transferred,
393 const size_t offset
394 )
395 {
396 if (!error)
397 {
398 if (session->progress_bar)
399 session->progress_bar->print_remaining(reader.get_remaining());
400
401 if (offset + reader.get_remaining() > 0)
402 {
403 const size_t size = reader.read // ??? takes_time
404 (
405 session->buffer.data + offset,
406 session->buffer.size - offset
407 );
408
409 if (reader.is_end_of_file())
410 LOG("error: unexpected end of file\n");
411 else
412 {
413 refresh_lock_timeout(session);
414
415 asio::async_write
416 (
417 session->socket,
418 asio::buffer(session->buffer.data, size + offset),
419 [this, session, reader](std::error_code e, size_t s)
420 {
421 read_transfer_handler(session, reader, e, s, 0);
422 }
423 );
424 }
425 }
426 else
427 {
428 session->pull_timer.reset();
429 if (session->progress_bar)
430 session->progress_bar.reset();
431 else
432 LOG(" OK\n");
433 read_command(session);
434 }
435 }
436 }
437
438 ///////////////////////////////////////////////////////////////////////////
439 void Server::start_reading
440 ///////////////////////////////////////////////////////////////////////////
441 (
442 std::shared_ptr<Session> session,
443 Async_Reader reader
444 )
445 {
446 session->buffer.write<int64_t>(reader.get_remaining());
447
448 LOGID("reading from = " << reader.get_current() << ", size = "
449 << reader.get_remaining() << ':');
450
451 if (log_pointer && reader.get_remaining() > session->buffer.ssize)
452 session->progress_bar.emplace(reader.get_remaining(), *log_pointer);
453
454 read_transfer_handler
455 (
456 session,
457 reader,
458 std::error_code(),
459 0,
460 session->buffer.index
461 );
462 }
463
464 ///////////////////////////////////////////////////////////////////////////
465 void Server::start_pulling(std::shared_ptr<Session> session)
466 ///////////////////////////////////////////////////////////////////////////
467 {
468 if
469 (
470 session->lock_before_pulling &&
471 session->state != Session::State::waiting_for_lock_to_pull
472 )
473 {
474 lock(session, Session::State::waiting_for_lock_to_pull);
475 return;
476 }
477
478 session->buffer.index = 1;
479 session->buffer.write<int64_t>(client.get_checkpoint());
480
481 if (session->send_pull_data)
482 {
483 start_reading
484 (
485 session,
486 client.get_journal().get_async_tail_reader(session->pull_checkpoint)
487 );
488 }
489 else
490 write_buffer_and_next_command(session, session->buffer.index);
491 }
492
493 ///////////////////////////////////////////////////////////////////////////
494 void Server::pull_handler
495 ///////////////////////////////////////////////////////////////////////////
496 (
497 const std::shared_ptr<Session> session,
498 const std::error_code error,
499 const size_t bytes_transferred
500 )
501 {
502 if (!error)
503 {
504 session->buffer.index = 1;
505 session->pull_checkpoint = session->buffer.read<int64_t>();
506 const std::chrono::milliseconds wait{session->buffer.read<int64_t>()};
507
508 if (!client_lock) // todo: deep-share option
509 client.pull(); // ??? takes_time
510
511 if (wait.count() > 0 && session->pull_checkpoint == client.get_checkpoint())
512 {
513 LOGID
514 (
515 "waiting at checkpoint = " << session->pull_checkpoint <<
516 " for " << wait.count() << " milliseconds\n"
517 );
518
519 session->state = Session::State::waiting_for_push_to_pull;
520 session->pull_timer.emplace(io_context);
521 session->pull_timer->expires_after(wait);
522 session->pull_timer->async_wait
523 (
524 [this, session](std::error_code timer_error)
525 {
526 if (!timer_error)
527 {
528 if (session->state == Session::State::waiting_for_push_to_pull)
529 {
530 session->state = Session::State::not_locking;
531 start_pulling(session);
532 }
533 }
534 }
535 );
536 }
537 else
538 start_pulling(session);
539 }
540 }
541
542 ///////////////////////////////////////////////////////////////////////////
543 void Server::pull
544 ///////////////////////////////////////////////////////////////////////////
545 (
546 const std::shared_ptr<Session> session,
547 bool lock,
548 bool send
549 )
550 {
551 session->lock_before_pulling = lock;
552 session->send_pull_data = send;
553 async_read(session, 1, 16, &Server::pull_handler);
554 }
555
556 ///////////////////////////////////////////////////////////////////////////
557 void Server::read_handler
558 ///////////////////////////////////////////////////////////////////////////
559 (
560 std::shared_ptr<Session> session,
561 std::error_code error,
562 size_t bytes_transferred
563 )
564 {
565 if (!error)
566 {
567 session->buffer.index = 1;
568 int64_t offset = session->buffer.read<int64_t>();
569 int64_t size = session->buffer.read<int64_t>();
570 int64_t until = offset + size;
571
572 if (until > client.get_checkpoint())
573 until = client.get_checkpoint();
574 if (offset > until)
575 offset = until;
576
577 const Async_Reader reader = client.get_journal().get_async_reader
578 (
579 offset,
580 until
581 );
582
583 session->buffer.index = 1;
584 start_reading(session, reader);
585 }
586 }
587
588 ///////////////////////////////////////////////////////////////////////////
589 void Server::read_blob_handler
590 ///////////////////////////////////////////////////////////////////////////
591 (
592 std::shared_ptr<Session> session,
593 std::error_code error,
594 size_t bytes_transferred
595 )
596 {
597 if (!error)
598 {
599 session->buffer.index = 1;
600 const int64_t blob_position = session->buffer.read<int64_t>();
601 const Async_Reader reader = client.get_journal().get_async_blob_reader
602 (
603 Blob(blob_position)
604 );
605
606 session->buffer.index = 1;
607 start_reading(session, reader);
608 }
609 }
610
611 ///////////////////////////////////////////////////////////////////////////
612 void Server::check_hash_handler
613 ///////////////////////////////////////////////////////////////////////////
614 (
615 const std::shared_ptr<Session> session,
616 const std::error_code error,
617 const size_t bytes_transferred
618 )
619 {
620 if (!error)
621 {
622 session->buffer.index = 1;
623 const auto checkpoint = session->buffer.read<int64_t>();
624 const auto hash = session->buffer.read<SHA_256::Hash>();
625
626 const Readonly_Journal &readonly_journal = client.get_journal();
627
628 if
629 (
630 checkpoint > readonly_journal.get_checkpoint_position() ||
631 Journal_Hasher::get_hash(readonly_journal, checkpoint) != hash // ??? takes_time
632 )
633 {
634 session->buffer.data[0] = 'h';
635 }
636
637 LOGID("hash for checkpoint = " << checkpoint << ", result = "
638 << session->buffer.data[0] << '\n');
639
640 write_buffer_and_next_command(session, 1);
641 }
642 }
643
644 ///////////////////////////////////////////////////////////////////////////
645 void Server::read_command_handler
646 ///////////////////////////////////////////////////////////////////////////
647 (
648 const std::shared_ptr<Session> session,
649 const std::error_code error,
650 const size_t bytes_transferred
651 )
652 {
653 if (!error)
654 {
655 LOGID(session->buffer.data[0] << '\n');
656
657 switch (session->buffer.data[0])
658 {
659 case 'P':
660 pull(session, false, true);
661 break;
662
663 case 'L':
664 pull(session, true, true);
665 break;
666
667 case 'i':
668 pull(session, false, false);
669 break;
670
671 case 'l':
672 pull(session, true, false);
673 break;
674
675 case 'U': case 'p':
676 session->unlock_after_push = (session->buffer.data[0] == 'U');
677 async_read(session, 0, 16, &Server::push_handler);
678 break;
679
680 case 'u':
681 if (session->state == Session::State::locking)
682 unlock(*session);
683 else
684 session->buffer.data[0] = 't';
685 write_buffer_and_next_command(session, 1);
686 break;
687
688 case 'H':
689 async_read(session, 1, 40, &Server::check_hash_handler);
690 break;
691
692 case 'r':
693 async_read(session, 1, 16, &Server::read_handler);
694 break;
695
696 case 'b':
697 async_read(session, 1, 8, &Server::read_blob_handler);
698 break;
699
700 default:
701 LOGID("unexpected command\n");
702 break;
703 }
704 }
705 }
706
707 ///////////////////////////////////////////////////////////////////////////
708 void Server::read_command(const std::shared_ptr<Session> session)
709 ///////////////////////////////////////////////////////////////////////////
710 {
711 async_read(session, 0, 1, &Server::read_command_handler);
712 }
713
714 ////////////////////////////////////////////////////////////////////////////
715 void Server::write_buffer_and_next_command
716 ////////////////////////////////////////////////////////////////////////////
717 (
718 const std::shared_ptr<Session> session,
719 const size_t size
720 )
721 {
722 asio::async_write
723 (
724 session->socket,
725 asio::buffer(session->buffer.data, size),
726 [this, session](std::error_code e, size_t s)
727 {
728 if (!e)
729 read_command(session);
730 }
731 );
732 }
733
734 ///////////////////////////////////////////////////////////////////////////
735 void Server::handshake_handler
736 ///////////////////////////////////////////////////////////////////////////
737 (
738 const std::shared_ptr<Session> session,
739 const std::error_code error,
740 const size_t bytes_transferred
741 )
742 {
743 if (!error)
744 {
745 session->buffer.index = 0;
746
747 if
748 (
749 session->buffer.read<char>() == 'j' &&
750 session->buffer.read<char>() == 'o' &&
751 session->buffer.read<char>() == 'e' &&
752 session->buffer.read<char>() == 'd' &&
753 session->buffer.read<char>() == 'b'
754 )
755 {
756 const int64_t client_version = session->buffer.read<int64_t>();
757 LOGID("client_version = " << client_version << '\n');
758
759 session->buffer.index = 5;
760 session->buffer.write<int64_t>(client_version < protocol_version ? 0 : protocol_version);
761 session->buffer.write<int64_t>(session->id);
762 session->buffer.write<int64_t>(client.get_checkpoint());
763 session->buffer.write<char>(is_readonly() ? 'R' : 'W');
764
765 write_buffer_and_next_command(session, session->buffer.index);
766 return;
767 }
768
769 LOGID("bad handshake\n");
770 }
771 }
772
773 ////////////////////////////////////////////////////////////////////////////
774 void Server::handle_accept
775 ////////////////////////////////////////////////////////////////////////////
776 (
777 const std::error_code error,
778 asio::ip::tcp::socket socket
779 )
780 {
781 if (!error && !stopped)
782 {
783 socket.set_option(asio::ip::tcp::no_delay(true));
784 std::shared_ptr<Session> session(new Session(*this, std::move(socket)));
785 async_read(session, 0, 13, &Server::handshake_handler);
786
787 start_accept();
788 }
789 }
790
791 ////////////////////////////////////////////////////////////////////////////
792 void Server::start_accept()
793 ////////////////////////////////////////////////////////////////////////////
794 {
795 if (!stopped)
796 {
797 acceptor.async_accept
798 (
799 io_context,
800 [this](std::error_code error, asio::ip::tcp::socket socket)
801 {
802 handle_accept(error, std::move(socket));
803 }
804 );
805 }
806 }
807
808 ////////////////////////////////////////////////////////////////////////////
810 ////////////////////////////////////////////////////////////////////////////
811 (
812 Client &client,
813 const bool share_client,
814 asio::io_context &io_context,
815 const uint16_t port,
816 const std::chrono::milliseconds lock_timeout,
817 std::ostream * const log_pointer
818 ):
819 start_time(std::chrono::steady_clock::now()),
820 client(client),
821 push_client(dynamic_cast<Writable_Journal_Client*>(&client)),
822 share_client(share_client),
823 io_context(io_context),
824 acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
825 port(acceptor.local_endpoint().port()),
826 stopped(true),
827 interrupt_signals(io_context, SIGINT, SIGTERM),
828 session_id(0),
829 lock_timeout(lock_timeout),
830 lock_timeout_timer(io_context),
831 locked(false),
832 log_pointer(log_pointer)
833 {
834 if (push_client)
835 push_client->push_unlock();
836
837 start();
838 }
839
840 ////////////////////////////////////////////////////////////////////////////
842 ////////////////////////////////////////////////////////////////////////////
843 {
844 if (stopped)
845 {
846 stopped = false;
847
848 if (!share_client && !is_readonly())
849 {
850 JOEDB_ASSERT(push_client);
851 client_lock.emplace(*push_client);
852 }
853 else
854 client.pull();
855
856 interrupt_signals.async_wait([this](const asio::error_code &error, int)
857 {
858 if (!error)
859 stop();
860 });
861
862 start_accept();
863
864 // Note: C++20 has operator<< for durations
865 LOG
866 (
867 port <<
868 ": start. lock_timeout = " << lock_timeout.count() <<
869 "; protocol_version = " << protocol_version << '\n'
870 );
871 write_status();
872 }
873 }
874
875 ////////////////////////////////////////////////////////////////////////////
877 ////////////////////////////////////////////////////////////////////////////
878 {
879 acceptor.cancel();
880 interrupt_signals.cancel();
881 stopped = true;
882 }
883
884 ////////////////////////////////////////////////////////////////////////////
886 ////////////////////////////////////////////////////////////////////////////
887 {
888 if (!stopped)
889 {
890 LOG(port << ": stop\n");
891
892 for (Session *session: sessions)
893 {
894 session->socket.close();
895 session->pull_timer.reset();
896 }
897
898 if (client_lock)
899 {
900 client_lock->unlock();
901 client_lock.reset();
902 }
903
905 }
906 }
907
908 ////////////////////////////////////////////////////////////////////////////
910 ////////////////////////////////////////////////////////////////////////////
911 {
912 try
913 {
914 if (!sessions.empty())
915 {
916 LOG("Bug: destroying server before sessions.\n");
917 }
918 }
919 catch (...)
920 {
921 }
922 }
923}
924
925#undef LOGID
926#undef LOG
#define LOG(x)
Definition Server.cpp:8
#define LOGID(x)
Definition Server.cpp:9
Handle concurrent access to a file with a joedb::Connection.
Definition Client.h:12
int64_t get_checkpoint() const
Definition Client.h:101
int64_t pull(std::chrono::milliseconds wait=std::chrono::milliseconds(0))
Definition Client.h:123
const Readonly_Journal & get_journal() const
Definition Client.h:96
void push_unlock()
Definition Client.h:143
static SHA_256::Hash get_hash(const Readonly_Journal &journal, int64_t checkpoint)
Async_Reader get_async_tail_reader(int64_t start_position) const
std::array< uint32_t, 8 > Hash
Definition SHA_256.h:60
void stop()
Definition Server.cpp:885
void start()
Definition Server.cpp:841
bool is_readonly() const
Definition Server.h:221
void stop_after_sessions()
Definition Server.cpp:876
std::chrono::milliseconds get_time_stamp() const
Definition Server.cpp:17
Server(Client &client, bool share_client, asio::io_context &io_context, uint16_t port, std::chrono::milliseconds lock_timeout, std::ostream *log_pointer)
Definition Server.cpp:811
constexpr int protocol_version
#define JOEDB_ASSERT(x)
Definition assert.h:18
std::string get_time_string_of_now()
Definition Blob.h:7