From b0f8b28977e59b7fbfc1ce57ee5c102b8e4e0690 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 12 Feb 2023 11:54:05 +0100 Subject: Touch documents on read (i.e. reset timeout on read) --- connectionregistry.cpp | 10 ++++++++++ connectionregistry.h | 5 +++++ debian/changelog | 1 + storage.cpp | 19 +++++++++++++++---- storage.h | 3 +++ tests/test-connectionregistry.cpp | 3 +++ tests/test-storage.cpp | 29 +++++++++++++++++++++++++++++ whiteboard.cpp | 11 +++++++++++ whiteboard.h | 1 + 9 files changed, 78 insertions(+), 4 deletions(-) diff --git a/connectionregistry.cpp b/connectionregistry.cpp index 412472d..e3c0c11 100644 --- a/connectionregistry.cpp +++ b/connectionregistry.cpp @@ -61,6 +61,16 @@ std::unordered_set::iterator ConnectionRegistry: return m_ids.at(id).end(); } +std::unordered_map::iterator ConnectionRegistry::begin() +{ + return m_connections.begin(); +} + +std::unordered_map::iterator ConnectionRegistry::end() +{ + return m_connections.end(); +} + void ConnectionRegistry::dump() const { std::cout << "Connection Registry:" << std::endl; diff --git a/connectionregistry.h b/connectionregistry.h index 25bd3b6..c3c6884 100644 --- a/connectionregistry.h +++ b/connectionregistry.h @@ -22,9 +22,14 @@ public: void addConnection(connection c); void delConnection(connection c); + // iterate over all connections associated with a certain id std::unordered_set::iterator begin(const std::string& id); std::unordered_set::iterator end(const std::string& id); + // iterate over all connections + std::unordered_map::iterator begin(); + std::unordered_map::iterator end(); + void dump() const; size_t number_of_connections() const; diff --git a/debian/changelog b/debian/changelog index 082b17f..41b4ce4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ whiteboard (1.8) UNRELEASED; urgency=medium * Added config.maxconnections, defaults to 1000 * Fixed package dependencies for PDF generation + * Touch documents on read (i.e. reset timeout on read) -- Roland Reichwein Fri, 10 Feb 2023 19:18:16 +0100 diff --git a/storage.cpp b/storage.cpp index c5caa79..6060caf 100644 --- a/storage.cpp +++ b/storage.cpp @@ -30,7 +30,8 @@ Storage::Storage(const Config& config): m_stmt_setCursorPos(m_db, "UPDATE documents SET cursorpos = ? WHERE id = ?"), m_stmt_setRow(m_db, "INSERT OR REPLACE INTO documents (id, value, rev, cursorpos, timestamp) values (?, ?, ?, ?, ?)"), m_stmt_getDbSizeGross(m_db, "SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"), - m_stmt_getDbSizeNet(m_db, "SELECT (page_count - freelist_count) * page_size as size FROM pragma_page_count(), pragma_freelist_count(), pragma_page_size()") + m_stmt_getDbSizeNet(m_db, "SELECT (page_count - freelist_count) * page_size as size FROM pragma_page_count(), pragma_freelist_count(), pragma_page_size()"), + m_stmt_touchDocument(m_db, "UPDATE documents SET timestamp = ? WHERE id = ?") { CompiledSQL::Guard g{m_stmt_create}; m_stmt_create.execute(); @@ -154,7 +155,8 @@ void Storage::setDocument(const std::string& id, const std::string& document) m_stmt_setDocument_new.bind(3, 0); m_stmt_setDocument_new.bind(4, 0); m_stmt_setDocument_new.bind(5, static_cast(unixepoch())); - m_stmt_setDocument_new.execute(); + if (!m_stmt_setDocument_new.execute()) + throw std::runtime_error("Unable to create document with id "s + id); } } @@ -165,7 +167,7 @@ void Storage::setRevision(const std::string& id, int rev) m_stmt_setRevision.bind(2, id); if (!m_stmt_setRevision.execute()) - throw std::runtime_error("Unable to insert row with id "s + id); + throw std::runtime_error("Unable to set revision for id "s + id); } void Storage::setCursorPos(const std::string& id, int cursorPos) @@ -175,7 +177,7 @@ void Storage::setCursorPos(const std::string& id, int cursorPos) m_stmt_setCursorPos.bind(2, id); if (!m_stmt_setCursorPos.execute()) - throw std::runtime_error("Unable to insert row with id "s + id); + throw std::runtime_error("Unable to set cursor position for id "s + id); } void Storage::setRow(const std::string& id, const std::string& document, int rev, int cursorPos) @@ -222,3 +224,12 @@ std::string Storage::generate_id() return "endofcodes"; } +void Storage::touchDocument(const std::string& id) +{ + CompiledSQL::Guard g{m_stmt_touchDocument}; + m_stmt_touchDocument.bind(1, static_cast(unixepoch())); + m_stmt_touchDocument.bind(2, id); + if (!m_stmt_touchDocument.execute()) + throw std::runtime_error("Unable to touch document with id "s + id); +} + diff --git a/storage.h b/storage.h index f997657..131b786 100644 --- a/storage.h +++ b/storage.h @@ -30,6 +30,8 @@ public: void setCursorPos(const std::string& id, int cursorPos); void setRow(const std::string& id, const std::string& document, int rev, int cursorPos); + void touchDocument(const std::string& id); + void cleanup(); std::string generate_id(); @@ -55,6 +57,7 @@ private: CompiledSQL m_stmt_setRow; CompiledSQL m_stmt_getDbSizeGross; CompiledSQL m_stmt_getDbSizeNet; + CompiledSQL m_stmt_touchDocument; }; uint32_t checksum32(const std::string& s); diff --git a/tests/test-connectionregistry.cpp b/tests/test-connectionregistry.cpp index 282f397..dbc2b7e 100644 --- a/tests/test-connectionregistry.cpp +++ b/tests/test-connectionregistry.cpp @@ -114,14 +114,17 @@ TEST_F(ConnectionRegistryTest, test_iterators) EXPECT_THROW(cr.begin(""), std::exception); EXPECT_THROW(cr.end(""), std::exception); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 0); cr.addConnection(c); EXPECT_THROW(cr.begin(""), std::exception); EXPECT_THROW(cr.end(""), std::exception); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 1); cr.setId(c, "id1"); EXPECT_EQ(std::distance(cr.begin("id1"), cr.end("id1")), 1); + EXPECT_EQ(std::distance(cr.begin(), cr.end()), 1); } TEST_F(ConnectionRegistryTest, test_guard) diff --git a/tests/test-storage.cpp b/tests/test-storage.cpp index 51a6058..6239f8f 100644 --- a/tests/test-storage.cpp +++ b/tests/test-storage.cpp @@ -12,6 +12,7 @@ namespace fs = std::filesystem; using namespace Reichwein; +using namespace std::string_literals; namespace { const std::string testConfigFilename{"./test.conf"}; @@ -118,6 +119,16 @@ TEST_F(StorageTest, setDocument) EXPECT_EQ(storage.getDocument("0"), "abc"); } +TEST_F(StorageTest, touchDocument) +{ + Storage storage(*m_config); + EXPECT_THROW(storage.touchDocument("0"), std::exception); + storage.setDocument("0", "abc"); + storage.touchDocument("0"); + EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); + EXPECT_EQ(storage.getDocument("0"), "abc"); +} + TEST_F(StorageTest, setRevision) { Storage storage(*m_config); @@ -126,6 +137,15 @@ TEST_F(StorageTest, setRevision) EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); EXPECT_EQ(storage.getRevision("0"), 123); + + try { + storage.setRevision("1", 123); + FAIL(); + } catch(const std::exception& ex) { + EXPECT_EQ("Unable to set revision for id 1"s, ex.what()); + } catch(...) { + FAIL(); + } } TEST_F(StorageTest, setCursorPos) @@ -136,6 +156,15 @@ TEST_F(StorageTest, setCursorPos) EXPECT_EQ(storage.getNumberOfDocuments(), 1UL); EXPECT_EQ(storage.getCursorPos("0"), 1234); + + try { + storage.setCursorPos("1", 12345); + FAIL(); + } catch(const std::exception& ex) { + EXPECT_EQ("Unable to set cursor position for id 1"s, ex.what()); + } catch(...) { + FAIL(); + } } TEST_F(StorageTest, setRow) diff --git a/whiteboard.cpp b/whiteboard.cpp index 044321b..5424a79 100644 --- a/whiteboard.cpp +++ b/whiteboard.cpp @@ -448,6 +448,16 @@ void Whiteboard::on_accept(boost::system::error_code ec, boost::asio::ip::tcp::s do_accept(); } +// for long running connections, don't timeout them but touch associated ids +// regularly, at cleanup time +void Whiteboard::touch_all_connections() +{ + std::for_each(m_registry.begin(), m_registry.end(), [&](const std::pair& i) + { + m_storage->touchDocument(i.second); + }); +} + // the actual main() for testability int Whiteboard::run(int argc, char* argv[]) { @@ -502,6 +512,7 @@ int Whiteboard::run(int argc, char* argv[]) std::lock_guard lock(m_storage_mutex); if (!m_storage) throw std::runtime_error("Storage not initialized"); + touch_all_connections(); m_storage->cleanup(); storage_cleanup_timer.expires_at(storage_cleanup_timer.expires_at() + boost::asio::chrono::hours(24)); storage_cleanup_timer.async_wait(storage_cleanup_callback); diff --git a/whiteboard.h b/whiteboard.h index 7648bd4..53036ac 100644 --- a/whiteboard.h +++ b/whiteboard.h @@ -30,5 +30,6 @@ private: void do_accept(); void on_accept(boost::system::error_code ec, boost::asio::ip::tcp::socket socket); + void touch_all_connections(); }; -- cgit v1.2.3