Commit ec518d7a7a4574f9cc1e0ccae03a9cd5c0674b3b
1 parent
7be65e95
Load and store createdAt time of stored QoS packets
Also fixes a bug in new interval calculation.
Showing
7 changed files
with
70 additions
and
18 deletions
mqttpacket.cpp
| ... | ... | @@ -434,7 +434,7 @@ void MqttPacket::handleConnect() |
| 434 | 434 | { |
| 435 | 435 | case Mqtt5Properties::WillDelayInterval: |
| 436 | 436 | willpublish.will_delay = readFourBytesToUint32(); |
| 437 | - willpublish.createdAt = std::chrono::steady_clock::now(); | |
| 437 | + willpublish.setCreatedAt(std::chrono::steady_clock::now()); | |
| 438 | 438 | break; |
| 439 | 439 | case Mqtt5Properties::PayloadFormatIndicator: |
| 440 | 440 | willpublish.propertyBuilder->writePayloadFormatIndicator(readByte()); |
| ... | ... | @@ -455,9 +455,8 @@ void MqttPacket::handleConnect() |
| 455 | 455 | } |
| 456 | 456 | case Mqtt5Properties::MessageExpiryInterval: |
| 457 | 457 | { |
| 458 | - willpublish.createdAt = std::chrono::steady_clock::now(); | |
| 459 | - uint32_t expiresAfter = readFourBytesToUint32(); | |
| 460 | - willpublish.expiresAfter = std::chrono::seconds(expiresAfter); | |
| 458 | + const uint32_t expiresAfter = readFourBytesToUint32(); | |
| 459 | + willpublish.setExpireAfter(expiresAfter); | |
| 461 | 460 | break; |
| 462 | 461 | } |
| 463 | 462 | case Mqtt5Properties::CorrelationData: |
| ... | ... | @@ -827,8 +826,7 @@ void MqttPacket::parsePublishData() |
| 827 | 826 | publishData.propertyBuilder->writePayloadFormatIndicator(readByte()); |
| 828 | 827 | break; |
| 829 | 828 | case Mqtt5Properties::MessageExpiryInterval: |
| 830 | - publishData.createdAt = std::chrono::steady_clock::now(); | |
| 831 | - publishData.expiresAfter = std::chrono::seconds(readFourBytesToUint32()); | |
| 829 | + publishData.setExpireAfter(readFourBytesToUint32()); | |
| 832 | 830 | break; |
| 833 | 831 | case Mqtt5Properties::TopicAlias: |
| 834 | 832 | { |
| ... | ... | @@ -1328,7 +1326,8 @@ bool MqttPacket::containsClientSpecificProperties() const |
| 1328 | 1326 | if (protocolVersion <= ProtocolVersion::Mqtt311 || !publishData.propertyBuilder) |
| 1329 | 1327 | return false; |
| 1330 | 1328 | |
| 1331 | - if (publishData.createdAt.time_since_epoch().count() == 0 || this->hasTopicAlias) // TODO: better | |
| 1329 | + // TODO: for the on-line clients, even with expire info, we can just copy the same packet. So, that case can be excluded. | |
| 1330 | + if (publishData.getHasExpireInfo() || this->hasTopicAlias) | |
| 1332 | 1331 | { |
| 1333 | 1332 | return true; |
| 1334 | 1333 | } | ... | ... |
sessionsandsubscriptionsdb.cpp
| ... | ... | @@ -18,6 +18,7 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. |
| 18 | 18 | #include "sessionsandsubscriptionsdb.h" |
| 19 | 19 | #include "mqttpacket.h" |
| 20 | 20 | #include "threadglobals.h" |
| 21 | +#include "utils.h" | |
| 21 | 22 | |
| 22 | 23 | #include "cassert" |
| 23 | 24 | |
| ... | ... | @@ -113,6 +114,7 @@ SessionsAndSubscriptionsResult SessionsAndSubscriptionsDB::readDataV2() |
| 113 | 114 | { |
| 114 | 115 | const uint16_t fixed_header_length = readUint16(eofFound); |
| 115 | 116 | const uint16_t id = readUint16(eofFound); |
| 117 | + const uint32_t originalPubAge = readUint32(eofFound); | |
| 116 | 118 | const uint32_t packlen = readUint32(eofFound); |
| 117 | 119 | |
| 118 | 120 | assert(id > 0); |
| ... | ... | @@ -127,7 +129,8 @@ SessionsAndSubscriptionsResult SessionsAndSubscriptionsDB::readDataV2() |
| 127 | 129 | pack.parsePublishData(); |
| 128 | 130 | Publish pub(pack.getPublishData()); |
| 129 | 131 | |
| 130 | - // TODO: update the pub.createdAt | |
| 132 | + const uint32_t newPubAge = persistence_state_age + originalPubAge; | |
| 133 | + pub.setCreatedAt(timepointFromAge(newPubAge)); | |
| 131 | 134 | |
| 132 | 135 | logger->logf(LOG_DEBUG, "Loaded QoS %d message for topic '%s'.", pub.qos, pub.topic.c_str()); |
| 133 | 136 | ses->qosPacketQueue.queuePublish(std::move(pub), id); |
| ... | ... | @@ -250,8 +253,6 @@ void SessionsAndSubscriptionsDB::saveData(const std::vector<std::unique_ptr<Sess |
| 250 | 253 | |
| 251 | 254 | logger->logf(LOG_DEBUG, "Saving QoS %d message for topic '%s'.", pub.qos, pub.topic.c_str()); |
| 252 | 255 | |
| 253 | - pub.clearClientSpecificProperties(); // TODO: unnecessary? Unwanted even? I need to store the expiration interval. And how to load it? | |
| 254 | - | |
| 255 | 256 | MqttPacket pack(ProtocolVersion::Mqtt5, pub); |
| 256 | 257 | pack.setPacketId(p.getPacketId()); |
| 257 | 258 | const uint32_t packSize = pack.getSizeIncludingNonPresentHeader(); |
| ... | ... | @@ -259,10 +260,11 @@ void SessionsAndSubscriptionsDB::saveData(const std::vector<std::unique_ptr<Sess |
| 259 | 260 | cirbuf.ensureFreeSpace(packSize + 32); |
| 260 | 261 | pack.readIntoBuf(cirbuf); |
| 261 | 262 | |
| 262 | - // TODO: save age | |
| 263 | + const uint32_t pubAge = ageFromTimePoint(pub.getCreatedAt()); | |
| 263 | 264 | |
| 264 | 265 | writeUint16(pack.getFixedHeaderLength()); |
| 265 | 266 | writeUint16(p.getPacketId()); |
| 267 | + writeUint32(pubAge); | |
| 266 | 268 | writeUint32(packSize); |
| 267 | 269 | writeCheck(cirbuf.tailPtr(), 1, cirbuf.usedBytes(), f); |
| 268 | 270 | } | ... | ... |
subscriptionstore.cpp
| ... | ... | @@ -292,7 +292,7 @@ void SubscriptionStore::sendQueuedWillMessages() |
| 292 | 292 | std::shared_ptr<Publish> p = (*it).lock(); |
| 293 | 293 | if (p) |
| 294 | 294 | { |
| 295 | - if (p->createdAt + std::chrono::seconds(p->will_delay) > now) | |
| 295 | + if (p->getCreatedAt() + std::chrono::seconds(p->will_delay) > now) | |
| 296 | 296 | break; |
| 297 | 297 | |
| 298 | 298 | logger->logf(LOG_DEBUG, "Sending delayed will on topic '%s'.", p->topic.c_str() ); | ... | ... |
types.cpp
| ... | ... | @@ -138,7 +138,7 @@ size_t PublishBase::getLengthWithoutFixedHeader() const |
| 138 | 138 | */ |
| 139 | 139 | void PublishBase::setClientSpecificProperties() |
| 140 | 140 | { |
| 141 | - if (this->createdAt.time_since_epoch().count() && this->topicAlias == 0) | |
| 141 | + if (!hasExpireInfo && this->topicAlias == 0) | |
| 142 | 142 | return; |
| 143 | 143 | |
| 144 | 144 | if (propertyBuilder) |
| ... | ... | @@ -146,11 +146,13 @@ void PublishBase::setClientSpecificProperties() |
| 146 | 146 | else |
| 147 | 147 | propertyBuilder = std::make_shared<Mqtt5PropertyBuilder>(); |
| 148 | 148 | |
| 149 | - if (createdAt.time_since_epoch().count() > 0) | |
| 149 | + if (hasExpireInfo) | |
| 150 | 150 | { |
| 151 | 151 | auto now = std::chrono::steady_clock::now(); |
| 152 | - std::chrono::seconds newExpiresAfter = std::chrono::duration_cast<std::chrono::seconds>(now - createdAt); | |
| 153 | - propertyBuilder->writeMessageExpiryInterval(newExpiresAfter.count()); | |
| 152 | + std::chrono::seconds delay = std::chrono::duration_cast<std::chrono::seconds>(now - createdAt); | |
| 153 | + int32_t newExpire = (this->expiresAfter - delay).count(); | |
| 154 | + if (newExpire > 0) | |
| 155 | + propertyBuilder->writeMessageExpiryInterval(newExpire); | |
| 154 | 156 | } |
| 155 | 157 | |
| 156 | 158 | if (topicAlias > 0) |
| ... | ... | @@ -178,10 +180,35 @@ bool PublishBase::hasUserProperties() const |
| 178 | 180 | |
| 179 | 181 | bool PublishBase::hasExpired() const |
| 180 | 182 | { |
| 183 | + if (!hasExpireInfo) | |
| 184 | + return false; | |
| 185 | + | |
| 181 | 186 | const std::chrono::seconds age = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - this->createdAt); |
| 182 | 187 | return (expiresAfter > age); |
| 183 | 188 | } |
| 184 | 189 | |
| 190 | +void PublishBase::setCreatedAt(std::chrono::time_point<std::chrono::steady_clock> t) | |
| 191 | +{ | |
| 192 | + this->createdAt = t; | |
| 193 | +} | |
| 194 | + | |
| 195 | +void PublishBase::setExpireAfter(uint32_t s) | |
| 196 | +{ | |
| 197 | + this->createdAt = std::chrono::steady_clock::now(); | |
| 198 | + this->expiresAfter = std::chrono::seconds(s); | |
| 199 | + this->hasExpireInfo = true; | |
| 200 | +} | |
| 201 | + | |
| 202 | +bool PublishBase::getHasExpireInfo() const | |
| 203 | +{ | |
| 204 | + return this->hasExpireInfo; | |
| 205 | +} | |
| 206 | + | |
| 207 | +const std::chrono::time_point<std::chrono::steady_clock> PublishBase::getCreatedAt() const | |
| 208 | +{ | |
| 209 | + return this->createdAt; | |
| 210 | +} | |
| 211 | + | |
| 185 | 212 | Publish::Publish(const Publish &other) : |
| 186 | 213 | PublishBase(other) |
| 187 | 214 | { | ... | ... |
types.h
| ... | ... | @@ -192,6 +192,10 @@ public: |
| 192 | 192 | */ |
| 193 | 193 | class PublishBase |
| 194 | 194 | { |
| 195 | + bool hasExpireInfo = false; | |
| 196 | + std::chrono::time_point<std::chrono::steady_clock> createdAt; | |
| 197 | + std::chrono::seconds expiresAfter; | |
| 198 | + | |
| 195 | 199 | public: |
| 196 | 200 | std::string topic; |
| 197 | 201 | std::string payload; |
| ... | ... | @@ -199,8 +203,6 @@ public: |
| 199 | 203 | bool retain = false; // Note: existing subscribers don't get publishes of retained messages with retain=1. [MQTT-3.3.1-9] |
| 200 | 204 | uint32_t will_delay = 0; // if will, this is the delay. Just storing here, to avoid having to make a WillMessage class |
| 201 | 205 | bool splitTopic = true; |
| 202 | - std::chrono::time_point<std::chrono::steady_clock> createdAt; | |
| 203 | - std::chrono::seconds expiresAfter; | |
| 204 | 206 | uint16_t topicAlias = 0; |
| 205 | 207 | bool skipTopic = false; |
| 206 | 208 | std::shared_ptr<Mqtt5PropertyBuilder> propertyBuilder; // Only contains data for sending, not receiving |
| ... | ... | @@ -213,6 +215,11 @@ public: |
| 213 | 215 | void constructPropertyBuilder(); |
| 214 | 216 | bool hasUserProperties() const; |
| 215 | 217 | bool hasExpired() const; |
| 218 | + | |
| 219 | + void setCreatedAt(std::chrono::time_point<std::chrono::steady_clock> t); | |
| 220 | + void setExpireAfter(uint32_t s); | |
| 221 | + bool getHasExpireInfo() const; | |
| 222 | + const std::chrono::time_point<std::chrono::steady_clock> getCreatedAt() const; | |
| 216 | 223 | }; |
| 217 | 224 | |
| 218 | 225 | class Publish : public PublishBase | ... | ... |
utils.cpp
| ... | ... | @@ -668,3 +668,17 @@ const std::string protocolVersionString(ProtocolVersion p) |
| 668 | 668 | return "unknown"; |
| 669 | 669 | } |
| 670 | 670 | } |
| 671 | + | |
| 672 | +uint32_t ageFromTimePoint(const std::chrono::time_point<std::chrono::steady_clock> &point) | |
| 673 | +{ | |
| 674 | + auto duration = std::chrono::steady_clock::now() - point; | |
| 675 | + auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration); | |
| 676 | + return seconds.count(); | |
| 677 | +} | |
| 678 | + | |
| 679 | +std::chrono::time_point<std::chrono::steady_clock> timepointFromAge(const uint32_t age) | |
| 680 | +{ | |
| 681 | + std::chrono::seconds seconds(age); | |
| 682 | + std::chrono::time_point<std::chrono::steady_clock> newPoint = std::chrono::steady_clock::now() + seconds; | |
| 683 | + return newPoint; | |
| 684 | +} | ... | ... |
utils.h
| ... | ... | @@ -127,5 +127,8 @@ const std::string websocketCloseCodeToString(uint16_t code); |
| 127 | 127 | |
| 128 | 128 | const std::string protocolVersionString(ProtocolVersion p); |
| 129 | 129 | |
| 130 | +uint32_t ageFromTimePoint(const std::chrono::time_point<std::chrono::steady_clock> &point); | |
| 131 | +std::chrono::time_point<std::chrono::steady_clock> timepointFromAge(const uint32_t age); | |
| 132 | + | |
| 130 | 133 | |
| 131 | 134 | #endif // UTILS_H | ... | ... |