-
Notifications
You must be signed in to change notification settings - Fork 1
Writing a store
Under construction
The easiest way to write a store_plugin, is to look at an existing one, like store_flatfile.
Required functions for all samplers are defined in the ldmsd_store struct definition, near the bottom of the file. This defines the specific functions to be called at various points. The get_plugin function returns the struct for this plugin.
.base = { .name = "csv", .type = LDMSD_PLUGIN_STORE, .term = term, .config = config, .usage = usage, }, .open = open_store, .get_context = get_ucontext, .store = store, .flush = flush_store, .close = close_store,
struct ldmsd_plugin *get_plugin(ldmsd_msg_log_f pf) { msglog = pf; PG.msglog = pf; PG.pname = PNAME; return &store_csv.base; }
About the functions:
- usage_ -- output for the help, which defines the usage.
- config -- called at configuration.
- store -- the actual storing
- get_context -- stores have a void* context which is returned by this call.
- term -- called when the store plugin terminates.
- open -- the store group opens individual stores (e.g., csv files) have the option of opening on demand.
- close -- the store group closes individual stores.
- flush -- individual stores (e.g., csv files) can have the option of flushing on demand. You might choose to do this if you have been relying on system buffering to flush, but you want to trigger a flush in order to ensure that the data is written to the store.
- msglog -- wrapper call for logging. Log levels are: LDMSD_LDEBUG, LDMSD_LINFO, LDMSD_LWARNING, LDMSD_LERROR, LDMSD_LCRITICAL, LDMSD_LALL.
- SAMP -- #define for the store plugin name, for convenience (e.g., "meminfo"); not used for csv, but could be.
get_plugin is called when the load call on the plugin is called.
Config is invoked by the config command. Arguments from the config are passed in the attr_value_list *avl. They can be extracted by name, as shown below. av_value returns null if there is no attribute with that name:
char *value; value = av_value(avl, "path"); if (!value) goto err;
You may want to prevent config from being called multiple times if your code is not prepared to handle the implications of the changes in config. For example, if your config creates a file, then subsequent calls to config on a running sampler, would have to handle the how these changes impact the current file, particularly while storing is occurring.
At the time of config information about the sets to be stored is unknown.
If you are using a configuration file, a non-zero return from config will abort processing the configuration file.
open on a store is called (roughly) when the store_group's store_handle is null, which is when the store_group is started or when the store_group is explicitly closed. So in many cases, the open may only be called once.
At this point, the schema name is known, and, via the metric_list, the metric names and types, however if there is an array, the size is not known. This limits the ability to make some headers at this point. Only at the store call are the values, and hence the number of values in an array, are known.
store is handed as parameters the store_handle, the set, and an array of metric indicies with the length of that array.
a) Only at the store call are the values, and hence the number of values in an array, are known. Thus some stores (e.g., store_csv) which print a header do so at this point.
b) get the timestamp for the set
ldms_transaction_timestamp_get(set);
c) get the name of the producer
pname = ldms_set_producer_name_get(set);
d) iterate through the metrics, check the type to write the value correctly
for (i = 0; i != metric_count; i++) { enum ldms_value_type metric_type = ldms_metric_type_get(set, metric_array[i]); //use same formats as ldms_ls switch (metric_type){ case LDMS_V_U64: rc = fprintf(s_handle->file, ",%"PRIu64, ldms_metric_get_u64(set, metric_array[i])); if (rc < 0) msglog(LDMSD_LERROR, PNAME ": Error %d writing to '%s'\n", rc, s_handle->path); break;
close can be used to close a store, for example closing a file handle. Closing a store manually, does not result in that state being transmitted to the store group. The store_group being stopped will call each of its stores to be closed.
flush can be used to trigger a flush to a store on demand. The store_group does not have a flush, however you can takes flush information as part of config and use this to trigger the flush at appropriate moments (store_csv does this). You might choose to do this if you have been relying on system buffering to flush, but you want to trigger a flush in order to ensure that the data is written to the store.
struct flatfile_store_instance *si = _sh; struct flatfile_metric_store *ms; LIST_FOREACH(ms, &si->ms_list, entry) { pthread_mutex_lock(&ms->lock); lrc = fflush(ms->file); if (lrc) { rc = lrc; eno = errno; msglog(LDMSD_LERROR, "Errro %d: %s at %s:%d\n", eno, strerror(eno), __FILE__, __LINE__); } pthread_mutex_unlock(&ms->lock); }
usage returns the usage (in most cases the config) information>
return " config name=store_flatfile path=<path>\n" " - Set the root path for the storage of flatfiles.\n" " path The path to the root of the flatfile directory\n";
term is called when the plugin is terminated.
While samplers might frequently have only a single set, it is far more common that store_plugins will have multiple targets to which they are storing. The store_plugin may define a way to store (e.g., csv files) for a variety of sets (or schema) or even for variables within a set. Thus, more bookkeeping may be required for a store_plugin. A typical construction is that a store_plugin has an array/list of each schema and its associated target.
Typically two types of locks are needed in implementing a store.
One is for configuration changes and possibly related changes, such as maintaining the array of schema and targets:
static pthread_mutex_t cfg_lock;
pthread_mutex_lock(&cfg_lock); root_path = strdup(value); pthread_mutex_unlock(&cfg_lock);
The other is a per-target lock, which is used when writing to a store and changes like rolling over a store:
static int store(ldmsd_store_handle_t _sh, ldms_set_t set, int *metric_arry, size_t metric_count)
{
...
enum ldms_value_type metric_type = ldms_metric_type_get(set, metric_arry[i]); switch (metric_type) { ... case LDMS_V_U64: rc = fprintf(si->ms[i]->file, " %"PRIu64"\n", ldms_metric_get_u64(set, metric_arry[i])); break; } pthread_mutex_unlock(&si->ms[i]->lock); }
- The metric_sets have a single timestamp for a group of variables. This can facilitate analysis. The store can split up, or further process, the variables as desired.
- rollover - stores that write to files can result in large files. Features like rollover can limit file sizes and enable easier determination of files of interest. Log management capabilities such as logrotate could handle some issues, however, for sufficiently large files the copy of log rotate can be time intensive and a file handle swap can invalidate the open file handle of the store, so it may be more efficient to do the file management directly in the store (many of the stores do this).
- performance - A variety of considerations around write performance, affect on ldmsd daemons, open file handles are discussed at Configuration-Considerations-and-Best-Practices-(v4)
Contributed store_plugins will go under a contrib directory in their own directory, as described in Contributing. Include not only the store_plugin, but also the Makefile.am, and the man page for the plugin.
Currently in v4, the schema names have to be explicitly supplied to the strgp_add line:
strgp_add name=store_flatfile plugin=store_flatfile schema=meminfo container=fmeminfo
This means that dynamically creating schema in the sampler will not automatically result in the store_plugin handling the new schema. Options for this are in consideration for v4.
New instances of a schema are discovered by the daemon hosting the store plugin and pass those to the store_plugin. The stores and the samplers can be started in either order.
a) config -- Multiple calls to config are possible, and, possibly, even desirable. In these cases, different actions in configuration are typically distinguished by including an action parameter in the config line. Examples may be when you want to provide a set of default configuration parameters and some overrides for particular stores.
b) store -- store is called repeatedly. The store_group does not check the return value of the store. Therefore there is no way to know if the store call actually completed properly.
c) schema names -- no ldmsd nor any current store checks that a schema has not changed. This may happen via any of the following:
- a sampler on each two different types of nodes might be generating different schema contents but with the same name (e.g, if two nodes have a different architecture and hence different entires in a source in /proc)
- a sampler plugin in a running daemon has been explicitly stopped and restarted with a different schema contents but with the same name
- a sampler daemon has died and is restarted, again with the sampler plugin defining different contents with the same schema name.
- Home
- Search
- Feature Overview
- LDMS Data Facilitates Analysis
- Contributing patches
- User Group Meeting Notes - BiWeekly!
- Publications
- News - now in Discussions
- Mailing Lists
- Help
Tutorials are available at the conference websites
- Coming soon!
- Testing Overview
- Test Plans & Documentation: ldms-test
- Man pages currently not posted, but they are available in the source and build
V3 has been deprecated and will be removed soon
- Configuring
- Configuration Considerations
- Running