aboutsummaryrefslogtreecommitdiffstats
path: root/storage/src/vespa/storageframework/generic/component/component.h
blob: fc4b934e7e3a19474f85b01ce4e55d379ca01162 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
 * \class storage::framework::Component
 * \ingroup component
 *
 * \brief Application component class
 *
 * The component class is used to give a component of an application a set of
 * generic tools without depending on the implementation of these.
 *
 * This class should not depend on the actual implementation of what it serves.
 * Neither in the header file nor the object file. Implementations will be
 * provided by the application server.
 *
 * Services given should all be generic features. Application specific stuff
 * we should handle in another way. The types of services offered are split in
 * two.
 *
 *   1. Services component implementation has that it registers in the component
 *      such that application server can access this. Metrics and status
 *      reporting are two such services.
 *   2. Services provided through application server, that the application
 *      server will inject into the component before it is opened for use.
 *      Clock, thread pool and memory management are examples of such services.
 *
 * The services offered with a short summary of what they provide are as
 * follows:
 *
 *  - Status reporters can register themselves as a reported in the component.
 *    A status server, for instance serving status information through a web
 *    server can thus fetch status pages wanted by clients and serve them.
 *    Status reporters thus don't need to know how status information is used.
 *
 *  - A metric set can be registered, with a path for where in the application
 *    metric set it should exist. This way, the components do not have to know
 *    about metric management and the implementation of the metric manager.
 *
 *  - A metric update hook can be registered. This will be called by the metric
 *    implementation at regular intervals or just before snapshotting/reporting.
 *
 *  - A clock object is given. Using a common clock component all over the
 *    application makes us able to fake the clock in testing environments.
 *    Fetching current time is also a somewhat expensive operations we might
 *    do often, so having this common object to fetch it, we can easily
 *    optimize clock fetching as we see fit later.
 *
 *  - A thread pool is given. This makes us able to use a thread pool.
 *    (Allthough currently we don't really need a thread pool, as
 *    threads typically live for the whole lifetime of the
 *    server. Another feature of this is that the thread interface has
 *    built in information needed to detect deadlocks and report
 *    status about thread behavior, such that deadlock detecting and
 *    thread status can be shown without the threads themselves
 *    depending on how this is done.
 *
 *  - A memory manager may also be provided, allowing components to request
 *    memory from a global pool, in order to let the application prioritize
 *    where to use memory. Again, this removes the dependency of how it is
 *    actually implemented to the components using it.
 *
 * Currently it is assumed that components are set up at application
 * initialization time, and that they live as long as the application. Thus no
 * unregister functionality is provided. Services that use registered status
 * reporters or metric sets will shut down before the component is deleted,
 * such that the component can be safely deleted without any unregistering
 * needed.
 */
#pragma once

#include "managedcomponent.h"
#include <vespa/vespalib/util/cpu_usage.h>
#include <atomic>
#include <optional>

namespace storage::framework {

struct ComponentRegister;
struct Runnable;
class Thread;

class Component : private ManagedComponent
{
    ComponentRegister* _componentRegister;
    vespalib::string _name;
    const StatusReporter* _status;
    metrics::Metric* _metric;
    ThreadPool* _threadPool;
    MetricRegistrator* _metricReg;
    std::pair<MetricUpdateHook*, vespalib::system_time::duration> _metricUpdateHook;
    const Clock* _clock;

    // ManagedComponent implementation
    metrics::Metric* getMetric() override { return _metric; }
    const StatusReporter* getStatusReporter() override { return _status; }
    void setMetricRegistrator(MetricRegistrator& mr) override;
    void setClock(Clock& c) override { _clock = &c; }
    void setThreadPool(ThreadPool& tp) override { _threadPool = &tp; }
    void open() override;
    void close() override;

public:
    using UP = std::unique_ptr<Component>;

    Component(ComponentRegister&, vespalib::stringref name);
    ~Component() override;

    /**
     * Register a status page, which might be visible to others through a
     * component showing status of components. Only one status page can be
     * registered per component. Use URI parameters in order to distinguish
     * multiple pages.
     */
    void registerStatusPage(const StatusReporter&);

    /**
     * Register a metric (typically a metric set) used by this component. Only
     * one metric set can be registered per component. Register a metric set in
     * order to register many metrics within the component.
     */
    void registerMetric(metrics::Metric&);

    /**
     * Register a metric update hook. Only one per component. Note that the
     * update hook will only be called if there actually is a metric mananger
     * component registered in the application.
     */
    void registerMetricUpdateHook(MetricUpdateHook&, vespalib::system_time::duration period);

    /** Get the name of the component. Must be a unique name. */
    [[nodiscard]] const vespalib::string& getName() const override { return _name; }

    /**
     * Get the thread pool for this application. Note that this call will fail
     * before the application has registered a threadpool. Applications are
     * encouraged to register a threadpool before adding components to avoid
     * needing components to wait before accessing threadpool.
     */
    [[nodiscard]] ThreadPool& getThreadPool() const;

    /**
     * Get the clock used in this application. This function will fail before
     * the application has registered a clock implementation. Applications are
     * encourated to register a clock implementation before adding components to
     * avoid needing components to delay using it.
     */
    [[nodiscard]] const Clock& getClock() const { return *_clock; }

    /**
     * Helper functions for components wanting to start a single thread.
     * If max wait time is not set, we assume process time includes waiting.
     * If max process time is not set, deadlock detector cannot detect deadlocks
     * in this thread. (Thus one is not required to call registerTick())
     */
    std::unique_ptr<Thread> startThread(Runnable&,
                                        vespalib::duration maxProcessTime = vespalib::duration::zero(),
                                        vespalib::duration waitTime = vespalib::duration::zero(),
                                        int ticksBeforeWait = 1,
                                        std::optional<vespalib::CpuUsage::Category> cpu_category = std::nullopt) const;

    void requestShutdown(vespalib::stringref reason);

};

}