1
0
Fork 0

Bug fix/internal issue #605 (#9405)

* fix recovery

* update iresearch

* more fixes

* address review comments
This commit is contained in:
Andrey Abramov 2019-07-05 13:14:37 +03:00 committed by GitHub
parent 54169cd44f
commit 595728ec1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1103 additions and 577 deletions

View File

@ -556,29 +556,36 @@ if(MSVC)
)
endif()
if (MSVC)
unset(IResearch_STATIC_LIBRARIES)
unset(IResearch_STATIC_LIBRARIES)
set(IRESEARCH_STATIC_DEPENDENCIES
${BFD_STATIC_LIBS}
${Unwind_STATIC_LIBS}
"$<TARGET_FILE:icudata-static>" # must expand icu-static into components
"$<TARGET_FILE:icui18n-static>" # must expand icu-static into components
"$<TARGET_FILE:icuuc-static>" # must expand icu-static into components
"$<TARGET_FILE:lz4_static>"
"$<TARGET_FILE:stemmer-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-delimiter-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-ngram-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-text-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-norm-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-stem-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-mask-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-format-1_0-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-tfidf-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-bm25-static>"
)
if (USE_SIMDCOMP)
list(APPEND IRESEARCH_STATIC_DEPENDENCIES "$<TARGET_FILE:${SIMD_LIBRARY_STATIC}>")
endif()
if (MSVC)
foreach(ELEMENT
${BFD_STATIC_LIBS}
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_RELEASE}>"
${Unwind_STATIC_LIBS}
"$<TARGET_FILE:icudata-static>" # must expand icu-static into components
"$<TARGET_FILE:icui18n-static>" # must expand icu-static into components
"$<TARGET_FILE:icuuc-static>" # must expand icu-static into components
"$<TARGET_FILE:lz4_static>"
"$<TARGET_FILE:stemmer-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-delimiter-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-ngram-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-text-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-norm-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-stem-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-mask-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-format-1_0-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-tfidf-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-bm25-static>"
)
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_RELEASE}>"
${IRESEARCH_STATIC_DEPENDENCIES})
set(IResearch_STATIC_LIBRARIES "${IResearch_STATIC_LIBRARIES} \"${ELEMENT}\"")
endforeach()
@ -621,25 +628,9 @@ if (MSVC)
unset(IResearch_STATIC_SCRT_LIBRARIES)
foreach(ELEMENT
${BFD_STATIC_LIBS}
"$<$<CONFIG:Debug>:${Boost_STATIC_staticRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_staticRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_staticRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_staticRT_SYSTEM_LIBRARY_RELEASE}>"
${Unwind_STATIC_LIBS}
"$<TARGET_FILE:icudata-static>" # must expand icu-static into components
"$<TARGET_FILE:icui18n-static>" # must expand icu-static into components
"$<TARGET_FILE:icuuc-static>" # must expand icu-static into components
"$<TARGET_FILE:lz4_static>"
"$<TARGET_FILE:stemmer-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-delimiter-static-scrt>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-ngram-static-scrt>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-text-static-scrt>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-norm-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-stem-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-mask-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-format-1_0-static-scrt>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-tfidf-static-scrt>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-bm25-static-scrt>"
)
"$<$<CONFIG:Debug>:${Boost_STATIC_staticRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_staticRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_staticRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_staticRT_SYSTEM_LIBRARY_RELEASE}>"
${IRESEARCH_STATIC_DEPENDENCIES})
set(IResearch_STATIC_SCRT_LIBRARIES "${IResearch_STATIC_SCRT_LIBRARIES} \"${ELEMENT}\"")
endforeach()
@ -679,29 +670,11 @@ if (MSVC)
${IResearch_TARGET_NAME}-static-scrt-allinone-build
)
elseif (APPLE)
unset(IResearch_STATIC_LIBRARIES)
foreach(ELEMENT
${BFD_STATIC_LIBS}
${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}
${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}
${Boost_STATIC_sharedRT_THREAD_LIBRARY_DEBUG}
${Unwind_STATIC_LIBS}
"$<TARGET_FILE:icudata-static>" # must expand icu-static into components
"$<TARGET_FILE:icui18n-static>" # must expand icu-static into components
"$<TARGET_FILE:icuuc-static>" # must expand icu-static into components
"$<TARGET_FILE:lz4_static>"
"$<TARGET_FILE:stemmer-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-delimiter-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-ngram-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-text-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-norm-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-stem-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-mask-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-format-1_0-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-tfidf-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-bm25-static>"
)
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_THREAD_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_THREAD_LIBRARY_RELEASE}>"
${IRESEARCH_STATIC_DEPENDENCIES})
set(IResearch_STATIC_LIBRARIES "${IResearch_STATIC_LIBRARIES} '${ELEMENT}'")
endforeach ()
@ -740,33 +713,15 @@ elseif (APPLE)
${IResearch_TARGET_NAME}-static-allinone-build
)
else()
unset(IResearch_STATIC_LIBRARIES)
foreach(ELEMENT
${BFD_STATIC_LIBS}
${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}
${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}
${Boost_STATIC_sharedRT_THREAD_LIBRARY_DEBUG}
${Unwind_STATIC_LIBS}
"$<TARGET_FILE:icudata-static>" # must expand icu-static into components
"$<TARGET_FILE:icui18n-static>" # must expand icu-static into components
"$<TARGET_FILE:icuuc-static>" # must expand icu-static into components
"$<TARGET_FILE:lz4_static>"
"$<TARGET_FILE:stemmer-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-delimiter-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-ngram-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-text-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-norm-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-stem-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-analyzer-mask-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-format-1_0-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-tfidf-static>"
"$<TARGET_FILE:${IResearch_TARGET_NAME}-scorer-bm25-static>"
)
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_LOCALE_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_SYSTEM_LIBRARY_RELEASE}>"
"$<$<CONFIG:Debug>:${Boost_STATIC_sharedRT_THREAD_LIBRARY_DEBUG}>$<$<NOT:$<CONFIG:Debug>>:${Boost_STATIC_sharedRT_THREAD_LIBRARY_RELEASE}>"
${IRESEARCH_STATIC_DEPENDENCIES})
set(IResearch_STATIC_LIBRARIES "${IResearch_STATIC_LIBRARIES} addlib '${ELEMENT}'\\n")
endforeach ()
set(IResearch_STATIC_LIBRARIES_OUT "${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/libresearch-sa${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(IResearch_STATIC_LIBRARIES_OUT "${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/libiresearch-sa${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(IResearch_STATIC_LIBRARIES_CMD "`which echo` -e \"create '${IResearch_STATIC_LIBRARIES_OUT}'\\n addlib '$<TARGET_FILE:${IResearch_TARGET_NAME}-static>'\\n ${IResearch_STATIC_LIBRARIES} save\\n end\" | ar -M")
add_custom_command(
OUTPUT "${IResearch_STATIC_LIBRARIES_OUT}"

View File

@ -82,12 +82,18 @@ DEFINE_ATTRIBUTE_TYPE(iresearch::term_meta)
index_meta& meta,
uint64_t generation,
uint64_t counter,
index_meta::index_segments_t&& segments
index_meta::index_segments_t&& segments,
bstring* payload
) {
meta.gen_ = generation;
meta.last_gen_ = generation;
meta.seg_counter_ = counter;
meta.segments_ = std::move(segments);
if (payload) {
meta.payload(std::move(*payload));
} else {
meta.payload(bytes_ref::NIL);
}
}
/*static*/ bool formats::exists(

View File

@ -447,7 +447,8 @@ struct IRESEARCH_API index_meta_reader {
index_meta& meta,
uint64_t generation,
uint64_t counter,
index_meta::index_segments_t&& segments
index_meta::index_segments_t&& segments,
bstring* payload_buf
);
}; // index_meta_reader

View File

@ -1041,8 +1041,24 @@ class doc_iterator : public irs::doc_iterator {
seek_to_block(target);
// FIXME binary search instead of linear
while ((doc_.value < target) && next());
if (begin_ == end_) {
cur_pos_ += relative_pos();
if (cur_pos_ == term_state_.docs_count) {
doc_.value = doc_limits::eof();
begin_ = end_ = docs_; // seal the iterator
return doc_limits::eof();
}
refill();
}
while (begin_ < end_ && *begin_ < target) {
++begin_;
}
doc_freq_ = doc_freqs_ + relative_pos();
next();
return doc_.value;
}
@ -1879,6 +1895,34 @@ class pay_iterator final : public pos_iterator {
template<typename PosItrType>
class pos_doc_iterator final: public doc_iterator {
public:
virtual doc_id_t seek(doc_id_t target) override {
if (target <= doc_.value) {
return doc_.value;
}
seek_to_block(target);
if (begin_ == end_) {
cur_pos_ += relative_pos();
if (cur_pos_ == term_state_.docs_count) {
doc_.value = doc_limits::eof();
begin_ = end_ = docs_; // seal the iterator
return doc_limits::eof();
}
refill();
}
while (begin_ < end_ && *begin_ < target) {
++begin_;
pos_.pend_pos_ += *doc_freq_++;
}
next();
return doc_.value;
}
virtual bool next() override {
if (begin_ == end_) {
cur_pos_ += relative_pos();
@ -1967,16 +2011,25 @@ struct index_meta_writer final: public irs::index_meta_writer {
static const string_ref FORMAT_PREFIX_TMP;
static const int32_t FORMAT_MIN = 0;
static const int32_t FORMAT_MAX = FORMAT_MIN;
static const int32_t FORMAT_MAX = 1;
enum { HAS_PAYLOAD = 1 };
explicit index_meta_writer(int32_t version) NOEXCEPT
: version_(version) {
assert(version_ >= FORMAT_MIN && version <= FORMAT_MAX);
}
virtual std::string filename(const index_meta& meta) const override;
using irs::index_meta_writer::prepare;
virtual bool prepare(directory& dir, index_meta& meta) override;
virtual bool commit() override;
virtual void rollback() NOEXCEPT override;
private:
directory* dir_ = nullptr;
index_meta* meta_ = nullptr;
int32_t version_;
}; // index_meta_writer
template<>
@ -2032,7 +2085,7 @@ bool index_meta_writer::prepare(directory& dir, index_meta& meta) {
}
{
format_utils::write_header(*out, FORMAT_NAME, FORMAT_MAX);
format_utils::write_header(*out, FORMAT_NAME, version_);
out->write_vlong(meta.generation());
out->write_long(meta.counter());
assert(meta.size() <= integer_traits<uint32_t>::const_max);
@ -2043,6 +2096,15 @@ bool index_meta_writer::prepare(directory& dir, index_meta& meta) {
write_string(*out, segment.meta.codec->type().name());
}
if (version_ > FORMAT_MIN) {
const byte_type flags = meta.payload().null() ? 0 : HAS_PAYLOAD;
out->write_byte(flags);
if (flags == HAS_PAYLOAD) {
irs::write_string(*out, meta.payload());
}
}
format_utils::write_footer(*out);
// important to close output here
}
@ -2170,7 +2232,7 @@ void index_meta_reader::read(
const auto checksum = format_utils::checksum(*in);
// check header
format_utils::check_header(
const int32_t version = format_utils::check_header(
*in,
index_meta_writer::FORMAT_NAME,
index_meta_writer::FORMAT_MIN,
@ -2194,8 +2256,23 @@ void index_meta_reader::read(
reader->read(dir, segment.meta, segment.filename);
}
bool has_payload = false;
bstring payload;
if (version > index_meta_writer::FORMAT_MIN) {
has_payload = (in->read_byte() & index_meta_writer::HAS_PAYLOAD);
if (has_payload) {
payload = irs::read_string<bstring>(*in);
}
}
format_utils::check_footer(*in, checksum);
complete(meta, gen, cnt, std::move(segments));
complete(
meta, gen, cnt,
std::move(segments),
has_payload ? &payload : nullptr
);
}
// ----------------------------------------------------------------------------
@ -2216,6 +2293,7 @@ struct segment_meta_writer final : public irs::segment_meta_writer{
explicit segment_meta_writer(int32_t version) NOEXCEPT
: version_(version) {
assert(version_ >= FORMAT_MIN && version <= FORMAT_MAX);
}
virtual void write(
@ -5207,7 +5285,7 @@ class format10 : public irs::version10::format {
format10() NOEXCEPT : format10(format10::type()) { }
virtual index_meta_writer::ptr get_index_meta_writer() const override final;
virtual index_meta_writer::ptr get_index_meta_writer() const override;
virtual index_meta_reader::ptr get_index_meta_reader() const override final;
virtual segment_meta_writer::ptr get_segment_meta_writer() const override;
@ -5235,7 +5313,9 @@ class format10 : public irs::version10::format {
}; // format10
index_meta_writer::ptr format10::get_index_meta_writer() const {
return irs::index_meta_writer::make<::index_meta_writer>();
return irs::index_meta_writer::make<::index_meta_writer>(
int32_t(::index_meta_writer::FORMAT_MIN)
);
}
index_meta_reader::ptr format10::get_index_meta_reader() const {
@ -5334,6 +5414,8 @@ class format11 final : public format10 {
format11() NOEXCEPT : format10(format11::type()) { }
virtual index_meta_writer::ptr get_index_meta_writer() const override final;
virtual field_writer::ptr get_field_writer(bool volatile_state) const override final;
virtual segment_meta_writer::ptr get_segment_meta_writer() const override final;
@ -5341,6 +5423,12 @@ class format11 final : public format10 {
virtual column_meta_writer::ptr get_column_meta_writer() const override final;
}; // format10
index_meta_writer::ptr format11::get_index_meta_writer() const {
return irs::index_meta_writer::make<::index_meta_writer>(
int32_t(::index_meta_writer::FORMAT_MAX)
);
}
field_writer::ptr format11::get_field_writer(bool volatile_state) const {
return irs::field_writer::make<burst_trie::field_writer>(
get_postings_writer(volatile_state),

View File

@ -127,14 +127,18 @@ index_meta::index_meta(const index_meta& rhs)
: gen_(rhs.gen_),
last_gen_(rhs.last_gen_),
seg_counter_(rhs.seg_counter_.load()),
segments_(rhs.segments_) {
segments_(rhs.segments_),
payload_buf_(rhs.payload_buf_),
payload_(rhs.payload_.null() ? bytes_ref::NIL : bytes_ref(payload_buf_)) {
}
index_meta::index_meta(index_meta&& rhs) NOEXCEPT
: gen_(std::move(rhs.gen_)),
last_gen_(std::move(rhs.last_gen_)),
seg_counter_(rhs.seg_counter_.load()),
segments_(std::move(rhs.segments_)) {
segments_(std::move(rhs.segments_)),
payload_buf_(std::move(rhs.payload_buf_)),
payload_(rhs.payload_.null() ? bytes_ref::NIL : bytes_ref(payload_buf_)) {
}
index_meta& index_meta::operator=(index_meta&& rhs) NOEXCEPT {
@ -143,6 +147,8 @@ index_meta& index_meta::operator=(index_meta&& rhs) NOEXCEPT {
last_gen_ = std::move(rhs.last_gen_);
seg_counter_ = rhs.seg_counter_.load();
segments_ = std::move(rhs.segments_);
payload_buf_ = std::move(rhs.payload_buf_);
payload_ = rhs.payload_.null() ? bytes_ref::NIL : bytes_ref(payload_buf_);
}
return *this;
@ -156,7 +162,8 @@ bool index_meta::operator==(const index_meta& other) const NOEXCEPT {
if (gen_ != other.gen_
|| last_gen_ != other.last_gen_
|| seg_counter_ != other.seg_counter_
|| segments_.size() != other.segments_.size()) {
|| segments_.size() != other.segments_.size()
|| payload_ != other.payload_) {
return false;
}

View File

@ -120,6 +120,9 @@ class IRESEARCH_API index_meta {
index_meta& operator=(const index_meta&) = delete;
bool operator==(const index_meta& other) const NOEXCEPT;
bool operator!=(const index_meta& other) const NOEXCEPT {
return !(*this == other);
}
template<typename ForwardIterator>
void add(ForwardIterator begin, ForwardIterator end) {
@ -177,8 +180,8 @@ class IRESEARCH_API index_meta {
last_gen_ = rhs.last_gen_;
}
size_t size() const { return segments_.size(); }
bool empty() const { return segments_.empty(); }
size_t size() const NOEXCEPT { return segments_.size(); }
bool empty() const NOEXCEPT { return segments_.empty(); }
void clear() {
segments_.clear();
@ -194,14 +197,20 @@ class IRESEARCH_API index_meta {
assert(i < segments_.size());
return segments_[i];
}
const index_segment_t& operator[](size_t i) const NOEXCEPT {
assert(i < segments_.size());
return segments_[i];
}
const index_segments_t& segments() const NOEXCEPT {
return segments_;
}
const bytes_ref& payload() const NOEXCEPT {
return payload_;
}
private:
friend class index_writer;
friend struct index_meta_reader;
@ -212,9 +221,26 @@ class IRESEARCH_API index_meta {
uint64_t last_gen_;
std::atomic<uint64_t> seg_counter_;
index_segments_t segments_;
bstring payload_buf_;
bytes_ref payload_;
IRESEARCH_API_PRIVATE_VARIABLES_END
uint64_t next_generation() const NOEXCEPT;
void payload(bstring&& payload) NOEXCEPT {
payload_buf_ = std::move(payload);
payload_ = payload_buf_;
}
void payload(const bytes_ref& payload) {
if (payload.null()) {
payload_buf_.clear();
payload_ = bytes_ref::NIL;
} else {
payload_buf_ = payload;
payload_ = payload_buf_;
}
}
}; // index_meta
NS_END

View File

@ -1811,7 +1811,7 @@ index_writer::active_segment_context index_writer::get_segment_context(
return active_segment_context(segment_ctx, segments_active_);
}
index_writer::pending_context_t index_writer::flush_all() {
index_writer::pending_context_t index_writer::flush_all(const bytes_ref& payload) {
REGISTER_TIMER_DETAILED();
bool modified = !type_limits<type_t::index_gen_t>::valid(meta_.last_gen_);
sync_context to_sync;
@ -2240,13 +2240,19 @@ index_writer::pending_context_t index_writer::flush_all() {
pending_meta->update_generation(meta_); // clone index metadata generation
modified |= !to_sync.empty(); // new files added
// new files were added or no payload was supplied and it's different compared to the previous one
auto& committed_payload = committed_state_->first->payload();
modified |= (!to_sync.empty()
|| (payload.null() && !committed_payload.null())
|| (!payload.null() && (committed_payload.null() || payload != committed_payload)));
// only flush a new index version upon a new index or a metadata change
if (!modified) {
return pending_context_t();
}
pending_meta->payload(payload);
pending_meta->seg_counter_.store(meta_.counter()); // ensure counter() >= max(seg#)
pending_context_t pending_context;
@ -2257,7 +2263,7 @@ index_writer::pending_context_t index_writer::flush_all() {
return pending_context;
}
bool index_writer::start() {
bool index_writer::start(const bytes_ref& payload) {
assert(!commit_lock_.try_lock()); // already locked
REGISTER_TIMER_DETAILED();
@ -2268,7 +2274,7 @@ bool index_writer::start() {
return false;
}
auto to_commit = flush_all();
auto to_commit = flush_all(payload);
if (!to_commit) {
// nothing to commit, no transaction started

View File

@ -590,12 +590,13 @@ class IRESEARCH_API index_writer:
////////////////////////////////////////////////////////////////////////////
/// @brief begins the two-phase transaction
/// @param payload arbitrary user supplied data to store in the index
/// @returns true if transaction has been sucessflully started
////////////////////////////////////////////////////////////////////////////
bool begin() {
bool begin(const bytes_ref& payload = bytes_ref::NIL) {
SCOPED_LOCK(commit_lock_);
return start();
return start(payload);
}
////////////////////////////////////////////////////////////////////////////
@ -609,14 +610,15 @@ class IRESEARCH_API index_writer:
////////////////////////////////////////////////////////////////////////////
/// @brief make all buffered changes visible for readers
/// @param payload arbitrary user supplied data to store in the index
///
/// Note that if begin() has been already called commit() is
/// @note that if begin() has been already called commit() is
/// relatively lightweight operation
////////////////////////////////////////////////////////////////////////////
void commit() {
void commit(const bytes_ref& payload = bytes_ref::NIL) {
SCOPED_LOCK(commit_lock_);
start();
start(payload);
finish();
}
@ -1005,12 +1007,12 @@ class IRESEARCH_API index_writer:
committed_state_t&& committed_state
) NOEXCEPT;
pending_context_t flush_all();
pending_context_t flush_all(const bytes_ref& payload);
flush_context_ptr get_flush_context(bool shared = true);
active_segment_context get_segment_context(flush_context& ctx); // return a usable segment or a nullptr segment if retry is required (e.g. no free segments available)
bool start(); // starts transaction
bool start(const bytes_ref& payload); // starts transaction
void finish(); // finishes transaction
void abort(); // aborts transaction

View File

@ -254,6 +254,15 @@ struct term_collector final: public irs::sort::term_collector {
}
};
FORCE_INLINE float_t tf(float_t freq) NOEXCEPT {
static_assert(
std::is_same<decltype(std::sqrt(freq)), float_t>::value,
"float_t expected"
);
return std::sqrt(freq);
}
NS_END // LOCAL
NS_ROOT
@ -287,27 +296,18 @@ struct stats final {
typedef bm25_sort::score_t score_t;
class const_scorer final : public irs::sort::scorer_base<bm25::score_t> {
struct const_score_ctx final : public irs::sort::score_ctx {
public:
DEFINE_FACTORY_INLINE(scorer)
explicit const_scorer(irs::boost_t boost) NOEXCEPT
explicit const_score_ctx(irs::boost_t boost) NOEXCEPT
: boost_(boost) {
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
score_cast(score_buf) = boost_;
}
private:
const irs::boost_t boost_;
}; // const_scorer
}; // const_score_ctx
class scorer : public irs::sort::scorer_base<bm25::score_t> {
struct score_ctx : public irs::sort::score_ctx {
public:
DEFINE_FACTORY_INLINE(scorer)
scorer(
score_ctx(
float_t k,
irs::boost_t boost,
const bm25::stats& stats,
@ -318,32 +318,20 @@ class scorer : public irs::sort::scorer_base<bm25::score_t> {
assert(freq_);
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
const float_t freq = tf();
score_cast(score_buf) = num_ * freq / (norm_const_ + freq);
}
protected:
FORCE_INLINE float_t tf() const NOEXCEPT {
return float_t(std::sqrt(freq_->value));
}
const frequency* freq_; // document frequency
float_t num_; // partially precomputed numerator : boost * (k + 1) * idf
float_t norm_const_; // 'k' factor
}; // scorer
}; // score_ctx
class norm_scorer final : public scorer {
struct norm_score_ctx final : public score_ctx {
public:
DEFINE_FACTORY_INLINE(norm_scorer)
norm_scorer(
norm_score_ctx(
float_t k,
irs::boost_t boost,
const bm25::stats& stats,
const frequency* freq,
irs::norm&& norm) NOEXCEPT
: scorer(k, boost, stats, freq),
: score_ctx(k, boost, stats, freq),
norm_(std::move(norm)) {
// if there is no norms, assume that b==0
if (!norm_.empty()) {
@ -352,15 +340,9 @@ class norm_scorer final : public scorer {
}
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
const float_t freq = tf();
score_cast(score_buf) = num_ * freq / (norm_const_ + norm_length_ * norm_.read() + freq);
}
private:
irs::norm norm_;
float_t norm_length_{ 0.f }; // precomputed 'k*b/avgD' if norms present, '0' otherwise
}; // norm_scorer
}; // norm_score_ctx
class sort final : public irs::sort::prepared_basic<bm25::score_t, bm25::stats> {
public:
@ -428,7 +410,7 @@ class sort final : public irs::sort::prepared_basic<bm25::score_t, bm25::stats>
return irs::memory::make_unique<field_collector>();
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, score_f> prepare_scorer(
const sub_reader& segment,
const term_reader& field,
const byte_type* query_stats,
@ -438,7 +420,7 @@ class sort final : public irs::sort::prepared_basic<bm25::score_t, bm25::stats>
auto& freq = doc_attrs.get<frequency>();
if (!freq) {
return nullptr;
return { nullptr, nullptr };
}
auto& stats = stats_cast(query_stats);
@ -450,18 +432,32 @@ class sort final : public irs::sort::prepared_basic<bm25::score_t, bm25::stats>
if (!doc) {
// we need 'document' attribute to be exposed
return nullptr;
return { nullptr, nullptr };
}
if (norm.reset(segment, field.meta().norm, *doc)) {
return bm25::scorer::make<bm25::norm_scorer>(
k_, boost, stats, freq.get(), std::move(norm)
);
return {
memory::make_unique<bm25::norm_score_ctx>(k_, boost, stats, freq.get(), std::move(norm)),
[](const void* ctx, byte_type* score_buf) NOEXCEPT {
auto& state = *static_cast<const bm25::norm_score_ctx*>(ctx);
const float_t tf = ::tf(state.freq_->value);
irs::sort::score_cast<score_t>(score_buf) = state.num_ * tf / (state.norm_const_ + state.norm_length_ * state.norm_.read() + tf);
}
};
}
}
// BM11
return bm25::scorer::make<bm25::scorer>(k_, boost, stats, freq.get());
return {
memory::make_unique<bm25::score_ctx>(k_, boost, stats, freq.get()),
[](const void* ctx, byte_type* score_buf) NOEXCEPT {
auto& state = *static_cast<const bm25::score_ctx*>(ctx);
const float_t tf = ::tf(state.freq_->value);
irs::sort::score_cast<score_t>(score_buf) = state.num_ * tf / (state.norm_const_ + tf);
}
};
}
virtual irs::sort::term_collector::ptr prepare_term_collector() const override {

View File

@ -130,14 +130,15 @@ class conjunction : public doc_iterator_base {
}
if (scores_.empty()) {
prepare_score(ord, [](byte_type*) { /*NOOP*/});
prepare_score(ord, nullptr, [](const void*, byte_type*) { /*NOOP*/});
} else {
// prepare score
prepare_score(ord, [this](byte_type* score) {
order_->prepare_score(score);
for (auto* it_score : scores_) {
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = *static_cast<const conjunction*>(ctx);
self.order_->prepare_score(score);
for (auto* it_score : self.scores_) {
it_score->evaluate();
order_->add(score, it_score->c_str());
self.order_->add(score, it_score->c_str());
}
});
}

View File

@ -155,29 +155,32 @@ class basic_disjunction final : public doc_iterator_base {
if (lhs_.score != &irs::score::no_score()
&& rhs_.score != &irs::score::no_score()) {
// both sub-iterators has score
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
score_iterator_impl(lhs_, score);
score_iterator_impl(rhs_, score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = *static_cast<const basic_disjunction*>(ctx);
self.ord_->prepare_score(score);
self.score_iterator_impl(self.lhs_, score);
self.score_iterator_impl(self.rhs_, score);
});
} else if (lhs_.score != &irs::score::no_score()) {
// only left sub-iterator has score
assert(rhs_.score == &irs::score::no_score());
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
score_iterator_impl(lhs_, score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = *static_cast<const basic_disjunction*>(ctx);
self.ord_->prepare_score(score);
self.score_iterator_impl(self.lhs_, score);
});
} else if (rhs_.score != &irs::score::no_score()) {
// only right sub-iterator has score
assert(lhs_.score == &irs::score::no_score());
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
score_iterator_impl(rhs_, score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = *static_cast<const basic_disjunction*>(ctx);
self.ord_->prepare_score(score);
self.score_iterator_impl(self.rhs_, score);
});
} else {
assert(lhs_.score == &irs::score::no_score());
assert(rhs_.score == &irs::score::no_score());
prepare_score(ord, [](byte_type*) {/*NOOP*/});
prepare_score(ord, nullptr, [](const void*, byte_type*) {/*NOOP*/});
}
}
@ -196,7 +199,7 @@ class basic_disjunction final : public doc_iterator_base {
}
}
void score_iterator_impl(doc_iterator_t& it, byte_type* lhs) {
void score_iterator_impl(doc_iterator_t& it, byte_type* lhs) const {
auto doc = it.value();
if (doc < doc_.value) {
@ -210,8 +213,8 @@ class basic_disjunction final : public doc_iterator_base {
}
}
doc_iterator_t lhs_;
doc_iterator_t rhs_;
mutable doc_iterator_t lhs_;
mutable doc_iterator_t rhs_;
document doc_;
const order::prepared* ord_;
}; // basic_disjunction
@ -353,21 +356,22 @@ class small_disjunction : public doc_iterator_base {
// prepare score
if (scored_itrs_.empty()) {
prepare_score(ord, [](byte_type*){ /*NOOP*/ });
prepare_score(ord, nullptr, [](const void*, byte_type*){ /*NOOP*/ });
} else {
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = *static_cast<const small_disjunction*>(ctx);
self.ord_->prepare_score(score);
for (auto& it : scored_itrs_) {
for (auto& it : self.scored_itrs_) {
auto doc = it.value();
if (doc < doc_.value) {
doc = it->seek(doc_.value);
if (doc < self.doc_.value) {
doc = it->seek(self.doc_.value);
}
if (doc == doc_.value) {
if (doc == self.doc_.value) {
it.score->evaluate();
ord_->add(score, it.score->c_str());
self.ord_->add(score, it.score->c_str());
}
}
});
@ -497,9 +501,10 @@ class disjunction : public doc_iterator_base {
std::iota(heap_.begin(), heap_.end(), size_t(0));
// prepare score
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
score_impl(score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = const_cast<disjunction&>(*static_cast<const disjunction*>(ctx));
self.ord_->prepare_score(score);
self.score_impl(score);
});
}

View File

@ -105,9 +105,12 @@ class min_match_disjunction : public doc_iterator_base {
std::iota(heap_.begin(), heap_.end(), size_t(0));
// prepare score
prepare_score(ord, [this](byte_type* score) {
ord_->prepare_score(score);
score_impl(score);
prepare_score(ord, this, [](const void* ctx, byte_type* score) {
auto& self = const_cast<min_match_disjunction&>(
*static_cast<const min_match_disjunction*>(ctx)
);
self.ord_->prepare_score(score);
self.score_impl(score);
});
}

View File

@ -43,7 +43,7 @@ DEFINE_ATTRIBUTE_TYPE(iresearch::score)
}
score::score() NOEXCEPT
: func_([](byte_type*){}) {
: func_([](const void*, byte_type*){}) {
}
NS_END // ROOT

View File

@ -35,7 +35,7 @@ NS_ROOT
//////////////////////////////////////////////////////////////////////////////
class IRESEARCH_API score : public attribute {
public:
typedef std::function<void(byte_type*)> score_f;
typedef void(*score_f)(const void*, byte_type*);
DECLARE_ATTRIBUTE_TYPE();
@ -62,10 +62,14 @@ class IRESEARCH_API score : public attribute {
void evaluate() const {
assert(func_);
func_(leak());
(*func_)(ctx_, leak());
}
bool prepare(const order::prepared& ord, score_f&& func) {
bool prepare(const order::prepared& ord,
const void* ctx,
const score_f func) {
assert(func);
if (ord.empty()) {
return false;
}
@ -73,7 +77,8 @@ class IRESEARCH_API score : public attribute {
value_.resize(ord.score_size());
ord.prepare_score(leak());
func_ = std::move(func);
ctx_ = ctx;
func_ = func;
return true;
}
@ -83,8 +88,9 @@ class IRESEARCH_API score : public attribute {
}
IRESEARCH_API_PRIVATE_VARIABLES_BEGIN
bstring value_;
score_f func_;
bstring value_; // score buffer
const void* ctx_{}; // arbitrary scoring context
score_f func_{}; // scoring function
IRESEARCH_API_PRIVATE_VARIABLES_END
}; // score

View File

@ -29,57 +29,50 @@ NS_ROOT
void basic_doc_iterator_base::prepare_score(
const order::prepared& order,
order::prepared::scorers&& scorers) {
struct scorer_ref {
explicit scorer_ref(const std::pair<sort::scorer::ptr, size_t>& bucket) NOEXCEPT
: scorer(bucket.first.get()), offset(bucket.second) {
assert(scorer);
}
irs::sort::scorer* scorer;
size_t offset;
}; // scorer_ref
scorers_ = std::move(scorers);
switch (scorers_.size()) {
case 0: {
// let order initialize empty score
doc_iterator_base::prepare_score(order, [](byte_type*){ });
doc_iterator_base::prepare_score(order, nullptr, [](const void*, byte_type*){ });
} break;
case 1: {
const scorer_ref ref(scorers_[0]);
auto& scorer = scorers_[0];
if (ref.offset) {
doc_iterator_base::prepare_score(order, [ref](byte_type* score) {
ref.scorer->score(score + ref.offset);
if (scorer.offset) {
doc_iterator_base::prepare_score(
order, &scorer,
[](const void* ctx, byte_type* score) {
auto& scorer = *static_cast<const order::prepared::scorers::entry*>(ctx);
(*scorer.func)(scorer.ctx.get(), score + scorer.offset);
});
} else {
auto* scorer = ref.scorer;
doc_iterator_base::prepare_score(order, [scorer](byte_type* score) {
scorer->score(score);
});
doc_iterator_base::prepare_score(order, scorer.ctx.get(), scorer.func);
}
} break;
case 2: {
const scorer_ref first(scorers_[0]);
const scorer_ref second(scorers_[1]);
if (first.offset) {
doc_iterator_base::prepare_score(order, [first, second](byte_type* score) {
first.scorer->score(score + first.offset);
second.scorer->score(score + second.offset);
if (scorers_[0].offset) {
doc_iterator_base::prepare_score(
order, &scorers_, [](const void* ctx, byte_type* score) {
auto& scorers = *static_cast<const order::prepared::scorers*>(ctx);
(*scorers[0].func)(scorers[0].ctx.get(), score + scorers[0].offset);
(*scorers[1].func)(scorers[1].ctx.get(), score + scorers[1].offset);
});
} else {
doc_iterator_base::prepare_score(order, [first, second](byte_type* score) {
first.scorer->score(score);
second.scorer->score(score + second.offset);
doc_iterator_base::prepare_score(
order, &scorers_, [](const void* ctx, byte_type* score) {
auto& scorers = *static_cast<const order::prepared::scorers*>(ctx);
(*scorers[0].func)(scorers[0].ctx.get(), score);
(*scorers[1].func)(scorers[1].ctx.get(), score + scorers[1].offset);
});
}
} break;
default: {
doc_iterator_base::prepare_score(order, [this](byte_type* score) {
scorers_.score(score);
doc_iterator_base::prepare_score(
order, &scorers_,
[](const void* ctx, byte_type* score) {
auto& scorers = *static_cast<const order::prepared::scorers*>(ctx);
scorers.score(score);
});
} break;
}
@ -113,8 +106,9 @@ basic_doc_iterator::basic_doc_iterator(
assert(doc_);
// set scorers
prepare_score(ord, ord.prepare_scorers(
segment, field, stats_, it_->attributes(), boost
prepare_score(
ord,
ord.prepare_scorers(segment, field, stats_, it_->attributes(), boost
));
}

View File

@ -55,8 +55,9 @@ class IRESEARCH_API doc_iterator_base : public doc_iterator {
void prepare_score(
const order::prepared& order,
score::score_f&& func) {
if (scr_.prepare(order, std::move(func))) {
const void* ctx,
score::score_f func) {
if (scr_.prepare(order, ctx, func)) {
attrs_.emplace(scr_);
}
}

View File

@ -37,10 +37,6 @@ sort::sort(const type_id& type) NOEXCEPT
: type_(&type) {
}
sort::prepared::prepared(attribute_view&& attrs) NOEXCEPT
: attrs_(std::move(attrs)) {
}
// ----------------------------------------------------------------------------
// --SECTION-- order
// ----------------------------------------------------------------------------
@ -298,9 +294,9 @@ order::prepared::scorers::scorers(
segment, field, stats_buf + entry.stats_offset, doc, boost
);
if (scorer) {
if (scorer.second) {
// skip empty scorers
scorers_.emplace_back(std::move(scorer), entry.score_offset);
scorers_.emplace_back(std::move(scorer.first), scorer.second, entry.score_offset);
}
}
}
@ -321,8 +317,8 @@ order::prepared::scorers& order::prepared::scorers::operator=(
void order::prepared::scorers::score(byte_type* scr) const {
for (auto& scorer : scorers_) {
assert(scorer.first);
scorer.first->score(scr + scorer.second);
assert(scorer.func);
(*scorer.func)(scorer.ctx.get(), scr + scorer.offset);
}
}

View File

@ -48,7 +48,8 @@ struct index_reader;
struct sub_reader;
struct term_reader;
typedef bool (*score_less_f)(const byte_type* lhs, const byte_type* rhs);
typedef bool(*score_less_f)(const byte_type* lhs, const byte_type* rhs);
typedef void(*score_f)(const void* ctx, byte_type*);
////////////////////////////////////////////////////////////////////////////////
/// @class sort
@ -98,29 +99,23 @@ class IRESEARCH_API sort {
/// @brief stateful object used for computing the document score based on the
/// stored state
////////////////////////////////////////////////////////////////////////////////
class IRESEARCH_API scorer {
class IRESEARCH_API score_ctx {
public:
DECLARE_UNIQUE_PTR(scorer);
DEFINE_FACTORY_INLINE(scorer)
DECLARE_UNIQUE_PTR(score_ctx);
virtual ~scorer() = default;
////////////////////////////////////////////////////////////////////////////////
/// @brief set the document score based on the stored state
////////////////////////////////////////////////////////////////////////////////
virtual void score(byte_type* score_buf) = 0;
virtual ~score_ctx() = default;
}; // scorer
template <typename T>
class scorer_base : public scorer {
public:
typedef T score_t;
template<typename T>
FORCE_INLINE static T& score_cast(byte_type* score_buf) NOEXCEPT {
assert(score_buf);
return *reinterpret_cast<T*>(score_buf);
}
FORCE_INLINE static T& score_cast(byte_type* score_buf) NOEXCEPT {
assert(score_buf);
return *reinterpret_cast<T*>(score_buf);
}
}; // scorer_base
template<typename T>
FORCE_INLINE static const T& score_cast(const byte_type* score_buf) NOEXCEPT {
return score_cast<T>(const_cast<byte_type*>(score_buf));
}
//////////////////////////////////////////////////////////////////////////////
/// @brief object used for collecting index statistics, for a specific matched
@ -164,19 +159,13 @@ class IRESEARCH_API sort {
/// @class sort::prepared
/// @brief base class for all prepared(compiled) sort entries
////////////////////////////////////////////////////////////////////////////////
class IRESEARCH_API prepared : public util::attribute_view_provider {
class IRESEARCH_API prepared {
public:
DECLARE_UNIQUE_PTR(prepared);
prepared() = default;
explicit prepared(attribute_view&& attrs) NOEXCEPT;
virtual ~prepared() = default;
using util::attribute_view_provider::attributes;
virtual attribute_view& attributes() NOEXCEPT override final {
return attrs_;
}
////////////////////////////////////////////////////////////////////////////
/// @brief store collected index statistics into 'stats' of the
/// current 'filter'
@ -217,13 +206,12 @@ class IRESEARCH_API sort {
////////////////////////////////////////////////////////////////////////////////
/// @brief create a stateful scorer used for computation of document scores
////////////////////////////////////////////////////////////////////////////////
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, score_f> prepare_scorer(
const sub_reader& segment,
const term_reader& field,
const byte_type* stats,
const attribute_view& doc_attrs,
boost_t boost
) const = 0;
boost_t boost) const = 0;
////////////////////////////////////////////////////////////////////////////
/// @brief create an object to be used for collecting index statistics, one
@ -630,10 +618,7 @@ class IRESEARCH_API order final {
/// @note always called on each matched 'field' irrespective of if it
/// contains a matching 'term'
//////////////////////////////////////////////////////////////////////////
void collect(
const sub_reader& segment,
const term_reader& field
) const;
void collect(const sub_reader& segment, const term_reader& field) const;
//////////////////////////////////////////////////////////////////////////
/// @brief collect term related statistics, i.e. term used in the filter
@ -684,6 +669,18 @@ class IRESEARCH_API order final {
////////////////////////////////////////////////////////////////////////////
class IRESEARCH_API scorers: private util::noncopyable { // noncopyable required by MSVC
public:
struct entry {
entry(sort::score_ctx::ptr&& ctx, score_f func, size_t offset)
: ctx(std::move(ctx)),
func(func),
offset(offset) {
}
sort::score_ctx::ptr ctx;
score_f func;
size_t offset;
};
scorers() = default;
scorers(
const prepared_order_t& buckets,
@ -699,7 +696,7 @@ class IRESEARCH_API order final {
void score(byte_type* score) const;
const std::pair<sort::scorer::ptr, size_t>& operator[](size_t i) const NOEXCEPT {
const entry& operator[](size_t i) const NOEXCEPT {
assert(i < scorers_.size());
return scorers_[i];
}
@ -710,7 +707,7 @@ class IRESEARCH_API order final {
private:
IRESEARCH_API_PRIVATE_VARIABLES_BEGIN
std::vector<std::pair<sort::scorer::ptr, size_t>> scorers_; // scorer + offset
std::vector<entry> scorers_; // scorer + offset
IRESEARCH_API_PRIVATE_VARIABLES_END
}; // scorers

View File

@ -226,6 +226,15 @@ struct term_collector final: public irs::sort::term_collector {
}
};
FORCE_INLINE float_t tfidf(float_t freq, float_t idf) NOEXCEPT {
static_assert(
std::is_same<decltype(std::sqrt(freq)), float_t>::value,
"float_t expected"
);
return idf * std::sqrt(freq);
}
NS_END // LOCAL
NS_ROOT
@ -240,27 +249,16 @@ struct idf final : attribute {
typedef tfidf_sort::score_t score_t;
class const_scorer final : public irs::sort::scorer_base<tfidf::score_t> {
public:
DEFINE_FACTORY_INLINE(const_scorer)
explicit const_scorer(irs::boost_t boost) NOEXCEPT
struct const_score_ctx final : public irs::sort::score_ctx {
explicit const_score_ctx(irs::boost_t boost) NOEXCEPT
: boost_(boost) {
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
score_cast(score_buf) = boost_;
}
private:
const irs::boost_t boost_;
}; // const_scorer
}; // const_score_ctx
class scorer : public irs::sort::scorer_base<tfidf::score_t> {
public:
DEFINE_FACTORY_INLINE(scorer)
scorer(
struct score_ctx : public irs::sort::score_ctx {
score_ctx(
irs::boost_t boost,
const tfidf::idf& idf,
const frequency* freq) NOEXCEPT
@ -269,40 +267,22 @@ class scorer : public irs::sort::scorer_base<tfidf::score_t> {
assert(freq_);
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
score_cast(score_buf) = tfidf();
}
protected:
FORCE_INLINE float_t tfidf() const NOEXCEPT {
return idf_ * float_t(std::sqrt(freq_->value));
}
private:
float_t idf_; // precomputed : boost * idf
const frequency* freq_;
}; // scorer
}; // score_ctx
class norm_scorer final : public scorer {
public:
DEFINE_FACTORY_INLINE(norm_scorer)
norm_scorer(
struct norm_score_ctx final : public score_ctx {
norm_score_ctx(
irs::norm&& norm,
irs::boost_t boost,
const tfidf::idf& idf,
const frequency* freq) NOEXCEPT
: scorer(boost, idf, freq),
: score_ctx(boost, idf, freq),
norm_(std::move(norm)) {
}
virtual void score(byte_type* score_buf) NOEXCEPT override {
score_cast(score_buf) = tfidf() * norm_.read();
}
private:
irs::norm norm_;
}; // norm_scorer
}; // norm_score_ctx
class sort final: irs::sort::prepared_basic<tfidf::score_t, tfidf::idf> {
public:
@ -352,7 +332,7 @@ class sort final: irs::sort::prepared_basic<tfidf::score_t, tfidf::idf> {
return irs::memory::make_unique<field_collector>();
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, score_f> prepare_scorer(
const sub_reader& segment,
const term_reader& field,
const byte_type* stats_buf,
@ -362,7 +342,7 @@ class sort final: irs::sort::prepared_basic<tfidf::score_t, tfidf::idf> {
auto& freq = doc_attrs.get<frequency>();
if (!freq) {
return nullptr;
return { nullptr, nullptr };
}
auto& stats = stats_cast(stats_buf);
@ -375,18 +355,28 @@ class sort final: irs::sort::prepared_basic<tfidf::score_t, tfidf::idf> {
if (!doc) {
// we need 'document' attribute to be exposed
return nullptr;
return { nullptr, nullptr };
}
if (norm.reset(segment, field.meta().norm, *doc)) {
return tfidf::scorer::make<tfidf::norm_scorer>(
std::move(norm), boost, stats, freq.get()
);
return {
memory::make_unique<tfidf::norm_score_ctx>(std::move(norm), boost, stats, freq.get()),
[](const void* ctx, byte_type* score_buf) NOEXCEPT {
auto& state = *static_cast<const tfidf::norm_score_ctx*>(ctx);
irs::sort::score_cast<tfidf::score_t>(score_buf) = ::tfidf(state.freq_->value, state.idf_)*state.norm_.read();
}
};
}
}
return tfidf::scorer::make<tfidf::scorer>(boost, stats, freq.get());
return {
memory::make_unique<tfidf::score_ctx>(boost, stats, freq.get()),
[](const void* ctx, byte_type* score_buf) NOEXCEPT {
auto& state = *static_cast<const tfidf::score_ctx*>(ctx);
irs::sort::score_cast<score_t>(score_buf) = ::tfidf(state.freq_->value, state.idf_);
}
};
}
virtual irs::sort::term_collector::ptr prepare_term_collector() const override {

View File

@ -7,7 +7,7 @@ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$(pwd)/bin"
ulimit -c unlimited
for i in `seq 1 1`; do
for j in 1 5 10 15 20 25; do
for j in 1 ; do
MAX_LINES=${j}000000
rm -r iresearch.data || {

View File

@ -7,7 +7,7 @@ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$(pwd)/build/bin"
ulimit -c unlimited
for i in `seq 1 1`; do
for j in 1 5 10 15 20 25; do
for j in 25 ; do
MAX_LINES=${j}000000
# search

View File

@ -32,14 +32,13 @@
using namespace iresearch;
NS_BEGIN(tests)
NS_BEGIN(detail)
TEST(index_meta_tests, memory_directory_read_write_10) {
auto codec = irs::formats::get("1_0");
ASSERT_NE(nullptr, codec);
irs::memory_directory dir;
auto writer = codec->get_index_meta_writer();
void index_meta_read_write(iresearch::directory& dir, const iresearch::format& codec) {
auto writer = codec.get_index_meta_writer();
/* check that there are no files in
* a directory */
// check that there are no files in a directory
std::vector<std::string> files;
auto list_files = [&files] (std::string& name) {
files.emplace_back(std::move(name));
@ -48,34 +47,34 @@ void index_meta_read_write(iresearch::directory& dir, const iresearch::format& c
ASSERT_TRUE(dir.visit(list_files));
ASSERT_TRUE(files.empty());
/* create index metadata and
* write it into the specified
* directory */
iresearch::index_meta meta_orig;
// create index metadata and write it into the specified directory
irs::index_meta meta_orig;
ASSERT_TRUE(meta_orig.payload().null());
// set payload
const irs::bytes_ref payload = ref_cast<byte_type>(string_ref("payload"));
const_cast<bytes_ref&>(meta_orig.payload()) = payload;
ASSERT_TRUE(writer->prepare(dir, meta_orig));
/* we should increase meta generation
* after we write to directory */
// we should increase meta generation after we write to directory
EXPECT_EQ(1, meta_orig.generation());
/* check that files was successfully
* written to directory */
// check that files were successfully
// written to directory
files.clear();
ASSERT_TRUE(dir.visit(list_files));
EXPECT_EQ(1, files.size());
EXPECT_EQ(files[0], iresearch::string_ref("pending_segments_1"));
EXPECT_EQ(files[0], irs::string_ref("pending_segments_1"));
writer->commit();
/* create index metadata and
* read it from specified the
* directory */
iresearch::index_meta meta_read;
// create index metadata and read it from the specified directory
irs::index_meta meta_read;
{
std::string segments_file;
auto reader = codec.get_index_meta_reader();
auto reader = codec->get_index_meta_reader();
const bool index_exists = reader->last_segments_file(dir, segments_file);
ASSERT_TRUE(index_exists);
@ -85,22 +84,74 @@ void index_meta_read_write(iresearch::directory& dir, const iresearch::format& c
EXPECT_EQ(meta_orig.counter(), meta_read.counter());
EXPECT_EQ(meta_orig.generation(), meta_read.generation());
EXPECT_EQ(meta_orig.size(), meta_read.size());
EXPECT_TRUE(meta_read.payload().null());
EXPECT_NE(meta_orig, meta_read);
const_cast<bytes_ref&>(meta_orig.payload()) = bytes_ref::NIL;
EXPECT_EQ(meta_orig, meta_read);
}
NS_END // detail
NS_END // tests
TEST(index_meta_tests, memory_directory_read_write) {
auto codec = irs::formats::get("1_0");
TEST(index_meta_tests, memory_directory_read_write_11) {
auto codec = irs::formats::get("1_1");
ASSERT_NE(nullptr, codec);
irs::memory_directory dir;
tests::detail::index_meta_read_write(dir, *codec);
auto writer = codec->get_index_meta_writer();
// check that there are no files in a directory
std::vector<std::string> files;
auto list_files = [&files] (std::string& name) {
files.emplace_back(std::move(name));
return true;
};
ASSERT_TRUE(dir.visit(list_files));
ASSERT_TRUE(files.empty());
// create index metadata and write it into the specified directory
irs::index_meta meta_orig;
ASSERT_TRUE(meta_orig.payload().null());
// set payload
const irs::bytes_ref payload = ref_cast<byte_type>(string_ref("payload"));
const_cast<bytes_ref&>(meta_orig.payload()) = payload;
ASSERT_TRUE(writer->prepare(dir, meta_orig));
// we should increase meta generation after we write to directory
EXPECT_EQ(1, meta_orig.generation());
// check that files were successfully
// written to directory
files.clear();
ASSERT_TRUE(dir.visit(list_files));
EXPECT_EQ(1, files.size());
EXPECT_EQ(files[0], irs::string_ref("pending_segments_1"));
writer->commit();
// create index metadata and read it from the specified directory
irs::index_meta meta_read;
{
std::string segments_file;
auto reader = codec->get_index_meta_reader();
const bool index_exists = reader->last_segments_file(dir, segments_file);
ASSERT_TRUE(index_exists);
reader->read(dir, meta_read, segments_file);
}
EXPECT_EQ(meta_orig.counter(), meta_read.counter());
EXPECT_EQ(meta_orig.generation(), meta_read.generation());
EXPECT_EQ(meta_orig.size(), meta_read.size());
EXPECT_EQ(meta_orig.payload(), meta_read.payload());
EXPECT_EQ(meta_orig, meta_read);
}
TEST(index_meta_tests, ctor) {
iresearch::index_meta meta;
irs::index_meta meta;
EXPECT_EQ(0, meta.counter());
EXPECT_EQ(0, meta.size());
EXPECT_TRUE(meta.payload().null());
EXPECT_EQ(irs::type_limits<type_t::index_gen_t>::invalid(), meta.generation());
}

View File

@ -21605,6 +21605,175 @@ INSTANTIATE_TEST_CASE_P(
tests::to_string
);
class index_test_case_10 : public tests::index_test_base { };
TEST_P(index_test_case_10, commit_payload) {
tests::json_doc_generator gen(
resource("simple_sequential.json"),
&tests::generic_json_field_factory);
auto& directory = dir();
auto* doc = gen.next();
auto writer = open_writer();
ASSERT_TRUE(writer->begin()); // initial transaction
writer->commit();
auto reader = irs::directory_reader::open(directory);
ASSERT_TRUE(reader.meta().meta.payload().null());
ASSERT_FALSE(writer->begin()); // transaction hasn't been started, no changes
writer->commit();
ASSERT_EQ(reader, reader.reopen());
// commit with a specified payload
{
auto payload = irs::ref_cast<irs::byte_type>(irs::string_ref(reader.meta().filename));
ASSERT_TRUE(writer->begin(payload)); // different payload supplied
writer->commit(irs::bytes_ref::EMPTY); // payload doesn't matter since transaction is already started
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_TRUE(new_reader.meta().meta.payload().null()); // '1_0' doesn't support payload
reader = new_reader;
}
}
ASSERT_TRUE(writer->begin());
writer->rollback();
// commit with empty payload
writer->commit(irs::bytes_ref::EMPTY);
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_TRUE(new_reader.meta().meta.payload().null()); // '1_0' doesn't support payload
reader = new_reader;
}
ASSERT_FALSE(writer->begin(irs::bytes_ref::EMPTY)); // transaction hasn't been started, no changes
writer->commit(irs::bytes_ref::EMPTY);
ASSERT_EQ(reader, reader.reopen());
// commit without payload
writer->commit();
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_TRUE(new_reader.meta().meta.payload().null()); // '1_0' doesn't support payload
reader = new_reader;
}
ASSERT_FALSE(writer->begin()); // transaction hasn't been started, no changes
writer->commit();
ASSERT_EQ(reader, reader.reopen());
}
INSTANTIATE_TEST_CASE_P(
index_test_10,
index_test_case_10,
::testing::Combine(
::testing::Values(
&tests::memory_directory,
&tests::fs_directory,
&tests::mmap_directory
),
::testing::Values("1_0")
),
tests::to_string
);
class index_test_case_11 : public tests::index_test_base { };
TEST_P(index_test_case_11, commit_payload) {
tests::json_doc_generator gen(
resource("simple_sequential.json"),
&tests::generic_json_field_factory);
auto& directory = dir();
auto* doc = gen.next();
auto writer = open_writer();
ASSERT_TRUE(writer->begin()); // initial transaction
writer->commit();
auto reader = irs::directory_reader::open(directory);
ASSERT_TRUE(reader.meta().meta.payload().null());
ASSERT_FALSE(writer->begin()); // transaction hasn't been started, no changes
writer->commit();
ASSERT_EQ(reader, reader.reopen());
// commit with a specified payload
{
auto payload = irs::ref_cast<irs::byte_type>(irs::string_ref(reader.meta().filename));
ASSERT_TRUE(writer->begin(payload)); // different payload supplied
writer->commit(irs::bytes_ref::EMPTY); // payload doesn't matter since transaction is already started
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_EQ(payload, new_reader.meta().meta.payload());
reader = new_reader;
}
}
ASSERT_TRUE(writer->begin());
writer->rollback();
// commit with empty payload
writer->commit(irs::bytes_ref::EMPTY);
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_EQ(irs::bytes_ref::EMPTY, new_reader.meta().meta.payload());
ASSERT_TRUE(new_reader.meta().meta.payload().empty());
reader = new_reader;
}
ASSERT_FALSE(writer->begin(irs::bytes_ref::EMPTY)); // transaction hasn't been started, no changes
writer->commit(irs::bytes_ref::EMPTY);
ASSERT_EQ(reader, reader.reopen());
// commit without payload
writer->commit();
// check written payload
{
auto new_reader = reader.reopen();
ASSERT_NE(reader, new_reader);
ASSERT_TRUE(new_reader.meta().meta.payload().null());
reader = new_reader;
}
ASSERT_FALSE(writer->begin()); // transaction hasn't been started, no changes
writer->commit();
ASSERT_EQ(reader, reader.reopen());
}
INSTANTIATE_TEST_CASE_P(
index_test_11,
index_test_case_11,
::testing::Combine(
::testing::Values(
&tests::memory_directory,
&tests::fs_directory,
&tests::mmap_directory
),
::testing::Values("1_1")
),
tests::to_string
);
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -59,14 +59,14 @@ namespace tests {
virtual irs::sort::field_collector::ptr prepare_field_collector() const override {
return nullptr; // do not need to collect stats
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
const iresearch::sub_reader&,
const iresearch::term_reader&,
const irs::byte_type* query_attrs,
const irs::attribute_view& doc_attrs,
irs::boost_t
) const override {
return nullptr;
return { nullptr, nullptr };
}
virtual irs::sort::term_collector::ptr prepare_term_collector() const override {
return nullptr; // do not need to collect stats

View File

@ -112,7 +112,7 @@ class all_filter_test_case : public tests::filter_test_case_base {
auto& sort = order.add<tests::sort::custom_sort>(false);
sort.prepare_field_collector_ = []()->irs::sort::field_collector::ptr { return nullptr; };
sort.prepare_scorer = [](const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&)->irs::sort::scorer::ptr { return nullptr; };
sort.prepare_scorer = [](const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&)->std::pair<irs::sort::score_ctx::ptr, irs::score_f> { return { nullptr, nullptr }; };
sort.prepare_term_collector_ = []()->irs::sort::term_collector::ptr { return nullptr; };
check_query(irs::all(), order, docs, rdr, false);
}

View File

@ -54,29 +54,13 @@ struct basic_sort : irs::sort {
: irs::sort(basic_sort::type()), idx(idx) {
}
struct basic_scorer : irs::sort::scorer_base<size_t> {
struct basic_scorer : irs::sort::score_ctx {
explicit basic_scorer(size_t idx) : idx(idx) {}
void score(irs::byte_type* score) override {
score_cast(score) = idx;
}
size_t idx;
};
struct prepared_sort : irs::sort::prepared {
template<typename T>
FORCE_INLINE static T& score_cast(irs::byte_type* score_buf) {
assert(score_buf);
return *reinterpret_cast<T*>(score_buf);
}
template<typename T>
FORCE_INLINE static const T& score_cast(const irs::byte_type* score_buf) {
assert(score_buf);
return *reinterpret_cast<const T*>(score_buf);
}
explicit prepared_sort(size_t idx) : idx(idx) { }
virtual void collect(
@ -96,14 +80,20 @@ struct basic_sort : irs::sort {
return nullptr; // do not need to collect stats
}
scorer::ptr prepare_scorer(
std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
const irs::sub_reader&,
const irs::term_reader&,
const irs::byte_type*,
const irs::attribute_view&,
irs::boost_t
) const override {
return scorer::ptr(new basic_scorer(idx));
return {
score_ctx::ptr(new basic_scorer(idx)),
[](const void* ctx, irs::byte_type* score) {
auto& state = *reinterpret_cast<const basic_scorer*>(ctx);
sort::score_cast<size_t>(score) = state.idx;
}
};
}
void prepare_stats(irs::byte_type*) const override { }
@ -180,8 +170,9 @@ class basic_doc_iterator: public irs::doc_iterator {
boost
);
score_.prepare(ord, [this] (irs::byte_type* score) {
scorers_.score(score);
score_.prepare(ord, this, [](const void* ctx, irs::byte_type* score) {
auto& self = *static_cast<const basic_doc_iterator*>(ctx);
self.scorers_.score(score);
});
attrs_.emplace(score_);

View File

@ -42,19 +42,12 @@ NS_BEGIN(sort)
/// @brief boost scorer assign boost value to the particular document score
////////////////////////////////////////////////////////////////////////////////
struct boost : public irs::sort {
class scorer: public irs::sort::scorer_base<irs::boost_t> {
struct score_ctx: public irs::sort::score_ctx {
public:
DEFINE_FACTORY_INLINE(scorer)
score_ctx(irs::boost_t boost): boost_(boost) { }
scorer(irs::boost_t boost): boost_(boost) { }
virtual void score(irs::byte_type* score) {
score_cast(score) = boost_;
}
private:
irs::boost_t boost_;
}; // sort::boost::scorer
};
class prepared: public irs::sort::prepared_basic<irs::boost_t, void> {
public:
@ -65,14 +58,21 @@ struct boost : public irs::sort {
return irs::flags::empty_instance();
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<irs::sort::score_ctx::ptr, irs::score_f> prepare_scorer(
const irs::sub_reader&,
const irs::term_reader&,
const irs::byte_type* query_attrs,
const irs::attribute_view& doc_attrs,
irs::boost_t boost
) const override {
return boost::scorer::make<boost::scorer>(boost);
return {
irs::memory::make_unique<boost::score_ctx>(boost),
[](const void* ctx, irs::byte_type* score_buf) {
auto& state = *reinterpret_cast<const score_ctx*>(ctx);
sort::score_cast<irs::boost_t>(score_buf) = state.boost_;
}
};
}
private:
@ -132,19 +132,7 @@ struct custom_sort: public irs::sort {
const custom_sort& sort_;
};
class scorer: public irs::sort::scorer_base<irs::doc_id_t> {
public:
virtual void score(irs::byte_type* score_buf) override {
ASSERT_TRUE(score_buf);
auto& doc_id = *reinterpret_cast<irs::doc_id_t*>(score_buf);
doc_id = document_attrs_.get<irs::document>()->value;
if (sort_.scorer_score) {
sort_.scorer_score(doc_id);
}
}
struct scorer: public irs::sort::score_ctx {
scorer(
const custom_sort& sort,
const irs::sub_reader& segment_reader,
@ -158,7 +146,6 @@ struct custom_sort: public irs::sort {
term_reader_(term_reader) {
}
private:
const irs::attribute_view& document_attrs_;
const irs::byte_type* filter_node_attrs_;
const irs::sub_reader& segment_reader_;
@ -194,7 +181,7 @@ struct custom_sort: public irs::sort {
return irs::memory::make_unique<collector>(sort_);
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
const irs::sub_reader& segment_reader,
const irs::term_reader& term_reader,
const irs::byte_type* filter_node_attrs,
@ -207,9 +194,20 @@ struct custom_sort: public irs::sort {
);
}
return sort::scorer::make<custom_sort::prepared::scorer>(
sort_, segment_reader, term_reader, filter_node_attrs, document_attrs
);
return {
irs::memory::make_unique<custom_sort::prepared::scorer>(sort_, segment_reader, term_reader, filter_node_attrs, document_attrs),
[](const void* ctx, irs::byte_type* score_buf) {
ASSERT_TRUE(score_buf);
auto& state = *reinterpret_cast<const scorer*>(ctx);
auto& doc_id = *reinterpret_cast<irs::doc_id_t*>(score_buf);
doc_id = state.document_attrs_.get<irs::document>()->value;
if (state.sort_.scorer_score) {
state.sort_.scorer_score(doc_id);
}
}
};
}
virtual void prepare_score(irs::byte_type* score) const override {
@ -242,7 +240,7 @@ struct custom_sort: public irs::sort {
std::function<void(const irs::sub_reader&, const irs::term_reader&, const irs::attribute_view&)> collector_collect_term;
std::function<void(irs::byte_type*, const irs::index_reader&, const irs::sort::field_collector*, const irs::sort::term_collector*)> collectors_collect_;
std::function<irs::sort::field_collector::ptr()> prepare_field_collector_;
std::function<scorer::ptr(const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&)> prepare_scorer;
std::pair<sort::score_ctx::ptr, irs::score_f>(*prepare_scorer)(const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&){};
std::function<irs::sort::term_collector::ptr()> prepare_term_collector_;
std::function<void(irs::doc_id_t&, const irs::doc_id_t&)> scorer_add;
std::function<bool(const irs::doc_id_t&, const irs::doc_id_t&)> scorer_less;
@ -295,23 +293,11 @@ struct frequency_sort: public irs::sort {
}
};
class scorer: public irs::sort::scorer_base<score_t> {
public:
struct scorer: public irs::sort::score_ctx {
scorer(const size_t* v_docs_count, const irs::attribute_view::ref<irs::document>::type& doc_id_t):
doc_id_t_attr(doc_id_t), docs_count(v_docs_count) {
}
virtual void score(irs::byte_type* score_buf) override {
auto& buf = score_cast(score_buf);
buf.id = doc_id_t_attr->value;
// docs_count may be nullptr if no collector called, e.g. by range_query for bitset_doc_iterator
if (docs_count) {
buf.value = 1. / *docs_count;
}
}
private:
const irs::attribute_view::ref<irs::document>::type& doc_id_t_attr;
const size_t* docs_count;
};
@ -341,7 +327,7 @@ struct frequency_sort: public irs::sort {
return nullptr; // do not need to collect stats
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<sort::score_ctx::ptr, irs::score_f> prepare_scorer(
const irs::sub_reader&,
const irs::term_reader&,
const irs::byte_type* stats_buf,
@ -351,7 +337,19 @@ struct frequency_sort: public irs::sort {
auto& doc_id_t = doc_attrs.get<irs::document>();
auto& stats = stats_cast(stats_buf);
const size_t* docs_count = &stats.count;
return sort::scorer::make<frequency_sort::prepared::scorer>(docs_count, doc_id_t);
return {
irs::memory::make_unique<frequency_sort::prepared::scorer>(docs_count, doc_id_t),
[](const void* ctx, irs::byte_type* score_buf) {
auto& state = *reinterpret_cast<const scorer*>(ctx);
auto& buf = irs::sort::score_cast<score_t>(score_buf);
buf.id = state.doc_id_t_attr->value;
// docs_count may be nullptr if no collector called, e.g. by range_query for bitset_doc_iterator
if (state.docs_count) {
buf.value = 1. / *state.docs_count;
}
}
};
}
virtual void prepare_score(irs::byte_type* score_buf) const override {

View File

@ -59,14 +59,14 @@ struct aligned_scorer : public irs::sort {
) const override {
// NOOP
}
virtual scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
const irs::sub_reader& segment,
const irs::term_reader& field,
const irs::byte_type* stats,
const irs::attribute_view& doc_attrs,
irs::boost_t boost
) const {
return nullptr;
return { nullptr, nullptr };
}
const irs::flags& features() const {
return features_;

View File

@ -868,6 +868,7 @@ int search(
irs::filter::prepared::ptr filter;
std::string tmpBuf;
#ifdef IRESEARCH_COMPLEX_SCORING
#if defined(_MSC_VER) && defined(IRESEARCH_DEBUG)
typedef irs::memory::memory_multi_size_pool<irs::memory::identity_grow> pool_t;
typedef irs::memory::memory_pool_multi_size_allocator<Entry, pool_t> alloc_t;
@ -878,7 +879,6 @@ int search(
pool_t pool(limit + 1); // +1 for least significant overflow element
#ifdef IRESEARCH_COMPLEX_SCORING
auto comparer = [&order](const irs::bstring& lhs, const irs::bstring& rhs)->bool {
return order.less(lhs.c_str(), rhs.c_str());
};
@ -886,9 +886,8 @@ int search(
comparer, alloc_t{pool}
);
#else
std::multimap<float, irs::doc_id_t, std::less<float>, alloc_t> sorted(
std::less<float>(), alloc_t{pool}
);
std::vector<std::pair<float_t, irs::doc_id_t>> sorted;
sorted.reserve(limit + 1); // +1 for least significant overflow element
#endif
// process a single task
@ -957,13 +956,41 @@ int search(
std::forward_as_tuple(raw_score_value),
std::forward_as_tuple(docs->value(), score_value)
);
#else
sorted.emplace(score_value, doc->value);
#endif
if (sorted.size() > limit) {
sorted.erase(--(sorted.end()));
}
#else
std::push_heap(
sorted.begin(), sorted.end(),
[](const std::pair<float_t, irs::doc_id_t>& lhs,
const std::pair<float_t, irs::doc_id_t>& rhs) NOEXCEPT {
return lhs.first < rhs.first;
});
if (sorted.size() > limit) {
std::pop_heap(
sorted.begin(), sorted.end(),
[](const std::pair<float_t, irs::doc_id_t>& lhs,
const std::pair<float_t, irs::doc_id_t>& rhs) NOEXCEPT {
return lhs.first < rhs.first;
});
sorted.pop_back();
}
auto end = sorted.end();
for (auto begin = sorted.begin(); begin != end; --end) {
std::pop_heap(
begin, end,
[](const std::pair<float_t, irs::doc_id_t>& lhs,
const std::pair<float_t, irs::doc_id_t>& rhs) NOEXCEPT {
return lhs.first < rhs.first;
});
}
#endif
}
}
}

View File

@ -345,7 +345,7 @@ void IResearchLink::batchInsert( // insert documents
}
if (!queue) {
throw std::runtime_error(std::string("failed to report status during batch insert for arangosearch link '") + arangodb::basics::StringUtils::itoa(_id) + "'");
throw std::runtime_error("failed to report status during batch insert for arangosearch link '" + arangodb::basics::StringUtils::itoa(_id) + "'");
}
if (!trx.state()) {
@ -430,8 +430,7 @@ void IResearchLink::batchInsert( // insert documents
try {
for (FieldIterator body(trx); begin != end; ++begin) {
auto res =
insertDocument(ctx->_ctx, body, begin->second, begin->first, _meta, id());
auto res = insertDocument(ctx->_ctx, body, begin->second, begin->first, _meta, id());
if (!res.ok()) {
LOG_TOPIC("e5eb1", WARN, arangodb::iresearch::TOPIC) << res.errorMessage();
@ -442,17 +441,20 @@ void IResearchLink::batchInsert( // insert documents
}
} catch (arangodb::basics::Exception const& e) {
LOG_TOPIC("72aa5", WARN, arangodb::iresearch::TOPIC)
<< "caught exception while inserting batch into arangosearch link '" << id() << "': " << e.code() << " " << e.what();
<< "caught exception while inserting batch into arangosearch link '" << id()
<< "': " << e.code() << " " << e.what();
IR_LOG_EXCEPTION();
queue->setStatus(e.code());
} catch (std::exception const& e) {
LOG_TOPIC("3cbae", WARN, arangodb::iresearch::TOPIC)
<< "caught exception while inserting batch into arangosearch link '" << id() << "': " << e.what();
<< "caught exception while inserting batch into arangosearch link '" << id()
<< "': " << e.what();
IR_LOG_EXCEPTION();
queue->setStatus(TRI_ERROR_INTERNAL);
} catch (...) {
LOG_TOPIC("3da8d", WARN, arangodb::iresearch::TOPIC)
<< "caught exception while inserting batch into arangosearch link '" << id() << "'";
<< "caught exception while inserting batch into arangosearch link '" << id()
<< "'";
IR_LOG_EXCEPTION();
queue->setStatus(TRI_ERROR_INTERNAL);
}
@ -474,15 +476,15 @@ arangodb::Result IResearchLink::cleanupUnsafe() {
try {
irs::directory_utils::remove_all_unreferenced(*(_dataStore._directory));
} catch (std::exception const& e) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while cleaning up arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "': " + e.what()
);
return arangodb::Result(
TRI_ERROR_INTERNAL,
"caught exception while cleaning up arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "': " + e.what());
} catch (...) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while cleaning up arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "'"
);
return arangodb::Result(
TRI_ERROR_INTERNAL,
"caught exception while cleaning up arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "'");
}
return arangodb::Result();
@ -498,8 +500,8 @@ arangodb::Result IResearchLink::commit() {
if (!*_asyncSelf) {
return arangodb::Result(
TRI_ERROR_ARANGO_INDEX_HANDLE_BAD, // the current link is no longer valid (checked after ReadLock aquisition)
std::string("failed to lock arangosearch link while commiting arangosearch link '") + std::to_string(id()) + "'"
);
"failed to lock arangosearch link while commiting arangosearch link '"
+ std::to_string(id()) + "'");
}
return commitUnsafe();
@ -523,16 +525,18 @@ arangodb::Result IResearchLink::commitUnsafe() {
if (!reader) {
// nothing more to do
LOG_TOPIC("37bcf", WARN, arangodb::iresearch::TOPIC)
<< "failed to update snapshot after commit, run id '" << size_t(&runId) << "', reuse the existing snapshot for arangosearch link '" << id() << "'";
<< "failed to update snapshot after commit, run id '" << size_t(&runId)
<< "', reuse the existing snapshot for arangosearch link '" << id() << "'";
return arangodb::Result();
}
if (_dataStore._reader == reader) {
// reader not modified
if (_dataStore._reader == reader
&& _dataStore._recovery_range_start == reader.meta().filename) {
// reader not modified
if (_flushCallback) {
//upgrade tick without writing WAL entry
// upgrade tick without writing WAL entry
return _flushCallback(VPackSlice::noneSlice());
}
@ -542,11 +546,8 @@ arangodb::Result IResearchLink::commitUnsafe() {
// if WAL 'Flush' recovery is enabled (must be for recoverable DB scenarios)
if (_flushCallback && RecoveryState::DONE == _dataStore._recovery) {
auto& checkpoint = reader.meta().filename;
auto checkpointFile = // checkpoint file name
checkpoint + std::string(IRESEARCH_CHECKPOINT_SUFFIX);
auto ref = irs::directory_utils::reference( // create a reference
*(_dataStore._directory), checkpointFile, true // args
);
auto checkpointFile = checkpoint + std::string(IRESEARCH_CHECKPOINT_SUFFIX);
auto ref = irs::directory_utils::reference(*(_dataStore._directory), checkpointFile, true);
arangodb::velocypack::Builder builder;
builder.add(arangodb::velocypack::Value(checkpoint));
@ -566,25 +567,26 @@ arangodb::Result IResearchLink::commitUnsafe() {
if (!out) { // create checkpoint
return arangodb::Result( // result
TRI_ERROR_CANNOT_WRITE_FILE, // code
std::string("failed to write checkpoint file for arangosearch link '") + std::to_string(id()) + "', run id '" + std::to_string(size_t(&runId)) + "', ignoring commit success, path: " + checkpointFile
);
"failed to write checkpoint file for arangosearch link '" + std::to_string(id()) +
"', run id '" + std::to_string(size_t(&runId)) +
"', ignoring commit success, path: " + checkpointFile);
}
irs::write_string(*out, previousCheckpoint); // will flush on deallocation
} catch (std::exception const& e) {
_dataStore._directory->remove(checkpointFile); // try to remove failed file
return arangodb::Result( // result
TRI_ERROR_ARANGO_IO_ERROR, // code
std::string("caught exception while writing checkpoint file for arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "': " + e.what()
);
return arangodb::Result(
TRI_ERROR_ARANGO_IO_ERROR,
"caught exception while writing checkpoint file for arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "': " + e.what());
} catch (...) {
_dataStore._directory->remove(checkpointFile); // try to remove failed file
return arangodb::Result( // result
TRI_ERROR_ARANGO_IO_ERROR, // code
std::string("caught exception while writing checkpoint file for arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "'"
);
return arangodb::Result(
TRI_ERROR_ARANGO_IO_ERROR,
"caught exception while writing checkpoint file for arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "'");
}
_dataStore._recovery_range_start = std::move(previousCheckpoint); // remember current checkpoint range start
@ -593,24 +595,22 @@ arangodb::Result IResearchLink::commitUnsafe() {
}
_dataStore._reader = reader; // update reader
arangodb::aql::QueryCache::instance()->invalidate(
&(_collection.vocbase()), _viewGuid
);
arangodb::aql::QueryCache::instance()->invalidate(&(_collection.vocbase()), _viewGuid);
} catch (arangodb::basics::Exception const& e) {
return arangodb::Result( // result
e.code(), // code
std::string("caught exception while committing arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "': " + e.what()
);
return arangodb::Result(
e.code(),
"caught exception while committing arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "': " + e.what());
} catch (std::exception const& e) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while committing arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "': " + e.what()
);
return arangodb::Result(
TRI_ERROR_INTERNAL,
"caught exception while committing arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "': " + e.what());
} catch (...) {
return arangodb::Result( // result
TRI_ERROR_INTERNAL, // code
std::string("caught exception while committing arangosearch link '") + std::to_string(id()) + "' run id '" + std::to_string(size_t(&runId)) + "'"
);
return arangodb::Result(
TRI_ERROR_INTERNAL,
"caught exception while committing arangosearch link '" + std::to_string(id()) +
"' run id '" + std::to_string(size_t(&runId)) + "'");
}
return arangodb::Result();
@ -1111,8 +1111,8 @@ arangodb::Result IResearchLink::initDataStore(InitCallback const& initCallback,
// if in recovery then recovery markers are expected
// if not in recovery then AFTER_CHECKPOINT will be converted to DONE by
// the post-recovery-callback (or left untouched if no DatabaseFeature
if (engine->inRecovery()) {
// the post-recovery-callback (or left untouched if no DatabaseFeature)
if (engine->inRecovery() && previousCheckpoint != recovery_reader.meta().filename) {
_dataStore._recovery = RecoveryState::DURING_CHECKPOINT; // exisitng data store (assume worst case, i.e. replaying just before checkpoint)
}
}

View File

@ -557,10 +557,8 @@ void registerRecoveryHelper() {
}
res = arangodb::RocksDBEngine::registerRecoveryHelper(
std::shared_ptr<RocksDBRecoveryHelper>(
const_cast<RocksDBRecoveryHelper*>(&rocksDBHelper),
[](RocksDBRecoveryHelper*)->void {}
)
std::shared_ptr<RocksDBRecoveryHelper>(std::shared_ptr<RocksDBRecoveryHelper>(),
const_cast<RocksDBRecoveryHelper*>(&rocksDBHelper))
);
if (!res.ok()) {
@ -765,8 +763,23 @@ arangodb::Result FlushFeature::releaseUnusedTicks(size_t& count) {
TRI_ASSERT(minTick <= engine->currentTick());
LOG_TOPIC("fd934", TRACE, Logger::FLUSH) << "Releasing tick " << minTick;
TRI_IF_FAILURE("FlushCrashBeforeSyncingMinTick") {
TRI_SegfaultDebugging("crashing before syncing min tick");
}
engine->waitForSyncTick(minTick);
TRI_IF_FAILURE("FlushCrashAfterSyncingMinTick") {
TRI_SegfaultDebugging("crashing after syncing min tick");
}
engine->releaseTick(minTick);
TRI_IF_FAILURE("FlushCrashAfterReleasingMinTick") {
TRI_SegfaultDebugging("crashing after releasing min tick");
}
return Result();
}

View File

@ -52,11 +52,10 @@ void FlushThread::wakeup() {
/// @brief main loop
void FlushThread::run() {
FlushFeature* flushFeature =
application_features::ApplicationServer::getFeature<FlushFeature>(
"Flush");
auto* flushFeature =
application_features::ApplicationServer::getFeature<FlushFeature>("Flush");
TRI_ASSERT(flushFeature != nullptr);
StorageEngine* engine = EngineSelectorFeature::ENGINE;
size_t count = 0;
while (!isStopping()) {
@ -68,20 +67,6 @@ void FlushThread::run() {
continue;
}
TRI_voc_tick_t toRelease = engine->currentTick();
LOG_TOPIC("fc0f4", TRACE, Logger::FLUSH)
<< "flush thread initiating sync for tick '" << toRelease << "'";
engine->waitForSyncTick(toRelease);
TRI_IF_FAILURE("FlushThreadCrashAfterWalSync") {
TRI_SegfaultDebugging("crashing before flush thread callbacks");
}
TRI_IF_FAILURE("FlushThreadCrashAfterCallbacks") {
TRI_SegfaultDebugging("crashing before releasing tick");
}
flushFeature->releaseUnusedTicks(count);
LOG_TOPIC_IF("2b2h1", DEBUG, arangodb::Logger::FLUSH, count)

View File

@ -112,22 +112,7 @@ struct custom_sort : public irs::sort {
const custom_sort& sort_;
};
class scorer : public irs::sort::scorer_base<irs::doc_id_t> {
public:
virtual void score(irs::byte_type* score_buf) override {
UNUSED(stats_);
UNUSED(segment_reader_);
UNUSED(term_reader_);
EXPECT_TRUE(score_buf);
auto& doc_id = *reinterpret_cast<irs::doc_id_t*>(score_buf);
doc_id = document_attrs_.get<irs::document>()->value;
if (sort_.scorer_score) {
sort_.scorer_score(doc_id);
}
}
struct scorer : public irs::sort::score_ctx {
scorer(const custom_sort& sort,
const irs::sub_reader& segment_reader,
const irs::term_reader& term_reader,
@ -139,7 +124,6 @@ struct custom_sort : public irs::sort {
sort_(sort),
term_reader_(term_reader) {}
private:
const irs::attribute_view& document_attrs_;
const irs::byte_type* stats_;
const irs::sub_reader& segment_reader_;
@ -172,19 +156,35 @@ struct custom_sort : public irs::sort {
return irs::memory::make_unique<custom_sort::prepared::collector>(sort_);
}
virtual scorer::ptr prepare_scorer(const irs::sub_reader& segment_reader,
const irs::term_reader& term_reader,
const irs::byte_type* filter_node_attrs,
const irs::attribute_view& document_attrs,
irs::boost_t) const override {
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
irs::sub_reader const& segment_reader,
irs::term_reader const& term_reader,
irs::byte_type const* filter_node_attrs,
irs::attribute_view const& document_attrs,
irs::boost_t boost) const override {
if (sort_.prepare_scorer) {
return sort_.prepare_scorer(segment_reader, term_reader,
filter_node_attrs, document_attrs);
return sort_.prepare_scorer(
segment_reader, term_reader,
filter_node_attrs, document_attrs, boost);
}
return sort::scorer::make<custom_sort::prepared::scorer>(sort_, segment_reader,
term_reader, filter_node_attrs,
document_attrs);
return {
std::make_unique<custom_sort::prepared::scorer>(
sort_, segment_reader, term_reader,
filter_node_attrs, document_attrs),
[](const void* ctx, irs::byte_type* score_buf) {
auto& ctxImpl = *reinterpret_cast<const custom_sort::prepared::scorer*>(ctx);
EXPECT_TRUE(score_buf);
auto& doc_id = *reinterpret_cast<irs::doc_id_t*>(score_buf);
doc_id = ctxImpl.document_attrs_.get<irs::document>()->value;
if (ctxImpl.sort_.scorer_score) {
ctxImpl.sort_.scorer_score(doc_id);
}
}
};
}
virtual irs::sort::term_collector::ptr prepare_term_collector() const override {
@ -217,7 +217,7 @@ struct custom_sort : public irs::sort {
std::function<void(const irs::sub_reader&, const irs::term_reader&, const irs::attribute_view&)> term_collector_collect;
std::function<void(irs::byte_type*, const irs::index_reader&)> collector_finish;
std::function<irs::sort::field_collector::ptr()> prepare_field_collector;
std::function<scorer::ptr(const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&)> prepare_scorer;
std::function<std::pair<score_ctx::ptr, irs::score_f>(const irs::sub_reader&, const irs::term_reader&, const irs::byte_type*, const irs::attribute_view&, irs::boost_t)> prepare_scorer;
std::function<irs::sort::term_collector::ptr()> prepare_term_collector;
std::function<void(irs::doc_id_t&, const irs::doc_id_t&)> scorer_add;
std::function<bool(const irs::doc_id_t&, const irs::doc_id_t&)> scorer_less;

View File

@ -106,23 +106,25 @@ struct DocIdScorer: public irs::sort {
virtual irs::sort::field_collector::ptr prepare_field_collector() const override { return nullptr; }
virtual void prepare_score(irs::byte_type* score) const override { }
virtual irs::sort::term_collector::ptr prepare_term_collector() const override { return nullptr; }
virtual irs::sort::scorer::ptr prepare_scorer(
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
irs::sub_reader const& segment,
irs::term_reader const& field,
irs::byte_type const*,
irs::attribute_view const& doc_attrs,
irs::boost_t
) const override {
return irs::sort::scorer::make<Scorer>(doc_attrs.get<irs::document>());
return {
std::make_unique<ScoreCtx>(doc_attrs.get<irs::document>()),
[](const void* ctx, irs::byte_type* score_buf) {
reinterpret_cast<uint64_t&>(*score_buf) = reinterpret_cast<const ScoreCtx*>(ctx)->_doc.get()->value;
}
};
}
};
struct Scorer: public irs::sort::scorer {
struct ScoreCtx: public irs::sort::score_ctx {
ScoreCtx(irs::attribute_view::ref<irs::document>::type const& doc): _doc(doc) { }
irs::attribute_view::ref<irs::document>::type const& _doc;
Scorer(irs::attribute_view::ref<irs::document>::type const& doc): _doc(doc) { }
virtual void score(irs::byte_type* score_buf) override {
reinterpret_cast<uint64_t&>(*score_buf) = _doc.get()->value;
}
};
};

View File

@ -102,22 +102,25 @@ struct BoostScorer : public irs::sort {
return nullptr;
}
virtual irs::sort::scorer::ptr prepare_scorer(irs::sub_reader const&,
irs::term_reader const&,
irs::byte_type const*,
irs::attribute_view const&,
irs::boost_t boost) const override {
struct Scorer : public irs::sort::scorer {
Scorer(irs::boost_t score) : scr(score) {}
virtual void score(irs::byte_type* score_buf) override {
*reinterpret_cast<score_t*>(score_buf) = scr;
}
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
irs::sub_reader const&,
irs::term_reader const&,
irs::byte_type const*,
irs::attribute_view const&,
irs::boost_t boost) const override {
struct ScoreCtx : public irs::sort::score_ctx {
ScoreCtx(irs::boost_t score) : scr(score) {}
irs::boost_t scr;
};
return irs::sort::scorer::make<Scorer>(boost);
return {
std::make_unique<ScoreCtx>(boost),
[](const void* ctx, irs::byte_type* score_buf) noexcept {
auto& state = *static_cast<const ScoreCtx*>(ctx);
irs::sort::score_cast<irs::boost_t>(score_buf) = state.scr;
}
};
}
};
@ -177,22 +180,25 @@ struct CustomScorer : public irs::sort {
return nullptr;
}
virtual irs::sort::scorer::ptr prepare_scorer(irs::sub_reader const&,
irs::term_reader const&,
irs::byte_type const*,
irs::attribute_view const&,
irs::boost_t) const override {
struct Scorer : public irs::sort::scorer {
Scorer(float_t score) : i(score) {}
virtual void score(irs::byte_type* score_buf) override {
*reinterpret_cast<score_t*>(score_buf) = i;
}
virtual std::pair<score_ctx::ptr, irs::score_f> prepare_scorer(
irs::sub_reader const&,
irs::term_reader const&,
irs::byte_type const*,
irs::attribute_view const&,
irs::boost_t) const override {
struct ScoreCtx : public irs::sort::score_ctx {
ScoreCtx(float_t score) : i(score) {}
float_t i;
};
return irs::sort::scorer::make<Scorer>(i);
return {
std::make_unique<ScoreCtx>(i),
[](const void* ctx, irs::byte_type* score_buf) noexcept {
auto& state = *static_cast<const ScoreCtx*>(ctx);
irs::sort::score_cast<float_t>(score_buf) = state.i;
}
};
}
float_t i;

View File

@ -0,0 +1,103 @@
/* jshint globalstrict:false, strict:false, unused : false */
/* global assertEqual, assertTrue, assertFalse, assertNull, fail, AQL_EXECUTE */
// //////////////////////////////////////////////////////////////////////////////
// / @brief recovery tests for views
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2010-2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////
var db = require('@arangodb').db;
var internal = require('internal');
var jsunity = require('jsunity');
function runSetup () {
'use strict';
internal.debugClearFailAt();
db._drop('UnitTestsRecoveryDummy');
var c = db._create('UnitTestsRecoveryDummy');
db._dropView('UnitTestsRecoveryView');
db._createView('UnitTestsRecoveryView', 'arangosearch', {});
var meta = { links: { 'UnitTestsRecoveryDummy': { includeAllFields: true } } };
db._view('UnitTestsRecoveryView').properties(meta);
internal.wal.flush(true, true);
internal.debugSetFailAt("FlushCrashAfterReleasingMinTick");
for (let i = 0; i < 10000; i++) {
c.save({ a: "foo_" + i, b: "bar_" + i, c: i });
}
c.save({ name: 'crashme' }, { waitForSync: true });
internal.debugSegfault('crashing server');
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief test suite
// //////////////////////////////////////////////////////////////////////////////
function recoverySuite () {
'use strict';
jsunity.jsUnity.attachAssertions();
return {
setUp: function () {},
tearDown: function () {},
// //////////////////////////////////////////////////////////////////////////////
// / @brief test whether we can restore the trx data
// //////////////////////////////////////////////////////////////////////////////
testIResearchLinkPopulateNoRelease: function () {
var v = db._view('UnitTestsRecoveryView');
assertEqual(v.name(), 'UnitTestsRecoveryView');
assertEqual(v.type(), 'arangosearch');
var p = v.properties().links;
assertTrue(p.hasOwnProperty('UnitTestsRecoveryDummy'));
assertTrue(p.UnitTestsRecoveryDummy.includeAllFields);
var result = AQL_EXECUTE("FOR doc IN UnitTestsRecoveryView SEARCH doc.c >= 0 OPTIONS {waitForSync: true} COLLECT WITH COUNT INTO length RETURN length").json;
assertTrue(result[0] > 0);
}
};
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief executes the test suite
// //////////////////////////////////////////////////////////////////////////////
function main (argv) {
'use strict';
if (argv[1] === 'setup') {
runSetup();
return 0;
} else {
jsunity.run(recoverySuite);
return jsunity.writeDone().status ? 0 : 1;
}
}

View File

@ -45,9 +45,9 @@ function runSetup () {
db._view('UnitTestsRecoveryView').properties(meta);
internal.wal.flush(true, true);
internal.debugSetFailAt("FlushThreadCrashAfterWalSync");
internal.debugSetFailAt("FlushCrashAfterSyncingMinTick");
for (let i = 0; i < 10000; i++) {
for (let i = 0; i < 100000; i++) {
c.save({ a: "foo_" + i, b: "bar_" + i, c: i });
}

View File

@ -45,7 +45,7 @@ function runSetup () {
db._view('UnitTestsRecoveryView').properties(meta);
internal.wal.flush(true, true);
internal.debugSetFailAt("FlushThreadCrashAfterCallbacks");
internal.debugSetFailAt("FlushCrashAfterSyncingMinTick");
for (let i = 0; i < 10000; i++) {
c.save({ a: "foo_" + i, b: "bar_" + i, c: i });

View File

@ -0,0 +1,103 @@
/* jshint globalstrict:false, strict:false, unused : false */
/* global assertEqual, assertTrue, assertFalse, assertNull, fail, AQL_EXECUTE */
// //////////////////////////////////////////////////////////////////////////////
// / @brief recovery tests for views
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2010-2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////
var db = require('@arangodb').db;
var internal = require('internal');
var jsunity = require('jsunity');
function runSetup () {
'use strict';
internal.debugClearFailAt();
db._drop('UnitTestsRecoveryDummy');
var c = db._create('UnitTestsRecoveryDummy');
db._dropView('UnitTestsRecoveryView');
db._createView('UnitTestsRecoveryView', 'arangosearch', {});
var meta = { links: { 'UnitTestsRecoveryDummy': { includeAllFields: true } } };
db._view('UnitTestsRecoveryView').properties(meta);
internal.wal.flush(true, true);
internal.debugSetFailAt("FlushCrashBeforeSyncingMinTick");
for (let i = 0; i < 10000; i++) {
c.save({ a: "foo_" + i, b: "bar_" + i, c: i });
}
c.save({ name: 'crashme' }, { waitForSync: true });
internal.debugSegfault('crashing server');
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief test suite
// //////////////////////////////////////////////////////////////////////////////
function recoverySuite () {
'use strict';
jsunity.jsUnity.attachAssertions();
return {
setUp: function () {},
tearDown: function () {},
// //////////////////////////////////////////////////////////////////////////////
// / @brief test whether we can restore the trx data
// //////////////////////////////////////////////////////////////////////////////
testIResearchLinkPopulateNoRelease: function () {
var v = db._view('UnitTestsRecoveryView');
assertEqual(v.name(), 'UnitTestsRecoveryView');
assertEqual(v.type(), 'arangosearch');
var p = v.properties().links;
assertTrue(p.hasOwnProperty('UnitTestsRecoveryDummy'));
assertTrue(p.UnitTestsRecoveryDummy.includeAllFields);
var result = AQL_EXECUTE("FOR doc IN UnitTestsRecoveryView SEARCH doc.c >= 0 OPTIONS {waitForSync: true} COLLECT WITH COUNT INTO length RETURN length").json;
assertTrue(result[0] > 0);
}
};
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief executes the test suite
// //////////////////////////////////////////////////////////////////////////////
function main (argv) {
'use strict';
if (argv[1] === 'setup') {
runSetup();
return 0;
} else {
jsunity.run(recoverySuite);
return jsunity.writeDone().status ? 0 : 1;
}
}

View File

@ -44,14 +44,13 @@ function runSetup () {
var meta = { links: { 'UnitTestsRecoveryDummy': { includeAllFields: true } } };
db._view('UnitTestsRecoveryView').properties(meta);
// FIXME uncomment when we'll be able to handle tons of removals properly
// for (let i = 0; i < 20000; i++) {
// c.save({ a: "foo_" + i, b: "bar_" + i, c: i, _key: "doc_" + i });
// }
//
// for (let i = 10000; i < 20000; i++) {
// c.remove("doc_" + i);
// }
for (let i = 0; i < 20000; i++) {
c.save({ a: "foo_" + i, b: "bar_" + i, c: i, _key: "doc_" + i });
}
for (let i = 10000; i < 20000; i++) {
c.remove("doc_" + i);
}
c.save({ name: "crashme" }, { waitForSync: true });
@ -82,9 +81,8 @@ function recoverySuite () {
assertTrue(p.hasOwnProperty('UnitTestsRecoveryDummy'));
assertTrue(p.UnitTestsRecoveryDummy.includeAllFields);
// FIXME uncomment when we'll be able to handle tons of removals properly
// var result = AQL_EXECUTE("FOR doc IN UnitTestsRecoveryView SEARCH doc.c >= 0 OPTIONS {waitForSync: true} COLLECT WITH COUNT INTO length RETURN length").json;
// assertEqual(result[0], 10000);
var result = AQL_EXECUTE("FOR doc IN UnitTestsRecoveryView SEARCH doc.c >= 0 OPTIONS {waitForSync: true} COLLECT WITH COUNT INTO length RETURN length").json;
assertEqual(result[0], 10000);
}
};