MySQL 8.0.30
Source Code Documentation
metadata_cache.h
Go to the documentation of this file.
1/*
2 Copyright (c) 2016, 2022, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23*/
24
25#ifndef MYSQLROUTER_METADATA_CACHE_INCLUDED
26#define MYSQLROUTER_METADATA_CACHE_INCLUDED
27
29
30#include <atomic>
31#include <chrono>
32#include <exception>
33#include <list>
34#include <map>
35#include <stdexcept>
36#include <string>
37#include <vector>
38
39#include "my_rapidjson_size_t.h"
40
41#include <rapidjson/document.h>
42
44#include "mysql_router_thread.h"
50#include "tcp_address.h"
51
52namespace metadata_cache {
53constexpr const uint16_t kDefaultMetadataPort{32275};
54constexpr const std::string_view kDefaultMetadataAddress{"127.0.0.1:32275"};
55constexpr const std::string_view kDefaultMetadataUser{""};
56constexpr const std::string_view kDefaultMetadataPassword{""};
57constexpr const std::chrono::milliseconds kDefaultMetadataTTL{500};
58constexpr const std::chrono::milliseconds kDefaultAuthCacheTTL{
60constexpr const std::chrono::milliseconds kDefaultAuthCacheRefreshInterval{
61 2000};
62// blank cluster name means pick the 1st (and only) cluster
63constexpr const std::string_view kDefaultMetadataCluster{""};
64constexpr const unsigned int kDefaultConnectTimeout{
66constexpr const unsigned int kDefaultReadTimeout{
68
69constexpr const std::string_view kNodeTagHidden{"_hidden"};
70constexpr const std::string_view kNodeTagDisconnectWhenHidden{
71 "_disconnect_existing_sessions_when_hidden"};
72
73/** @class metadata_error
74 * Class that represents all the exceptions that are thrown while fetching the
75 * metadata.
76 *
77 */
78class metadata_error : public std::runtime_error {
79 public:
80 explicit metadata_error(const std::string &what_arg)
81 : std::runtime_error(what_arg) {}
82};
83
84/** @class LookupResult
85 *
86 * Class holding result after looking up data in the cache.
87 */
89 public:
90 /** @brief Constructor */
91 LookupResult(const cluster_nodes_list_t &instance_vector_)
92 : instance_vector(instance_vector_) {}
93
94 /** @brief List of ManagedInstance objects */
96};
97
98/**
99 * @brief Abstract class that provides interface for listener on
100 * cluster status changes.
101 *
102 * When state of cluster is changed, notify function is called.
103 */
105 public:
106 /**
107 * @brief Callback function that is called when state of cluster is
108 * changed.
109 *
110 * @param instances allowed nodes
111 * @param metadata_servers list of the Cluster metadata servers
112 * @param md_servers_reachable true if metadata changed, false if metadata
113 * unavailable
114 * @param view_id current metadata view_id in case of ReplicaSet cluster
115 */
117 const LookupResult &instances,
118 const metadata_cache::metadata_servers_list_t &metadata_servers,
119 const bool md_servers_reachable, const uint64_t view_id) = 0;
120
122 // disable copy as it isn't needed right now. Feel free to enable
123 // must be explicitly defined though.
125 const ClusterStateListenerInterface &) = delete;
127 const ClusterStateListenerInterface &) = delete;
129};
130
131/**
132 * @brief Abstract class that provides interface for listener on
133 * whether the listening sockets acceptors state should be updated.
134 */
136 public:
137 /**
138 * @brief Callback function that is called when the state of the sockets
139 * acceptors is handled during the metadata refresh.
140 *
141 * @param instances allowed nodes for new connections
142 */
143 virtual bool update_socket_acceptor_state(const LookupResult &instances) = 0;
144
146
148 default;
150 const AcceptorUpdateHandlerInterface &) = default;
151
154 default;
155
157};
158
159/**
160 * Abstract class that provides interface for listener on metadata refresh.
161 */
163 public:
164 /**
165 * Callback that is going to be used on each metadata refresh.
166 *
167 * @param[in] instances_changed Informs if the instances returned by the
168 * metadata refresh has changed since last md refresh.
169 * @param[in] instances List of new instances available after md refresh.
170 */
171 virtual void on_md_refresh(const bool instances_changed,
172 const LookupResult &instances) = 0;
173
175};
176/**
177 * @brief Abstract class that provides interface for adding and removing
178 * observers on cluster status changes.
179 *
180 * When state of cluster is changed, then
181 * ClusterStateListenerInterface::notify function is called
182 * for every registered observer.
183 */
185 public:
186 /**
187 * @brief Register observer that is notified when there is a change in the
188 * cluster nodes setup/state discovered.
189 *
190 * @param listener Observer object that is notified when cluster nodes
191 * state is changed.
192 *
193 * @throw std::runtime_error if metadata cache not initialized
194 */
196
197 /**
198 * @brief Unregister observer previously registered with add_state_listener()
199 *
200 * @param listener Observer object that should be unregistered.
201 *
202 * @throw std::runtime_error if metadata cache not initialized
203 */
205 ClusterStateListenerInterface *listener) = 0;
206
208 // disable copy as it isn't needed right now. Feel free to enable
209 // must be explicitly defined though.
211 const ClusterStateNotifierInterface &) = delete;
213 const ClusterStateNotifierInterface &) = delete;
215};
216
217/**
218 * @brief Metadata TTL configuration
219 */
221 // The time to live for the cached data
222 std::chrono::milliseconds ttl;
223
224 // auth_cache_ttl TTL of the rest user authentication data
225 std::chrono::milliseconds auth_cache_ttl;
226
227 // auth_cache_refresh_interval Refresh rate of the rest user authentication
228 // data
229 std::chrono::milliseconds auth_cache_refresh_interval;
230};
231
234 public:
235 /** @brief Initialize a MetadataCache object and start caching
236 *
237 * The metadata_cache::cache_init function will initialize a MetadataCache
238 * object using the given arguments and store it globally using the given
239 * cache_name.
240 *
241 * Parameters host, port, user, password are used to setup the connection with
242 * the metadata server.
243 *
244 * Cache name given by cache_name can be empty, but must be unique.
245 *
246 * The parameters connection_timeout and connection_attempts are used when
247 * connected to the metadata server.
248 *
249 * Throws a std::runtime_error when the cache object was already
250 * initialized.
251 *
252 * @param cluster_type type of the cluster the metadata cache object will
253 * represent (GR or ReplicaSet)
254 * @param router_id id of the router in the cluster metadata
255 * @param cluster_type_specific_id id of the ReplicaSet in case of the
256 * ReplicaSet, Replication Group name for GR Cluster (if bootstrapped as a
257 * single Cluster, empty otherwise)
258 * @param clusterset_id UUID of the ClusterSet the Cluster belongs to (if
259 * bootstrapped as a ClusterSet, empty otherwise)
260 * @param metadata_servers The list of cluster metadata servers
261 * @param ttl_config metadata TTL configuration
262 * @param ssl_options SSL relatd options for connection
263 * @param target_cluster object identifying the Cluster this operation refers
264 * to
265 * @param session_config Metadata MySQL session configuration
266 * @param router_attributes Router attributes to be registered in the metadata
267 * @param thread_stack_size memory in kilobytes allocated for thread's stack
268 * @param use_cluster_notifications Flag indicating if the metadata cache
269 * should use cluster notifications as an
270 * additional trigger for metadata refresh
271 * (only available for GR cluster type)
272 * @param view_id last known view_id of the cluster metadata (only relevant
273 * for ReplicaSet cluster)
274 *
275 */
276 virtual void cache_init(
277 const mysqlrouter::ClusterType cluster_type, const unsigned router_id,
278 const std::string &cluster_type_specific_id,
279 const std::string &clusterset_id,
280 const metadata_servers_list_t &metadata_servers,
281 const MetadataCacheTTLConfig &ttl_config,
282 const mysqlrouter::SSLOptions &ssl_options,
283 const mysqlrouter::TargetCluster &target_cluster,
284 const MetadataCacheMySQLSessionConfig &session_config,
285 const RouterAttributes &router_attributes,
286 size_t thread_stack_size = mysql_harness::kDefaultStackSizeInKiloBytes,
287 bool use_cluster_notifications = false, const uint64_t view_id = 0) = 0;
288
289 virtual void instance_name(const std::string &inst_name) = 0;
290 virtual std::string instance_name() const = 0;
291
292 virtual bool is_initialized() noexcept = 0;
293
294 virtual mysqlrouter::ClusterType cluster_type() const = 0;
295
296 /**
297 * @brief Start the metadata cache
298 */
299 virtual void cache_start() = 0;
300
301 /**
302 * @brief Teardown the metadata cache
303 */
304 virtual void cache_stop() noexcept = 0;
305
306 /** @brief Returns list of managed server in a HA cluster
307 * * Returns a list of MySQL servers managed by the topology for the given
308 * HA cluster.
309 *
310 * @return List of ManagedInstance objects
311 */
312 virtual LookupResult get_cluster_nodes() = 0;
313
314 /** @brief Wait until there's a primary member in the cluster
315 *
316 * To be called when the primary member of a single-primary cluster is down
317 * and we want to wait until one becomes elected.
318 *
319 * @param primary_server_uuid - server_uuid of the PRIMARY that shall be
320 * failover from.
321 * @param timeout - amount of time to wait for a failover, in seconds
322 * @return true if a primary member exists
323 */
324 virtual bool wait_primary_failover(const std::string &primary_server_uuid,
325 const std::chrono::seconds &timeout) = 0;
326
327 /**
328 * @brief Register observer that is notified when there is a change in the
329 * cluster nodes setup/state discovered.
330 *
331 * @param listener Observer object that is notified when cluster nodes
332 * state is changed.
333 */
334 void add_state_listener(ClusterStateListenerInterface *listener) override = 0;
335
336 /**
337 * @brief Unregister observer previously registered with add_state_listener()
338 *
339 * @param listener Observer object that should be unregistered.
340 */
341 void remove_state_listener(ClusterStateListenerInterface *listener) override =
342 0;
343
344 /**
345 * @brief Register observer that is notified when the state of listening
346 * socket acceptors should be updated on the next metadata refresh.
347 *
348 * @param listener Observer object that is notified when replicaset nodes
349 * state is changed.
350 */
351 virtual void add_acceptor_handler_listener(
352 AcceptorUpdateHandlerInterface *listener) = 0;
353
354 /**
355 * @brief Unregister observer previously registered with
356 * add_acceptor_handler_listener()
357 *
358 * @param listener Observer object that should be unregistered.
359 */
360 virtual void remove_acceptor_handler_listener(
361 AcceptorUpdateHandlerInterface *listener) = 0;
362
363 /**
364 * Register observer that is notified when the metadata refresh is triggered.
365 *
366 * @param listener Observer object that is notified on metadata refresh.
367 */
368 virtual void add_md_refresh_listener(
370
371 /**
372 * @brief Unregister observer previously registered with
373 * add_md_refresh_listener()
374 *
375 * @param listener Observer object that should be unregistered.
376 */
377 virtual void remove_md_refresh_listener(
379
380 /** @brief Get authentication data (password hash and privileges) for the
381 * given user.
382 *
383 * @param username - name of the user for which the authentidation data
384 * is requested
385 * @return true and password hash with privileges - authentication data
386 * requested for the given user.
387 * @return false and empty data set - username is not found or authentication
388 * data expired.
389 */
390 virtual std::pair<bool, std::pair<std::string, rapidjson::Document>>
391 get_rest_user_auth_data(const std::string &username) const = 0;
392
393 /**
394 * @brief Enable fetching authentication metadata when using metadata_cache
395 * http authentication backend.
396 */
397 virtual void enable_fetch_auth_metadata() = 0;
398
399 /**
400 * Force cache update in refresh loop.
401 */
402 virtual void force_cache_update() = 0;
403
404 /**
405 * Check values of auth_cache_ttl and auth_cache_refresh_interval timers.
406 *
407 * @throw std::invalid_argument for each of the following scenarios:
408 * 1. auth_cache_ttl < ttl
409 * 2. auth_cache_refresh_interval < ttl
410 * 3. auth_cache_refresh_interval > auth_cache_ttl
411 */
412 virtual void check_auth_metadata_timers() const = 0;
413
414 /**
415 * Toggle socket acceptors state update on next metadata refresh.
416 */
417 virtual void handle_sockets_acceptors_on_md_refresh() = 0;
418
420 // disable copy as it isn't needed right now. Feel free to enable
421 // must be explicitly defined though.
422 explicit MetadataCacheAPIBase(const MetadataCacheAPIBase &) = delete;
423 MetadataCacheAPIBase &operator=(const MetadataCacheAPIBase &) = delete;
424 ~MetadataCacheAPIBase() override = default;
425
429 std::chrono::system_clock::time_point last_refresh_succeeded;
430 std::chrono::system_clock::time_point last_refresh_failed;
431
434 };
435
437 virtual std::string cluster_type_specific_id() const = 0;
439
440 virtual std::chrono::milliseconds ttl() const = 0;
441
442 using metadata_factory_t = std::function<std::shared_ptr<MetaData>(
443 mysqlrouter::ClusterType cluster_type,
445 const mysqlrouter::SSLOptions &ssl_options,
446 const bool use_cluster_notifications, unsigned view_id)>;
447
449};
450
451// This provides a factory method that returns a pluggable instance
452// to the underlying transport layer implementation. The transport
453// layer provides the means from which the metadata is
454// fetched.
455
456std::shared_ptr<MetaData> metadata_factory_get_instance(
457 const mysqlrouter::ClusterType cluster_type,
459 const mysqlrouter::SSLOptions &ssl_options, const bool use_gr_notifications,
460 const unsigned view_id);
461
463 public:
464 static MetadataCacheAPIBase *instance();
465
466 void cache_init(const mysqlrouter::ClusterType cluster_type,
467 const unsigned router_id,
468 const std::string &cluster_type_specific_id,
469 const std::string &clusterset_id,
470 const metadata_servers_list_t &metadata_servers,
471 const MetadataCacheTTLConfig &ttl_config,
472 const mysqlrouter::SSLOptions &ssl_options,
473 const mysqlrouter::TargetCluster &target_cluster,
474 const MetadataCacheMySQLSessionConfig &session_config,
475 const RouterAttributes &router_attributes,
476 size_t thread_stack_size, bool use_cluster_notifications,
477 const uint64_t view_id) override;
478
479 mysqlrouter::ClusterType cluster_type() const override;
480
481 void instance_name(const std::string &inst_name) override;
482 std::string instance_name() const override;
483
484 std::string cluster_type_specific_id() const override;
485 mysqlrouter::TargetCluster target_cluster() const override;
486 std::chrono::milliseconds ttl() const override;
487
488 bool is_initialized() noexcept override { return is_initialized_; }
489 void cache_start() override;
490
491 void cache_stop() noexcept override;
492
493 LookupResult get_cluster_nodes() override;
494
495 bool wait_primary_failover(const std::string &primary_server_uuid,
496 const std::chrono::seconds &timeout) override;
497
498 void add_state_listener(ClusterStateListenerInterface *listener) override;
499
500 void remove_state_listener(ClusterStateListenerInterface *listener) override;
501
502 void add_acceptor_handler_listener(
503 AcceptorUpdateHandlerInterface *listener) override;
504
505 void remove_acceptor_handler_listener(
506 AcceptorUpdateHandlerInterface *listener) override;
507
508 void add_md_refresh_listener(
509 MetadataRefreshListenerInterface *listener) override;
510
511 void remove_md_refresh_listener(
512 MetadataRefreshListenerInterface *listener) override;
513
514 RefreshStatus get_refresh_status() override;
515
516 std::pair<bool, std::pair<std::string, rapidjson::Document>>
517 get_rest_user_auth_data(const std::string &user) const override;
518
519 void enable_fetch_auth_metadata() override;
520 void force_cache_update() override;
521 void check_auth_metadata_timers() const override;
522
523 void handle_sockets_acceptors_on_md_refresh() override;
524
525 void set_instance_factory(metadata_factory_t cb) override {
526 instance_factory_ = std::move(cb);
527 }
528
529 private:
531
532 struct InstData {
533 std::string name;
534 };
536
537 std::atomic<bool> is_initialized_{false};
538 MetadataCacheAPI() = default;
541};
542
543} // namespace metadata_cache
544
545#endif // MYSQLROUTER_METADATA_CACHE_INCLUDED
Monitor pattern.
Definition: monitor.h:38
Abstract class that provides interface for listener on whether the listening sockets acceptors state ...
Definition: metadata_cache.h:135
AcceptorUpdateHandlerInterface & operator=(AcceptorUpdateHandlerInterface &&)=default
virtual bool update_socket_acceptor_state(const LookupResult &instances)=0
Callback function that is called when the state of the sockets acceptors is handled during the metada...
AcceptorUpdateHandlerInterface & operator=(const AcceptorUpdateHandlerInterface &)=default
AcceptorUpdateHandlerInterface(AcceptorUpdateHandlerInterface &&)=default
AcceptorUpdateHandlerInterface(const AcceptorUpdateHandlerInterface &)=default
Abstract class that provides interface for listener on cluster status changes.
Definition: metadata_cache.h:104
virtual void notify_instances_changed(const LookupResult &instances, const metadata_cache::metadata_servers_list_t &metadata_servers, const bool md_servers_reachable, const uint64_t view_id)=0
Callback function that is called when state of cluster is changed.
ClusterStateListenerInterface & operator=(const ClusterStateListenerInterface &)=delete
ClusterStateListenerInterface(const ClusterStateListenerInterface &)=delete
Abstract class that provides interface for adding and removing observers on cluster status changes.
Definition: metadata_cache.h:184
ClusterStateNotifierInterface(const ClusterStateNotifierInterface &)=delete
ClusterStateNotifierInterface & operator=(const ClusterStateNotifierInterface &)=delete
virtual void add_state_listener(ClusterStateListenerInterface *listener)=0
Register observer that is notified when there is a change in the cluster nodes setup/state discovered...
virtual void remove_state_listener(ClusterStateListenerInterface *listener)=0
Unregister observer previously registered with add_state_listener()
Class holding result after looking up data in the cache.
Definition: metadata_cache.h:88
LookupResult(const cluster_nodes_list_t &instance_vector_)
Constructor.
Definition: metadata_cache.h:91
const cluster_nodes_list_t instance_vector
List of ManagedInstance objects.
Definition: metadata_cache.h:95
Definition: metadata_cache.h:233
virtual std::string cluster_type_specific_id() const =0
virtual std::string instance_name() const =0
virtual void cache_init(const mysqlrouter::ClusterType cluster_type, const unsigned router_id, const std::string &cluster_type_specific_id, const std::string &clusterset_id, const metadata_servers_list_t &metadata_servers, const MetadataCacheTTLConfig &ttl_config, const mysqlrouter::SSLOptions &ssl_options, const mysqlrouter::TargetCluster &target_cluster, const MetadataCacheMySQLSessionConfig &session_config, const RouterAttributes &router_attributes, size_t thread_stack_size=mysql_harness::kDefaultStackSizeInKiloBytes, bool use_cluster_notifications=false, const uint64_t view_id=0)=0
Initialize a MetadataCache object and start caching.
virtual std::chrono::milliseconds ttl() const =0
virtual void instance_name(const std::string &inst_name)=0
virtual bool is_initialized() noexcept=0
virtual mysqlrouter::TargetCluster target_cluster() const =0
virtual RefreshStatus get_refresh_status()=0
virtual void set_instance_factory(metadata_factory_t cb)=0
std::function< std::shared_ptr< MetaData >(mysqlrouter::ClusterType cluster_type, const metadata_cache::MetadataCacheMySQLSessionConfig &session_config, const mysqlrouter::SSLOptions &ssl_options, const bool use_cluster_notifications, unsigned view_id)> metadata_factory_t
Definition: metadata_cache.h:446
Definition: metadata_cache.h:462
bool is_initialized() noexcept override
Definition: metadata_cache.h:488
MetadataCacheAPI & operator=(const MetadataCacheAPI &)=delete
MetadataCacheAPI(const MetadataCacheAPI &)=delete
Abstract class that provides interface for listener on metadata refresh.
Definition: metadata_cache.h:162
virtual void on_md_refresh(const bool instances_changed, const LookupResult &instances)=0
Callback that is going to be used on each metadata refresh.
Class that represents all the exceptions that are thrown while fetching the metadata.
Definition: metadata_cache.h:78
metadata_error(const std::string &what_arg)
Definition: metadata_cache.h:80
static constexpr int kDefaultReadTimeout
Definition: mysql_session.h:157
static constexpr int kDefaultConnectTimeout
Definition: mysql_session.h:156
Definition: cluster_metadata.h:146
#define METADATA_CACHE_EXPORT
Definition: metadata_cache_export.h:15
Define rapidjson::SizeType to be std::size_t.
char * user
Definition: mysqladmin.cc:59
static bool timeout(bool(*wait_condition)())
Timeout function.
Definition: log0meb.cc:495
Definition: metadata_cache.h:52
constexpr const std::string_view kDefaultMetadataCluster
Definition: metadata_cache.h:63
constexpr const std::string_view kDefaultMetadataUser
Definition: metadata_cache.h:55
std::vector< metadata_server_t > metadata_servers_list_t
Definition: metadata_cache_datatypes.h:140
constexpr const std::string_view kNodeTagHidden
Definition: metadata_cache.h:69
std::vector< ManagedInstance > cluster_nodes_list_t
Definition: metadata_cache_datatypes.h:136
constexpr const std::string_view kDefaultMetadataAddress
Definition: metadata_cache.h:54
constexpr const std::chrono::milliseconds kDefaultMetadataTTL
Definition: metadata_cache.h:57
constexpr const std::chrono::milliseconds kDefaultAuthCacheTTL
Definition: metadata_cache.h:58
constexpr const std::string_view kDefaultMetadataPassword
Definition: metadata_cache.h:56
constexpr const unsigned int kDefaultConnectTimeout
Definition: metadata_cache.h:64
constexpr const std::chrono::milliseconds kDefaultAuthCacheRefreshInterval
Definition: metadata_cache.h:60
constexpr const uint16_t kDefaultMetadataPort
Definition: metadata_cache.h:53
constexpr const unsigned int kDefaultReadTimeout
Definition: metadata_cache.h:66
constexpr const std::string_view kNodeTagDisconnectWhenHidden
Definition: metadata_cache.h:70
std::shared_ptr< MetaData > metadata_factory_get_instance(const mysqlrouter::ClusterType cluster_type, const metadata_cache::MetadataCacheMySQLSessionConfig &session_config, const mysqlrouter::SSLOptions &ssl_options, const bool use_gr_notifications, const unsigned view_id)
Return an instance of cluster metadata.
Definition: metadata_factory.cc:47
static const size_t kDefaultStackSizeInKiloBytes
Definition: mysql_router_thread.h:43
Definition: dim.h:357
ClusterType
Definition: cluster_metadata.h:128
Definition: my_rapidjson_size_t.h:37
Definition: varlen_sort.h:183
std::string last_metadata_server_host
Definition: metadata_cache.h:432
uint64_t refresh_failed
Definition: metadata_cache.h:427
std::chrono::system_clock::time_point last_refresh_failed
Definition: metadata_cache.h:430
uint16_t last_metadata_server_port
Definition: metadata_cache.h:433
uint64_t refresh_succeeded
Definition: metadata_cache.h:428
std::chrono::system_clock::time_point last_refresh_succeeded
Definition: metadata_cache.h:429
Definition: metadata_cache.h:532
std::string name
Definition: metadata_cache.h:533
Metadata MySQL session configuration.
Definition: metadata_cache_datatypes.h:192
Metadata TTL configuration.
Definition: metadata_cache.h:220
std::chrono::milliseconds auth_cache_refresh_interval
Definition: metadata_cache.h:229
std::chrono::milliseconds ttl
Definition: metadata_cache.h:222
std::chrono::milliseconds auth_cache_ttl
Definition: metadata_cache.h:225
Definition: metadata_cache_datatypes.h:208
SSL connection related options.
Definition: datatypes.h:38
double seconds()
Definition: task.cc:309