summaryrefslogtreecommitdiffstats
path: root/storage/src/vespa/storage/frameworkimpl/memory/memorystatusviewer.h
blob: 3ad378db78ee085b44eb1a9dde2307fbffc8f949 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * \class storage::MemoryStatusViewer
 *
 * \brief Generates status to access through status pages.
 *
 * Keeps a history of the largest memory inprints seen historically. This is
 * done be defining periods, where a period is always a multiplum of the length
 * of the period shorter than it. The last entry will store the biggest memory
 * imprint ever seen, and the earlier entries will show biggest for their time
 * period.
 *
 * To avoid having all periods cleared once the biggest period resets, the
 * periods keep data for each of the periods one size below it. Thus, a year
 * keeps data for 12 months, a month for 30 days, and so on.
 *
 * The memory state objects are divided in 3 parts. Current memory data, max
 * memory data since since reset and counts for how often various events have
 * happened.
 *
 * The counts will have their total count values stored in the current entry.
 * When the next period is updated getting a copy of these counts, we can see
 * how many counts have happened recently, by taking the current entry and
 * subtract those accounted for earlier.
 *
 * The current memory data will not be interesting for anything than to show the
 * actual now values in the current entry.
 *
 * The max since reset values will be the values used for the various periods.
 * When a period is updated with new data for a subpart of their period, the
 * max seen data is reset in the period in front, such that a lower maximum
 * can be found.
 */

#pragma once

#include <vespa/storage/common/storagecomponent.h>
#include <vespa/storageframework/defaultimplementation/memory/memorystate.h>
#include <vespa/storageframework/generic/status/htmlstatusreporter.h>
#include <vespa/vespalib/util/document_runnable.h>
#include <vespa/vespalib/util/sync.h>
#include <deque>
#include <vector>


namespace metrics {
    class MetricManager;
}

namespace storage {

class StorageServerInterface;

class MemoryStatusViewer : public framework::HtmlStatusReporter,
                           private framework::Runnable
{
public:
    typedef framework::defaultimplementation::MemoryState::SnapShot SnapShot;
    struct Entry {
        typedef std::shared_ptr<Entry> SP;

        std::string _name;
        framework::SecondTime _maxAge;
        framework::SecondTime _timeTaken;
        SnapShot _data;
        uint64_t _maxMemory;

        Entry(const std::string& name, framework::Clock&,
              framework::SecondTime maxAge);
        bool containsData() const { return (_maxMemory != 0); }

        void assign(const SnapShot& snapshot, uint64_t maxMemory,
                    framework::SecondTime time)
        {
            _data = snapshot;
            _maxMemory = maxMemory;
            _timeTaken = time;
        }
    };

    struct MemoryTimeEntry {
        uint64_t used;
        uint64_t usedWithoutCache;

        MemoryTimeEntry(uint64_t u, uint64_t wo)
            : used(u), usedWithoutCache(wo) {}

        void keepMax(const MemoryTimeEntry& e) {
            used = (used > e.used ? used : e.used);
            usedWithoutCache = (usedWithoutCache > e.usedWithoutCache
                    ? usedWithoutCache : e.usedWithoutCache);
        }
    };

private:
    framework::Component _component;
    framework::defaultimplementation::MemoryManager& _manager;
    const metrics::MetricManager& _metricManager;
    vespalib::Monitor _workerMonitor;

    std::vector<Entry::SP> _states;
    std::deque<MemoryTimeEntry> _memoryHistory;
    uint32_t _memoryHistorySize;
    framework::SecondTime _memoryHistoryPeriod;
    framework::SecondTime _allowedSlackPeriod;
    framework::SecondTime _lastHistoryUpdate;
    framework::Thread::UP _thread;
    framework::SecondTime _processedTime;

    void addEntry(const std::string& name, uint32_t maxAge) {
        _states.push_back(Entry::SP(new Entry(name, _component.getClock(),
                                              framework::SecondTime(maxAge))));
    }
    void run(framework::ThreadHandle&) override;
    void grabMemoryUsage();
    void printSnapshot(std::ostream& out, Entry& entry,
                       std::map<const framework::MemoryAllocationType*,
                                uint32_t>& colors) const;

public:
    MemoryStatusViewer(
            framework::defaultimplementation::MemoryManager&,
            const metrics::MetricManager&,
            StorageComponentRegister&);
    ~MemoryStatusViewer();

    void reportHtmlHeaderAdditions(std::ostream&, const framework::HttpUrlPath&) const override;
    void reportHtmlStatus(std::ostream&, const framework::HttpUrlPath&) const override;

    /** Useful for testing. */
    framework::SecondTime getProcessedTime() const { return _processedTime; }
    void notifyThread() const;
    void printDebugOutput(std::ostream&) const;

};

}