25#include "AttachmentPool.h"
35using namespace fbcpp::impl;
38static std::chrono::steady_clock::time_point steadyNow()
40 return std::chrono::steady_clock::now();
43static std::unique_ptr<AttachmentPoolEntry> extractEntry(
44 std::vector<std::unique_ptr<AttachmentPoolEntry>>& entries, AttachmentPoolEntry* entry)
46 const auto it = std::find_if(
47 entries.begin(), entries.end(), [entry](
const auto& candidate) { return candidate.get() == entry; });
49 if (it == entries.end())
52 auto owned = std::move(*it);
67 "AttachmentPoolOptions::setAttachmentOptions must not enable AttachmentOptions::setCreateDatabase");
71 throw FbCppException(
"AttachmentPoolOptions::setMaxSize must be at least 1");
74 throw FbCppException(
"AttachmentPoolOptions::setMinSize must not be greater than setMaxSize");
76 for (std::size_t i = 0u; i < options.
getMinSize(); ++i)
78 auto entry = std::make_unique<AttachmentPoolEntry>();
80 entry->createdAt = entry->lastReturnedAt = steadyNow();
82 available.push_back(entry.get());
83 entries.push_back(std::move(entry));
89 assert(inUse == 0u && pending == 0u);
103 std::lock_guard mutexGuard{mutex};
104 return entries.size();
109 std::lock_guard mutexGuard{mutex};
110 return available.size();
115 std::lock_guard mutexGuard{mutex};
121 auto lease = acquireImpl(
true);
122 assert(lease.has_value());
123 return std::move(*lease);
128 return acquireImpl(
false);
133 std::unique_lock mutexGuard{mutex};
136 while (!available.empty())
138 auto* entry = available.front();
139 available.pop_front();
141 auto owned = extractEntry(entries, entry);
149std::optional<PooledAttachment> AttachmentPool::acquireImpl(
bool throwOnFailure)
151 std::unique_lock mutexGuard{mutex};
165 evictLocked(mutexGuard);
167 if (!available.empty())
169 auto* entry = available.front();
170 available.pop_front();
181 entry->attachment->ping();
193 auto owned = extractEntry(entries, entry);
206 if (entries.size() + pending < options.
getMaxSize())
211 std::unique_ptr<AttachmentPoolEntry> newEntry;
212 std::exception_ptr failure;
216 newEntry = std::make_unique<AttachmentPoolEntry>();
217 newEntry->attachment = std::make_unique<Attachment>(*client, uri, options.
getAttachmentOptions());
218 newEntry->createdAt = newEntry->lastReturnedAt = steadyNow();
222 failure = std::current_exception();
230 availableCondition.notify_one();
233 std::rethrow_exception(failure);
238 auto* entryPtr = newEntry.get();
239 entries.push_back(std::move(newEntry));
245 const auto remaining = deadline - steadyNow();
247 if (remaining <= std::chrono::steady_clock::duration::zero() ||
248 availableCondition.wait_for(mutexGuard, remaining) == std::cv_status::timeout)
251 throw FbCppException(
"Timed out waiting for an available connection from AttachmentPool");
258void AttachmentPool::evictLocked(std::unique_lock<std::mutex>& lock)
260 assert(lock.owns_lock());
262 const auto currentTime = steadyNow();
263 std::vector<std::unique_ptr<AttachmentPoolEntry>> toDestroy;
265 for (
auto it = available.begin(); it != available.end();)
278 it = available.erase(it);
280 if (
auto owned = extractEntry(entries, entry))
281 toDestroy.push_back(std::move(owned));
287 if (!toDestroy.empty())
295void AttachmentPool::release(AttachmentPoolEntry& entry)
noexcept
299 bool resetFailed =
false;
301 if (!closed && options.getSessionResetOnRelease() && entry.attachment && entry.attachment->isValid())
305 entry.attachment->resetSession();
313 std::unique_ptr<AttachmentPoolEntry> toDestroy;
316 std::lock_guard mutexGuard{mutex};
321 const bool discard = closed || !entry.attachment || !entry.attachment->isValid() || resetFailed;
324 toDestroy = extractEntry(entries, &entry);
327 entry.lastReturnedAt = steadyNow();
328 available.push_back(&entry);
332 availableCondition.notify_one();
bool getCreateDatabase() const
Returns whether the database should be created instead of connected to.
Represents options used when creating an AttachmentPool object.
const std::optional< std::chrono::milliseconds > & getIdleTimeout() const
Returns the maximum time a connection may stay idle in the pool before being closed.
std::size_t getMaxSize() const
Returns the maximum number of connections the pool may create.
bool getValidateOnAcquire() const
Returns whether a connection is validated (via ping) before being handed out by acquire().
const std::optional< std::chrono::milliseconds > & getMaxLifetime() const
Returns the maximum lifetime of a connection, regardless of usage.
std::chrono::milliseconds getAcquireTimeout() const
Returns how long acquire() waits for a connection to become available before throwing.
const AttachmentOptions & getAttachmentOptions() const
Returns the options used to create each pooled Attachment.
std::size_t getMinSize() const
Returns the minimum number of connections kept warm by the pool.
std::size_t inUseCount()
Returns the number of connections currently leased out by the pool.
PooledAttachment acquire()
Acquires a connection from the pool, creating one if needed and allowed by getMaxSize().
void close()
Closes every available connection and marks the pool as closed.
std::size_t availableCount()
Returns the number of connections currently available (not leased) in the pool.
~AttachmentPool() noexcept
Closes every idle connection in the pool.
std::size_t size()
Returns the total number of connections currently created by the pool (in use plus available).
std::optional< PooledAttachment > tryAcquire()
Attempts to acquire a connection from the pool without throwing.
AttachmentPool(Client &client, std::string uri, const AttachmentPoolOptions &options={})
Constructs an AttachmentPool that connects to the database specified by the URI using the specified C...
Represents a Firebird client library instance.
Base exception class for all fb-cpp exceptions.
RAII lease for an Attachment borrowed from an AttachmentPool.