-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sql partition aware routing[API-1862] #1167
Merged
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
9187896
sql aware draft
akeles85 6337517
sql_result update
akeles85 d24577d
LRU Cache Interface
akeles85 9918fc8
std::priority_queue used
akeles85 8a37050
sql service implementation
akeles85 65fcca8
revert unused file
akeles85 0af4c06
clang format
akeles85 ca96b11
LRU Test added
akeles85 f6d5967
LRU Test added
akeles85 c4e2c7e
basic type tests
akeles85 65c9ede
unit tests for complex types
akeles85 d7c8736
test case update
akeles85 d171c8d
test added for routing
akeles85 981f313
clang format
akeles85 cc41570
3 members for sql test
akeles85 7b6df81
Merge branch 'sql_partition_aware' into concurrent_map
akeles85 5a5734c
Merge pull request #6 from akeles85/concurrent_map
akeles85 8062180
cluster version check added
akeles85 1d20dbd
test case failure fix
akeles85 72469a9
member3_ reset
akeles85 6a1f05d
additional test cases
akeles85 89fc51f
statement partition arg index set
akeles85 7279125
tdd review fix
akeles85 37a24e6
clang format
akeles85 bcea0c0
comments added
akeles85 c27da7a
clang format
akeles85 236bdaf
Merge branch 'hazelcast:master' into sql_partition_aware
akeles85 db360cc
jenkins fix
akeles85 0db8e4c
Merge branch 'sql_partition_aware' of github.com:akeles85/hazelcast-c…
akeles85 b3d6a57
compiler error fix for jenkins
akeles85 7aad67f
jenkins linux compiler error
akeles85 ccfe2d5
read_optimized_lru_cache removed from public API
akeles85 150f63c
code review fixes
akeles85 8d3080c
initial value of lock
akeles85 4143e37
rename variable
akeles85 f63e7c1
clang format
akeles85 142c5a3
Merge branch 'master' into sql_partition_aware
akeles85 cdb70d0
compiler error fixed
akeles85 0ae6fb0
windows bat file is updated
akeles85 File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
203 changes: 203 additions & 0 deletions
203
hazelcast/include/hazelcast/client/sql/impl/read_optimized_lru_cache.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
/* | ||
* Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
#pragma once | ||
|
||
#include <queue> | ||
#include "hazelcast/util/SynchronizedMap.h" | ||
#include "hazelcast/util/export.h" | ||
|
||
namespace hazelcast { | ||
namespace client { | ||
namespace sql { | ||
namespace impl { | ||
|
||
/** | ||
* Implementation of an LRU cache optimized for read-heavy use cases. | ||
* <p> | ||
* It stores the entries in a {SynchronizedMap}, along with the last | ||
* access time. It allows the size to grow beyond the capacity, up to | ||
* `cleanup_threshold_`, at which point the inserting thread will remove a batch | ||
* of the eldest items in two passes. | ||
* <p> | ||
* The cleanup process isn't synchronized to guarantee that the capacity is not | ||
* exceeded. The cache is available during the cleanup for reads and writes. If | ||
* there's a large number of writes by many threads, the one thread doing the | ||
* cleanup might not be quick enough and there's no upper bound on the actual | ||
* size of the cache. This is done to optimize the happy path when the keys fit | ||
* into the cache. | ||
*/ | ||
template<typename K, typename V> | ||
class read_optimized_lru_cache | ||
{ | ||
public: | ||
/** | ||
* @param capacity Capacity of the cache | ||
* @param cleanup_threshold The size at which the cache will clean up oldest | ||
* entries in batch. `cleanup_threshold - capacity` entries will be | ||
* removed | ||
* @throws exception::illegal_argument if capacity is smaller or equal to 0, | ||
* or if the cleanup_threshold is smaller than capacity | ||
*/ | ||
explicit read_optimized_lru_cache(const uint32_t capacity, | ||
const uint32_t cleanup_threshold) | ||
{ | ||
if (capacity == 0) { | ||
BOOST_THROW_EXCEPTION( | ||
client::exception::illegal_argument("capacity == 0")); | ||
} | ||
if (cleanup_threshold <= capacity) { | ||
BOOST_THROW_EXCEPTION(client::exception::illegal_argument( | ||
"cleanupThreshold <= capacity")); | ||
} | ||
|
||
capacity_ = capacity; | ||
cleanup_threshold_ = cleanup_threshold; | ||
} | ||
|
||
/** | ||
* @param key the key of the cache entry | ||
* @param default_value the default value if the key is not cached. | ||
* @returns Returns the value to which the specified key is cached, | ||
* or default value if this cache contains no mapping for the key. | ||
*/ | ||
std::shared_ptr<V> get_or_default(const K& key, | ||
const std::shared_ptr<V>& default_value) | ||
{ | ||
const auto existing_value = get(key); | ||
return (existing_value != nullptr) ? existing_value : default_value; | ||
} | ||
|
||
/** | ||
* @param key the key of the cache entry | ||
* Returns the value to which the specified key is cached, | ||
* or {@code null} if this cache contains no mapping for the key. | ||
* @returns Returns the value to which the specified key is cached | ||
*/ | ||
std::shared_ptr<V> get(const K& key) | ||
{ | ||
auto value_from_cache = cache_.get(key); | ||
if (value_from_cache == nullptr) { | ||
return nullptr; | ||
} | ||
value_from_cache->touch(); | ||
return std::make_shared<int32_t>(value_from_cache->value_); | ||
} | ||
|
||
/** | ||
* @param key the key of the cache entry | ||
* @param value the value of the cache entry | ||
* @throws exception::illegal_argument if the value equals to nullptr | ||
*/ | ||
void put(const K& key, const std::shared_ptr<V>& value) | ||
{ | ||
if (value == nullptr) { | ||
BOOST_THROW_EXCEPTION(client::exception::illegal_argument( | ||
"Null values are disallowed")); | ||
} | ||
|
||
auto old_value = | ||
cache_.put(key, std::make_shared<value_and_timestamp<V>>(*value)); | ||
if (old_value == nullptr && cache_.size() > cleanup_threshold_) { | ||
do_cleanup(); | ||
} | ||
} | ||
|
||
/** | ||
* @param key the key of the cache entry | ||
* Removes the cached value for the given key | ||
*/ | ||
void remove(const K& key) { cache_.remove(key); } | ||
|
||
protected: | ||
/** | ||
* Helper class to hold the value with timestamp. | ||
*/ | ||
template<typename T> | ||
class value_and_timestamp | ||
{ | ||
public: | ||
const T value_; | ||
int64_t timestamp_; | ||
ihsandemir marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
value_and_timestamp(T value) | ||
: value_(value) | ||
{ | ||
touch(); | ||
} | ||
|
||
void touch() { timestamp_ = util::current_time_nanos(); } | ||
}; | ||
|
||
util::SynchronizedMap<K, value_and_timestamp<V>> cache_; | ||
|
||
private: | ||
/** | ||
* Cleans the cache | ||
*/ | ||
void do_cleanup() | ||
{ | ||
bool expected = false; | ||
// if no thread is cleaning up, we'll do it | ||
if (!cleanup_lock_.compare_exchange_strong(expected, true)) { | ||
return; | ||
} | ||
|
||
util::finally release_lock( | ||
[this]() { this->cleanup_lock_.store(false); }); | ||
|
||
if (capacity_ >= cache_.size()) { | ||
// this can happen if the cache is concurrently modified | ||
return; | ||
} | ||
auto entries_to_remove = cache_.size() - capacity_; | ||
|
||
/*max heap*/ | ||
std::priority_queue<int64_t> oldest_timestamps; | ||
|
||
// 1st pass | ||
const auto values = cache_.values(); | ||
for (const auto& value_and_timestamp : values) { | ||
oldest_timestamps.push(value_and_timestamp->timestamp_); | ||
if (oldest_timestamps.size() > entries_to_remove) { | ||
oldest_timestamps.pop(); | ||
} | ||
} | ||
|
||
// find out the highest value in the queue - the value, below which | ||
// entries will be removed | ||
if (oldest_timestamps.empty()) { | ||
// this can happen if the cache is concurrently modified | ||
return; | ||
} | ||
int64_t remove_threshold = oldest_timestamps.top(); | ||
oldest_timestamps.pop(); | ||
|
||
// 2nd pass | ||
cache_.remove_values_if( | ||
[remove_threshold](const value_and_timestamp<V>& v) -> bool { | ||
return (v.timestamp_ <= remove_threshold); | ||
}); | ||
} | ||
|
||
std::atomic<bool> cleanup_lock_{ false }; | ||
uint32_t capacity_; | ||
uint32_t cleanup_threshold_; | ||
}; | ||
|
||
} // namespace impl | ||
} // namespace sql | ||
} // namespace client | ||
} // namespace hazelcast |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not return
optional
which makes small value optimizations for you?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it to be consistent with SynchronizedMap, so I prefer to leave it as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may have another version of synchronized map to work with optional, what do you think? They are very similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Data is stored in synchronized map, and we just increment the usage count of the shared_ptr with get (which is done via atomic instruction). If we use optional, wouldn't it be expensive, because this time we will copy the data for storing it in optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this specific case, the value is an (int32 + int64_t) struct (
std::shared_ptr<impl::read_optimized_lru_cache<std::string, int32_t>> partition_argument_index_cache_;
) hence, copy would not be a big problem for this specific case. We have no other use of read_optimized_lru_cache structure any other places and have no such concern at the moment. Hence, I thought that it would have been better than using shared_ptr.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you are right, I have changed the implementation. (#1188)