<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * A cache for an ideal nodes implementation.
 *
 * The cache is localized for quick, localized access.
 *   - There is only one spot one request can be cached, so one can quickly
 *     look whether there is a cache entry on that spot.
 *   - Use LSB bits of bucket to lookup entry such that localized entries use
 *     separate cache spots.
 *
 *
 * Making it cheap for localized
 * access, regardless of real implementation. Basically, uses LSB bits for
 * buckets, as these are the bits that differ on localized access.
 */
#pragma once

#include <vespa/vdslib/container/lruorder.h>
#include <vespa/vdslib/distribution/idealnodecalculator.h>
#include <vespa/vespalib/stllike/hash_map.h>
#include <vespa/vespalib/util/linkedptr.h>

namespace storage {
namespace lib {

class IdealNodeCalculatorCache : public IdealNodeCalculatorConfigurable {
    typedef document::BucketId BucketId;

    /** Cache for all buckets for one given type (same upstate and nodetypes) */
    class TypeCache {
        struct Entry {
            IdealNodeList _result;
            LruOrder<BucketId, TypeCache>::EntryRef _order;
        };
        typedef vespalib::hash_map<BucketId, Entry, BucketId::hash> EntryMap;

        const IdealNodeCalculator& _calc;
        const NodeType& _nodeType;
        UpStates _upStates;
        LruOrder<BucketId, TypeCache> _order;
        EntryMap _entries;
        uint32_t _hitCount;
        uint32_t _missCount;
    public:
        typedef vespalib::LinkedPtr<TypeCache> LP;

        TypeCache(const IdealNodeCalculator& c, const NodeType& t,
                  UpStates us, uint32_t size)
            : _calc(c), _nodeType(t), _upStates(us), _order(size, *this),
              _hitCount(0), _missCount(0) {}

        IdealNodeList get(const document::BucketId& bucket) {
            EntryMap::const_iterator it(_entries.find(bucket));
            if (it == _entries.end()) {
                ++_missCount;
                Entry& newEntry(_entries[bucket]);
                newEntry._result = _calc.getIdealNodes(
                        _nodeType, bucket, _upStates);
                newEntry._order = _order.add(bucket);
                return newEntry._result;
            } else {
                ++_hitCount;
                _order.moveToStart(it->second._order);
                return it->second._result;
            }
        }

        void removedFromOrder(const BucketId& bucket) {
            _entries.erase(bucket);
        }

        void clearCache() {
            _entries.clear();
            _order.clear();
        }<commit_msg>Ai, ai, ai. Here a nasty bug has been lurking... Accessing a moved/erase object....<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * A cache for an ideal nodes implementation.
 *
 * The cache is localized for quick, localized access.
 *   - There is only one spot one request can be cached, so one can quickly
 *     look whether there is a cache entry on that spot.
 *   - Use LSB bits of bucket to lookup entry such that localized entries use
 *     separate cache spots.
 *
 *
 * Making it cheap for localized
 * access, regardless of real implementation. Basically, uses LSB bits for
 * buckets, as these are the bits that differ on localized access.
 */
#pragma once

#include <vespa/vdslib/container/lruorder.h>
#include <vespa/vdslib/distribution/idealnodecalculator.h>
#include <vespa/vespalib/stllike/hash_map.h>
#include <vespa/vespalib/util/linkedptr.h>

namespace storage {
namespace lib {

class IdealNodeCalculatorCache : public IdealNodeCalculatorConfigurable {
    typedef document::BucketId BucketId;

    /** Cache for all buckets for one given type (same upstate and nodetypes) */
    class TypeCache {
        struct Entry {
            IdealNodeList _result;
            LruOrder<BucketId, TypeCache>::EntryRef _order;
        };
        typedef vespalib::hash_map<BucketId, Entry, BucketId::hash> EntryMap;

        const IdealNodeCalculator& _calc;
        const NodeType& _nodeType;
        UpStates _upStates;
        LruOrder<BucketId, TypeCache> _order;
        EntryMap _entries;
        uint32_t _hitCount;
        uint32_t _missCount;
    public:
        typedef vespalib::LinkedPtr<TypeCache> LP;

        TypeCache(const IdealNodeCalculator& c, const NodeType& t, UpStates us, uint32_t size)
            : _calc(c), _nodeType(t), _upStates(us), _order(size, *this),
              _hitCount(0), _missCount(0)
        { }

        IdealNodeList get(const document::BucketId& bucket) {
            EntryMap::const_iterator it(_entries.find(bucket));
            if (it == _entries.end()) {
                ++_missCount;
                Entry& newEntry(_entries[bucket]);
                newEntry._result = _calc.getIdealNodes(_nodeType, bucket, _upStates);
                LruOrder<BucketId, TypeCache>::EntryRef tmpOrder = _order.add(bucket);
                Entry& movedEntry(_entries[bucket]); // The entry might very well move after the previous add.
                movedEntry._order = tmpOrder;
                return movedEntry._result;
            } else {
                ++_hitCount;
                _order.moveToStart(it->second._order);
                return it->second._result;
            }
        }

        void removedFromOrder(const BucketId& bucket) {
            _entries.erase(bucket);
        }

        void clearCache() {
            _entries.clear();
            _order.clear();
        }
{'fraction_non_alphanumeric': 0.08827708703374779, 'fraction_numerical': 0.00497335701598579, 'mean_word_length': 2.4003623188405796, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7391083', 'n_tokens_mistral': 1631, 'n_tokens_neox': 1549, 'n_words': 470}