Repository: VictoriaMetrics/VictoriaMetrics
Pull request #1050 (2021-02-03 16:16:54)

Adds ability to query right vmselect endpoint based on the query type
If you use `vmselect` you have to specify the right endpoint for the alerts query type: `/prometheus` or `/graphite`.

This PR adds an optional `-datasource.appendTypePrefix` flag to `vmalert` to determine the right datasource path. It enables us to specify only base vmselect path as `-datasource.url`, for example: `<vmselect-addr>/select/<tennant>/` and the right prefix will be added based on the alert query type.


Patch:
diff --git a/README.md b/README.md
index 02c4afa76..2107f4cb3 100644
--- a/README.md
+++ b/README.md
@@ -812,10 +812,7 @@ The exported data can be imported to VictoriaMetrics via [/api/v1/import/native]
 
 ### How to export data in JSON line format
 
-Consider [exporting data in native format](#how-to-export-data-in-native-format) if big amounts of data must be migrated between VictoriaMetrics instances,
-since exporting in native format usually consumes lower amounts of CPU and memory resources, while the resulting exported data occupies lower amounts of disk space.
-
-In order to export data in JSON line format, send a request to `http://<victoriametrics-addr>:8428/api/v1/export?match[]=<timeseries_selector_for_export>`,
+Send a request to `http://<victoriametrics-addr>:8428/api/v1/export?match[]=<timeseries_selector_for_export>`,
 where `<timeseries_selector_for_export>` may contain any [time series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors)
 for metrics to export. Use `{__name__!=""}` selector for fetching all the time series.
 The response would contain all the data for the selected time series in [JSON streaming format](https://en.wikipedia.org/wiki/JSON_streaming#Line-delimited_JSON).
@@ -1365,10 +1362,8 @@ See the example of alerting rules for VM components [here](https://github.com/Vi
   This prevents from ingesting metrics with too many labels. It is recommended [monitoring](#monitoring) `vm_metrics_with_dropped_labels_total`
   metric in order to determine whether `-maxLabelsPerTimeseries` must be adjusted for your workload.
 
-* If you store Graphite metrics like `foo.bar.baz` in VictoriaMetrics, then `-search.treatDotsAsIsInRegexps` command-line flag could be useful.
-  By default `.` chars in regexps match any char. If you need matching only dots, then the `\\.` must be used in regexp filters.
-  When `-search.treatDotsAsIsInRegexps` option is enabled, then dots in regexps are automatically escaped in order to match only dots instead of arbitrary chars.
-  This may significantly increase performance when locating time series for the given label filters.
+* If you store Graphite metrics like `foo.bar.baz` in VictoriaMetrics, then use `{__graphite__="foo.*.baz"}` syntax for selecting such metrics.
+  This expression is equivalent to `{__name__=~"foo[.][^.]*[.]baz"}`, but it works faster and it is easier to use when migrating from Graphite.
 
 * VictoriaMetrics ignores `NaN` values during data ingestion.
 
diff --git a/app/vmagent/README.md b/app/vmagent/README.md
index d2d48e5ec..bf40e17ba 100644
--- a/app/vmagent/README.md
+++ b/app/vmagent/README.md
@@ -388,3 +388,227 @@ curl -s http://<vmagent-host>:8429/debug/pprof/profile > cpu.pprof
 The command for collecting CPU profile waits for 30 seconds before returning.
 
 The collected profiles may be analyzed with [go tool pprof](https://github.com/google/pprof).
+
+
+## Advanced usage
+
+`vmagent` can be fine-tuned with various command-line flags. Run `./vmagent -help` in order to see the full list of these flags with their desciptions and default values:
+
+```
+./vmagent -help
+
+vmagent collects metrics data via popular data ingestion protocols and routes it to VictoriaMetrics.
+
+See the docs at https://victoriametrics.github.io/vmagent.html .
+
+  -csvTrimTimestamp duration
+    	Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -dryRun
+    	Whether to check only config files without running vmagent. The following files are checked: -promscrape.config, -remoteWrite.relabelConfig, -remoteWrite.urlRelabelConfig . Unknown config entries are allowed in -promscrape.config by default. This can be changed with -promscrape.config.strictParse
+  -enableTCP6
+    	Whether to enable IPv6 for listening and dialing. By default only IPv4 TCP is used
+  -envflag.enable
+    	Whether to enable reading flags from environment variables additionally to command line. Command line flag values have priority over values from environment vars. Flags are read only from command line if this flag isn't set
+  -envflag.prefix string
+    	Prefix for environment variables if -envflag.enable is set
+  -fs.disableMmap
+    	Whether to use pread() instead of mmap() for reading data files. By default mmap() is used for 64-bit arches and pread() is used for 32-bit arches, since they cannot read data files bigger than 2^32 bytes in memory. mmap() is usually faster for reading small data chunks than pread()
+  -graphiteListenAddr string
+    	TCP and UDP address to listen for Graphite plaintext data. Usually :2003 must be set. Doesn't work if empty
+  -graphiteTrimTimestamp duration
+    	Trim timestamps for Graphite data to this duration. Minimum practical duration is 1s. Higher duration (i.e. 1m) may be used for reducing disk space usage for timestamp data (default 1s)
+  -http.connTimeout duration
+    	Incoming http connections are closed after the configured timeout. This may help spreading incoming load among a cluster of services behind load balancer. Note that the real timeout may be bigger by up to 10% as a protection from Thundering herd problem (default 2m0s)
+  -http.disableResponseCompression
+    	Disable compression of HTTP responses for saving CPU resources. By default compression is enabled to save network bandwidth
+  -http.idleConnTimeout duration
+    	Timeout for incoming idle http connections (default 1m0s)
+  -http.maxGracefulShutdownDuration duration
+    	The maximum duration for graceful shutdown of HTTP server. Highly loaded server may require increased value for graceful shutdown (default 7s)
+  -http.pathPrefix string
+    	An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
+  -http.shutdownDelay duration
+    	Optional delay before http server shutdown. During this dealy the servier returns non-OK responses from /health page, so load balancers can route new requests to other servers
+  -httpAuth.password string
+    	Password for HTTP Basic Auth. The authentication is disabled if -httpAuth.username is empty
+  -httpAuth.username string
+    	Username for HTTP Basic Auth. The authentication is disabled if empty. See also -httpAuth.password
+  -httpListenAddr string
+    	TCP address to listen for http connections. Set this flag to empty value in order to disable listening on any port. This mode may be useful for running multiple vmagent instances on the same server. Note that /targets and /metrics pages aren't available if -httpListenAddr='' (default ":8429")
+  -import.maxLineLen max_rows_per_line
+    	The maximum length in bytes of a single line accepted by /api/v1/import; the line length can be limited with max_rows_per_line query arg passed to /api/v1/export
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 104857600)
+  -influx.maxLineSize value
+    	The maximum size in bytes for a single Influx line during parsing
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 262144)
+  -influxListenAddr http://<vmagent>:8429/write
+    	TCP and UDP address to listen for Influx line protocol data. Usually :8189 must be set. Doesn't work if empty. This flag isn't needed when ingesting data over HTTP - just send it to http://<vmagent>:8429/write
+  -influxMeasurementFieldSeparator string
+    	Separator for '{measurement}{separator}{field_name}' metric name when inserted via Influx line protocol (default "_")
+  -influxSkipMeasurement
+    	Uses '{field_name}' as a metric name while ignoring '{measurement}' and '-influxMeasurementFieldSeparator'
+  -influxSkipSingleField
+    	Uses '{measurement}' instead of '{measurement}{separator}{field_name}' for metic name if Influx line contains only a single field
+  -influxTrimTimestamp duration
+    	Trim timestamps for Influx line protocol data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -insert.maxQueueDuration duration
+    	The maximum duration for waiting in the queue for insert requests due to -maxConcurrentInserts (default 1m0s)
+  -loggerDisableTimestamps
+    	Whether to disable writing timestamps in logs
+  -loggerErrorsPerSecondLimit int
+    	Per-second limit on the number of ERROR messages. If more than the given number of errors are emitted per second, then the remaining errors are suppressed. Zero value disables the rate limit
+  -loggerFormat string
+    	Format for logs. Possible values: default, json (default "default")
+  -loggerLevel string
+    	Minimum level of errors to log. Possible values: INFO, WARN, ERROR, FATAL, PANIC (default "INFO")
+  -loggerOutput string
+    	Output for the logs. Supported values: stderr, stdout (default "stderr")
+  -loggerTimezone string
+    	Timezone to use for timestamps in logs. Local timezone can be used (default "UTC")
+  -loggerWarnsPerSecondLimit int
+    	Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero value disables the rate limit
+  -maxConcurrentInserts int
+    	The maximum number of concurrent inserts. Default value should work for most cases, since it minimizes the overhead for concurrent inserts. This option is tigthly coupled with -insert.maxQueueDuration (default 16)
+  -maxInsertRequestSize value
+    	The maximum size in bytes of a single Prometheus remote_write API request
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 33554432)
+  -memory.allowedBytes value
+    	Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to non-zero value. Too low value may increase cache miss rate, which usually results in higher CPU and disk IO usage. Too high value may evict too much data from OS page cache, which will result in higher disk IO usage
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 0)
+  -memory.allowedPercent float
+    	Allowed percent of system memory VictoriaMetrics caches may occupy. See also -memory.allowedBytes. Too low value may increase cache miss rate, which usually results in higher CPU and disk IO usage. Too high value may evict too much data from OS page cache, which will result in higher disk IO usage (default 60)
+  -metricsAuthKey string
+    	Auth key for /metrics. It overrides httpAuth settings
+  -opentsdbHTTPListenAddr string
+    	TCP address to listen for OpentTSDB HTTP put requests. Usually :4242 must be set. Doesn't work if empty
+  -opentsdbListenAddr string
+    	TCP and UDP address to listen for OpentTSDB metrics. Telnet put messages and HTTP /api/put messages are simultaneously served on TCP port. Usually :4242 must be set. Doesn't work if empty
+  -opentsdbTrimTimestamp duration
+    	Trim timestamps for OpenTSDB 'telnet put' data to this duration. Minimum practical duration is 1s. Higher duration (i.e. 1m) may be used for reducing disk space usage for timestamp data (default 1s)
+  -opentsdbhttp.maxInsertRequestSize value
+    	The maximum size of OpenTSDB HTTP put request
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 33554432)
+  -opentsdbhttpTrimTimestamp duration
+    	Trim timestamps for OpenTSDB HTTP data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -pprofAuthKey string
+    	Auth key for /debug/pprof. It overrides httpAuth settings
+  -promscrape.config string
+    	Optional path to Prometheus config file with 'scrape_configs' section containing targets to scrape. See https://victoriametrics.github.io/#how-to-scrape-prometheus-exporters-such-as-node-exporter for details
+  -promscrape.config.dryRun
+    	Checks -promscrape.config file for errors and unsupported fields and then exits. Returns non-zero exit code on parsing errors and emits these errors to stderr. See also -promscrape.config.strictParse command-line flag. Pass -loggerLevel=ERROR if you don't need to see info messages in the output.
+  -promscrape.config.strictParse
+    	Whether to allow only supported fields in -promscrape.config . By default unsupported fields are silently skipped
+  -promscrape.configCheckInterval duration
+    	Interval for checking for changes in '-promscrape.config' file. By default the checking is disabled. Send SIGHUP signal in order to force config check for changes
+  -promscrape.consulSDCheckInterval consul_sd_configs
+    	Interval for checking for changes in Consul. This works only if consul_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#consul_sd_config for details (default 30s)
+  -promscrape.disableCompression
+    	Whether to disable sending 'Accept-Encoding: gzip' request headers to all the scrape targets. This may reduce CPU usage on scrape targets at the cost of higher network bandwidth utilization. It is possible to set 'disable_compression: true' individually per each 'scrape_config' section in '-promscrape.config' for fine grained control
+  -promscrape.disableKeepAlive disable_keepalive: true
+    	Whether to disable HTTP keep-alive connections when scraping all the targets. This may be useful when targets has no support for HTTP keep-alive connection. It is possible to set disable_keepalive: true individually per each 'scrape_config` section in '-promscrape.config' for fine grained control. Note that disabling HTTP keep-alive may increase load on both vmagent and scrape targets
+  -promscrape.discovery.concurrency int
+    	The maximum number of concurrent requests to Prometheus autodiscovery API (Consul, Kubernetes, etc.) (default 100)
+  -promscrape.discovery.concurrentWaitTime duration
+    	The maximum duration for waiting to perform API requests if more than -promscrape.discovery.concurrency requests are simultaneously performed (default 1m0s)
+  -promscrape.dnsSDCheckInterval dns_sd_configs
+    	Interval for checking for changes in dns. This works only if dns_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dns_sd_config for details (default 30s)
+  -promscrape.dockerswarmSDCheckInterval dockerswarm_sd_configs
+    	Interval for checking for changes in dockerswarm. This works only if dockerswarm_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dockerswarm_sd_config for details (default 30s)
+  -promscrape.dropOriginalLabels
+    	Whether to drop original labels for scrape targets at /targets and /api/v1/targets pages. This may be needed for reducing memory usage when original labels for big number of scrape targets occupy big amounts of memory. Note that this reduces debuggability for improper per-target relabeling configs
+  -promscrape.ec2SDCheckInterval ec2_sd_configs
+    	Interval for checking for changes in ec2. This works only if ec2_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#ec2_sd_config for details (default 1m0s)
+  -promscrape.eurekaSDCheckInterval eureka_sd_configs
+    	Interval for checking for changes in eureka. This works only if eureka_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#eureka_sd_config for details (default 30s)
+  -promscrape.fileSDCheckInterval duration
+    	Interval for checking for changes in 'file_sd_config'. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#file_sd_config for details (default 30s)
+  -promscrape.gceSDCheckInterval gce_sd_configs
+    	Interval for checking for changes in gce. This works only if gce_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#gce_sd_config for details (default 1m0s)
+  -promscrape.kubernetesSDCheckInterval kubernetes_sd_configs
+    	Interval for checking for changes in Kubernetes API server. This works only if kubernetes_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config for details (default 30s)
+  -promscrape.maxDroppedTargets droppedTargets
+    	The maximum number of droppedTargets shown at /api/v1/targets page. Increase this value if your setup drops more scrape targets during relabeling and you need investigating labels for all the dropped targets. Note that the increased number of tracked dropped targets may result in increased memory usage (default 1000)
+  -promscrape.maxScrapeSize value
+    	The maximum size of scrape response in bytes to process from Prometheus targets. Bigger responses are rejected
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 16777216)
+  -promscrape.openstackSDCheckInterval openstack_sd_configs
+    	Interval for checking for changes in openstack API server. This works only if openstack_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#openstack_sd_config for details (default 30s)
+  -promscrape.streamParse stream_parse: true
+    	Whether to enable stream parsing for metrics obtained from scrape targets. This may be useful for reducing memory usage when millions of metrics are exposed per each scrape target. It is posible to set stream_parse: true individually per each `scrape_config` section in `-promscrape.config` for fine grained control
+  -promscrape.suppressDuplicateScrapeTargetErrors duplicate scrape target
+    	Whether to suppress duplicate scrape target errors; see https://victoriametrics.github.io/vmagent.html#troubleshooting for details
+  -promscrape.suppressScrapeErrors
+    	Whether to suppress scrape errors logging. The last error for each target is always available at '/targets' page even if scrape errors logging is suppressed
+  -remoteWrite.basicAuth.password array
+    	Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.basicAuth.username array
+    	Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.bearerToken array
+    	Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.flushInterval duration
+    	Interval for flushing the data to remote storage. Higher value reduces network bandwidth usage at the cost of delayed push of scraped data to remote storage. Minimum supported interval is 1 second (default 1s)
+  -remoteWrite.label array
+    	Optional label in the form 'name=value' to add to all the metrics before sending them to -remoteWrite.url. Pass multiple -remoteWrite.label flags in order to add multiple flags to metrics before sending them to remote storage
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.maxBlockSize value
+    	The maximum size in bytes of unpacked request to send to remote storage. It shouldn't exceed -maxInsertRequestSize from VictoriaMetrics
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 8388608)
+  -remoteWrite.maxDiskUsagePerURL value
+    	The maximum file-based buffer size in bytes at -remoteWrite.tmpDataPath for each -remoteWrite.url. When buffer size reaches the configured maximum, then old data is dropped when adding new data to the buffer. Buffered data is stored in ~500MB chunks, so the minimum practical value for this flag is 500000000. Disk usage is unlimited if the value is set to 0
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 0)
+  -remoteWrite.proxyURL array
+    	Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.queues int
+    	The number of concurrent queues to each -remoteWrite.url. Set more queues if default number of queues isn't enough for sending high volume of collected data to remote storage (default 4)
+  -remoteWrite.rateLimit array
+    	Optional rate limit in bytes per second for data sent to -remoteWrite.url. By default the rate limit is disabled. It can be useful for limiting load on remote storage when big amounts of buffered data is sent after temporary unavailability of the remote storage
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.relabelConfig string
+    	Optional path to file with relabel_config entries. These entries are applied to all the metrics before sending them to -remoteWrite.url. See https://victoriametrics.github.io/vmagent.html#relabeling for details
+  -remoteWrite.roundDigits array
+    	Round metric values to this number of decimal digits after the point before writing them to remote storage. Examples: -remoteWrite.roundDigits=2 would round 1.236 to 1.24, while -remoteWrite.roundDigits=-1 would round 126.78 to 130. By default digits rounding is disabled. Set it to 100 for disabling it for a particular remote storage. This option may be used for improving data compression for the stored metrics
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.sendTimeout array
+    	Timeout for sending a single block of data to -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.showURL
+    	Whether to show -remoteWrite.url in the exported metrics. It is hidden by default, since it can contain sensitive info such as auth key
+  -remoteWrite.significantFigures array
+    	The number of significant figures to leave in metric values before writing them to remote storage. See https://en.wikipedia.org/wiki/Significant_figures . Zero value saves all the significant figures. This option may be used for improving data compression for the stored metrics. See also -remoteWrite.roundDigits
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsCAFile array
+    	Optional path to TLS CA file to use for verifying connections to -remoteWrite.url. By default system CA is used. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsCertFile array
+    	Optional path to client-side TLS certificate file to use when connecting to -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsInsecureSkipVerify array
+    	Whether to skip tls verification when connecting to -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsKeyFile array
+    	Optional path to client-side TLS certificate key to use when connecting to -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsServerName array
+    	Optional TLS server name to use for connections to -remoteWrite.url. By default the server name from -remoteWrite.url is used. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tmpDataPath string
+    	Path to directory where temporary data for remote write component is stored (default "vmagent-remotewrite-data")
+  -remoteWrite.url array
+    	Remote storage URL to write data to. It must support Prometheus remote_write API. It is recommended using VictoriaMetrics as remote storage. Example url: http://<victoriametrics-host>:8428/api/v1/write . Pass multiple -remoteWrite.url flags in order to write data concurrently to multiple remote storage systems
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.urlRelabelConfig array
+    	Optional path to relabel config for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -tls
+    	Whether to enable TLS (aka HTTPS) for incoming requests. -tlsCertFile and -tlsKeyFile must be set if -tls is set
+  -tlsCertFile string
+    	Path to file with TLS certificate. Used only if -tls is set. Prefer ECDSA certs instead of RSA certs, since RSA certs are slow
+  -tlsKeyFile string
+    	Path to file with TLS key. Used only if -tls is set
+  -version
+    	Show VictoriaMetrics version
+```
diff --git a/app/vmalert/README.md b/app/vmalert/README.md
index 1d0e785e0..1a98ed83f 100644
--- a/app/vmalert/README.md
+++ b/app/vmalert/README.md
@@ -82,6 +82,10 @@ name: <string>
 # up round execution speed. 
 [ concurrency: <integer> | default = 1 ]
 
+# Optional type for expressions inside the rules. Supported values: "graphite" and "prometheus".
+# By default "prometheus" rule type is used.
+[ type: <string> ]
+
 rules:
   [ - <rule> ... ]
 ```
@@ -182,7 +186,7 @@ Used as alert source in AlertManager.
 ### Graphite
 
 vmalert sends requests to `<-datasource.url>/render?format=json` during evaluation of alerting and recording rules
-if the corresponding rule contains `type: "graphite"` config option. It is expected that the `<-datasource.url>/render`
+if the corresponding group or rule contains `type: "graphite"` config option. It is expected that the `<-datasource.url>/render`
 implements [Graphite Render API](https://graphite.readthedocs.io/en/stable/render_api.html) for `format=json`.
 
 
diff --git a/app/vmalert/datasource/init.go b/app/vmalert/datasource/init.go
index 9c9546dea..d1d3b5109 100644
--- a/app/vmalert/datasource/init.go
+++ b/app/vmalert/datasource/init.go
@@ -11,6 +11,7 @@ import (
 var (
 	addr = flag.String("datasource.url", "", "Victoria Metrics or VMSelect url. Required parameter."+
 		" E.g. http://127.0.0.1:8428")
+	appendTypePrefix  = flag.Bool("datasource.appendTypePrefix", false, "Whether to add type prefix to -datasource.url based on the query type. Set to true if sending different query types to VMSelect URL.")
 	basicAuthUsername = flag.String("datasource.basicAuth.username", "", "Optional basic auth username for -datasource.url")
 	basicAuthPassword = flag.String("datasource.basicAuth.password", "", "Optional basic auth password for -datasource.url")
 
@@ -41,5 +42,5 @@ func Init() (Querier, error) {
 	}
 	tr.MaxIdleConns = *maxIdleConnections
 	c := &http.Client{Transport: tr}
-	return NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, *lookBack, *queryStep, c), nil
+	return NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, *lookBack, *queryStep, *appendTypePrefix, c), nil
 }
diff --git a/app/vmalert/datasource/vm.go b/app/vmalert/datasource/vm.go
index 0c0626fbc..e06b3af3d 100644
--- a/app/vmalert/datasource/vm.go
+++ b/app/vmalert/datasource/vm.go
@@ -74,26 +74,31 @@ func (r graphiteResponse) metrics() []Metric {
 
 // VMStorage represents vmstorage entity with ability to read and write metrics
 type VMStorage struct {
-	c             *http.Client
-	datasourceURL string
-	basicAuthUser string
-	basicAuthPass string
-	lookBack      time.Duration
-	queryStep     time.Duration
+	c                *http.Client
+	datasourceURL    string
+	basicAuthUser    string
+	basicAuthPass    string
+	appendTypePrefix bool
+	lookBack         time.Duration
+	queryStep        time.Duration
 }
 
 const queryPath = "/api/v1/query"
 const graphitePath = "/render"
 
+const prometheusPrefix = "/prometheus"
+const graphitePrefix = "/graphite"
+
 // NewVMStorage is a constructor for VMStorage
-func NewVMStorage(baseURL, basicAuthUser, basicAuthPass string, lookBack time.Duration, queryStep time.Duration, c *http.Client) *VMStorage {
+func NewVMStorage(baseURL, basicAuthUser, basicAuthPass string, lookBack time.Duration, queryStep time.Duration, appendTypePrefix bool, c *http.Client) *VMStorage {
 	return &VMStorage{
-		c:             c,
-		basicAuthUser: basicAuthUser,
-		basicAuthPass: basicAuthPass,
-		datasourceURL: strings.TrimSuffix(baseURL, "/"),
-		lookBack:      lookBack,
-		queryStep:     queryStep,
+		c:                c,
+		basicAuthUser:    basicAuthUser,
+		basicAuthPass:    basicAuthPass,
+		datasourceURL:    strings.TrimSuffix(baseURL, "/"),
+		appendTypePrefix: appendTypePrefix,
+		lookBack:         lookBack,
+		queryStep:        queryStep,
 	}
 }
 
@@ -137,6 +142,9 @@ func (s *VMStorage) queryDataSource(
 }
 
 func (s *VMStorage) setPrometheusReqParams(r *http.Request, query string) {
+	if s.appendTypePrefix {
+		r.URL.Path += prometheusPrefix
+	}
 	r.URL.Path += queryPath
 	q := r.URL.Query()
 	q.Set("query", query)
@@ -151,6 +159,9 @@ func (s *VMStorage) setPrometheusReqParams(r *http.Request, query string) {
 }
 
 func (s *VMStorage) setGraphiteReqParams(r *http.Request, query string) {
+	if s.appendTypePrefix {
+		r.URL.Path += graphitePrefix
+	}
 	r.URL.Path += graphitePath
 	q := r.URL.Query()
 	q.Set("format", "json")
diff --git a/app/vmalert/remoteread/init.go b/app/vmalert/remoteread/init.go
index 7a0aa32ac..165db403d 100644
--- a/app/vmalert/remoteread/init.go
+++ b/app/vmalert/remoteread/init.go
@@ -35,5 +35,5 @@ func Init() (datasource.Querier, error) {
 		return nil, fmt.Errorf("failed to create transport: %w", err)
 	}
 	c := &http.Client{Transport: tr}
-	return datasource.NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, 0, 0, c), nil
+	return datasource.NewVMStorage(*addr, *basicAuthUsername, *basicAuthPassword, 0, 0, false, c), nil
 }
diff --git a/app/vmselect/graphite/metrics_api.go b/app/vmselect/graphite/metrics_api.go
index 1fe1b6537..7e169371a 100644
--- a/app/vmselect/graphite/metrics_api.go
+++ b/app/vmselect/graphite/metrics_api.go
@@ -12,6 +12,7 @@ import (
 	"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/bufferedwriter"
 	"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
 	"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/searchutils"
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
 	"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
 	"github.com/VictoriaMetrics/metrics"
 )
@@ -338,7 +339,10 @@ func getRegexpForQuery(query string, delimiter byte) (*regexp.Regexp, error) {
 	if re := regexpCache[k]; re != nil {
 		return re.re, re.err
 	}
-	rs := getRegexpStringForQuery(query, delimiter, false)
+	rs, tail := getRegexpStringForQuery(query, delimiter, false)
+	if len(tail) > 0 {
+		return nil, fmt.Errorf("unexpected tail left after parsing query %q; tail: %q", query, tail)
+	}
 	re, err := regexp.Compile(rs)
 	regexpCache[k] = &regexpCacheEntry{
 		re:  re,
@@ -355,61 +359,73 @@ func getRegexpForQuery(query string, delimiter byte) (*regexp.Regexp, error) {
 	return re, err
 }
 
-func getRegexpStringForQuery(query string, delimiter byte, isSubquery bool) string {
+func getRegexpStringForQuery(query string, delimiter byte, isSubquery bool) (string, string) {
 	var a []string
+	var tail string
 	quotedDelimiter := regexp.QuoteMeta(string([]byte{delimiter}))
-	tillNextDelimiter := "[^" + quotedDelimiter + "]*"
-	j := 0
-	for i := 0; i < len(query); i++ {
-		switch query[i] {
-		case '*':
-			a = append(a, regexp.QuoteMeta(query[j:i]))
-			a = append(a, tillNextDelimiter)
-			j = i + 1
-		case '{':
+	for {
+		n := strings.IndexAny(query, "*{[,}")
+		if n < 0 {
+			a = append(a, regexp.QuoteMeta(query))
+			tail = ""
+			goto end
+		}
+		a = append(a, regexp.QuoteMeta(query[:n]))
+		query = query[n:]
+		switch query[0] {
+		case ',', '}':
 			if isSubquery {
-				break
+				tail = query
+				goto end
 			}
-			a = append(a, regexp.QuoteMeta(query[j:i]))
-			tmp := query[i+1:]
-			if n := strings.IndexByte(tmp, '}'); n < 0 {
-				rs := getRegexpStringForQuery(query[i:], delimiter, true)
-				a = append(a, rs)
-				i = len(query)
-			} else {
-				a = append(a, "(?:")
-				opts := strings.Split(tmp[:n], ",")
-				for j, opt := range opts {
-					opts[j] = getRegexpStringForQuery(opt, delimiter, true)
+			a = append(a, regexp.QuoteMeta(query[:1]))
+			query = query[1:]
+		case '*':
+			a = append(a, "[^"+quotedDelimiter+"]*")
+			query = query[1:]
+		case '{':
+			var opts []string
+			for {
+				var x string
+				x, tail = getRegexpStringForQuery(query[1:], delimiter, true)
+				opts = append(opts, x)
+				if len(tail) == 0 {
+					a = append(a, regexp.QuoteMeta("{"))
+					a = append(a, strings.Join(opts, ","))
+					goto end
+				}
+				if tail[0] == ',' {
+					query = tail
+					continue
+				}
+				if tail[0] == '}' {
+					a = append(a, "(?:"+strings.Join(opts, "|")+")")
+					query = tail[1:]
+					break
 				}
-				a = append(a, strings.Join(opts, "|"))
-				a = append(a, ")")
-				i += n + 1
+				logger.Panicf("BUG: unexpected first char at tail %q; want `.` or `}`", tail)
 			}
-			j = i + 1
 		case '[':
-			a = append(a, regexp.QuoteMeta(query[j:i]))
-			tmp := query[i:]
-			if n := strings.IndexByte(tmp, ']'); n < 0 {
-				a = append(a, regexp.QuoteMeta(query[i:]))
-				i = len(query)
-			} else {
-				a = append(a, tmp[:n+1])
-				i += n
+			n := strings.IndexByte(query, ']')
+			if n < 0 {
+				a = append(a, regexp.QuoteMeta(query))
+				tail = ""
+				goto end
 			}
-			j = i + 1
+			a = append(a, query[:n+1])
+			query = query[n+1:]
 		}
 	}
-	a = append(a, regexp.QuoteMeta(query[j:]))
+end:
 	s := strings.Join(a, "")
 	if isSubquery {
-		return s
+		return s, tail
 	}
 	if !strings.HasSuffix(s, quotedDelimiter) {
 		s += quotedDelimiter + "?"
 	}
-	s = "^(?:" + s + ")$"
-	return s
+	s = "^" + s + "$"
+	return s, tail
 }
 
 type regexpCacheEntry struct {
diff --git a/app/vmselect/promql/exec.go b/app/vmselect/promql/exec.go
index 086cde594..151f2dbde 100644
--- a/app/vmselect/promql/exec.go
+++ b/app/vmselect/promql/exec.go
@@ -22,7 +22,7 @@ var (
 	treatDotsAsIsInRegexps = flag.Bool("search.treatDotsAsIsInRegexps", false, "Whether to treat dots as is in regexp label filters used in queries. "+
 		`For example, foo{bar=~"a.b.c"} will be automatically converted to foo{bar=~"a\\.b\\.c"}, i.e. all the dots in regexp filters will be automatically escaped `+
 		`in order to match only dot char instead of matching any char. Dots in ".+", ".*" and ".{n}" regexps aren't escaped. `+
-		`Such escaping can be useful when querying Graphite data`)
+		`This option is DEPRECATED in favor of {__graphite__="a.*.c"} syntax for selecting metrics matching the given Graphite metrics filter`)
 )
 
 var slowQueries = metrics.NewCounter(`vm_slow_queries_total`)
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index d1aa1345d..a63c5271d 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -2,13 +2,15 @@
 
 # tip
 
+* BUGFIX: vmselect: fix the bug peventing from proper searching by Graphite filter with wildcards such as `{__graphite__="foo.*.bar"}`.
+
 
 # [v1.53.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.53.0)
 
 * FEATURE: added [vmctl tool](https://victoriametrics.github.io/vmctl.html) to VictoriaMetrics release process. Now it is packaged in `vmutils-*.tar.gz` archive on [the releases page](https://github.com/VictoriaMetrics/VictoriaMetrics/releases). Source code for `vmctl` tool has been moved from [github.com/VictoriaMetrics/vmctl](https://github.com/VictoriaMetrics/vmctl) to [github.com/VictoriaMetrics/VictoriaMetrics/app/vmctl](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmctl).
 * FEATURE: added `-loggerTimezone` command-line flag for adjusting time zone for timestamps in log messages. By default UTC is used.
 * FEATURE: added `-search.maxStepForPointsAdjustment` command-line flag, which can be used for disabling adjustment for points returned by `/api/v1/query_range` handler if such points have timestamps closer than `-search.latencyOffset` to the current time. Such points may contain incomplete data, so they are substituted by the previous values for `step` query args smaller than one minute by default.
-* FEATURE: vmselect: added ability to use Graphite-compatible filters in MetricsQL via `{__graphite__="foo.*.bar"}` syntax. This expression is equivalent to `{__name__=~"foo[.][^.]*[.]bar"}`, but it works faster and it is easier to use when migrating from Graphite to VictoriaMetrics.
+* FEATURE: vmselect: added ability to use Graphite-compatible filters in MetricsQL via `{__graphite__="foo.*.bar"}` syntax. This expression is equivalent to `{__name__=~"foo[.][^.]*[.]bar"}`, but it works faster and it is easier to use when migrating from Graphite to VictoriaMetrics. This feature deprecates the usage of `-search.treatDotsAsIsInRegexps` command-line flag.
 * FEATURE: vmselect: added ability to set additional label filters, which must be applied during queries. Such label filters can be set via optional `extra_label` query arg, which is accepted by [querying API](https://victoriametrics.github.io/#prometheus-querying-api-usage) handlers. For example, the request to `/api/v1/query_range?extra_label=tenant_id=123&query=<query>` adds `{tenant_id="123"}` label filter to the given `<query>`. It is expected that the `extra_label` query arg is automatically set by auth proxy sitting
 in front of VictoriaMetrics. [Contact us](mailto:sales@victoriametrics.com) if you need assistance with such a proxy. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1021 .
 * FEATURE: vmalert: added `-datasource.queryStep` command-line flag for passing optional `step` query arg to `/api/v1/query` endpoint. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1025
diff --git a/docs/Single-server-VictoriaMetrics.md b/docs/Single-server-VictoriaMetrics.md
index 02c4afa76..2107f4cb3 100644
--- a/docs/Single-server-VictoriaMetrics.md
+++ b/docs/Single-server-VictoriaMetrics.md
@@ -812,10 +812,7 @@ The exported data can be imported to VictoriaMetrics via [/api/v1/import/native]
 
 ### How to export data in JSON line format
 
-Consider [exporting data in native format](#how-to-export-data-in-native-format) if big amounts of data must be migrated between VictoriaMetrics instances,
-since exporting in native format usually consumes lower amounts of CPU and memory resources, while the resulting exported data occupies lower amounts of disk space.
-
-In order to export data in JSON line format, send a request to `http://<victoriametrics-addr>:8428/api/v1/export?match[]=<timeseries_selector_for_export>`,
+Send a request to `http://<victoriametrics-addr>:8428/api/v1/export?match[]=<timeseries_selector_for_export>`,
 where `<timeseries_selector_for_export>` may contain any [time series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors)
 for metrics to export. Use `{__name__!=""}` selector for fetching all the time series.
 The response would contain all the data for the selected time series in [JSON streaming format](https://en.wikipedia.org/wiki/JSON_streaming#Line-delimited_JSON).
@@ -1365,10 +1362,8 @@ See the example of alerting rules for VM components [here](https://github.com/Vi
   This prevents from ingesting metrics with too many labels. It is recommended [monitoring](#monitoring) `vm_metrics_with_dropped_labels_total`
   metric in order to determine whether `-maxLabelsPerTimeseries` must be adjusted for your workload.
 
-* If you store Graphite metrics like `foo.bar.baz` in VictoriaMetrics, then `-search.treatDotsAsIsInRegexps` command-line flag could be useful.
-  By default `.` chars in regexps match any char. If you need matching only dots, then the `\\.` must be used in regexp filters.
-  When `-search.treatDotsAsIsInRegexps` option is enabled, then dots in regexps are automatically escaped in order to match only dots instead of arbitrary chars.
-  This may significantly increase performance when locating time series for the given label filters.
+* If you store Graphite metrics like `foo.bar.baz` in VictoriaMetrics, then use `{__graphite__="foo.*.baz"}` syntax for selecting such metrics.
+  This expression is equivalent to `{__name__=~"foo[.][^.]*[.]baz"}`, but it works faster and it is easier to use when migrating from Graphite.
 
 * VictoriaMetrics ignores `NaN` values during data ingestion.
 
diff --git a/docs/vmagent.md b/docs/vmagent.md
index d2d48e5ec..bf40e17ba 100644
--- a/docs/vmagent.md
+++ b/docs/vmagent.md
@@ -388,3 +388,227 @@ curl -s http://<vmagent-host>:8429/debug/pprof/profile > cpu.pprof
 The command for collecting CPU profile waits for 30 seconds before returning.
 
 The collected profiles may be analyzed with [go tool pprof](https://github.com/google/pprof).
+
+
+## Advanced usage
+
+`vmagent` can be fine-tuned with various command-line flags. Run `./vmagent -help` in order to see the full list of these flags with their desciptions and default values:
+
+```
+./vmagent -help
+
+vmagent collects metrics data via popular data ingestion protocols and routes it to VictoriaMetrics.
+
+See the docs at https://victoriametrics.github.io/vmagent.html .
+
+  -csvTrimTimestamp duration
+    	Trim timestamps when importing csv data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -dryRun
+    	Whether to check only config files without running vmagent. The following files are checked: -promscrape.config, -remoteWrite.relabelConfig, -remoteWrite.urlRelabelConfig . Unknown config entries are allowed in -promscrape.config by default. This can be changed with -promscrape.config.strictParse
+  -enableTCP6
+    	Whether to enable IPv6 for listening and dialing. By default only IPv4 TCP is used
+  -envflag.enable
+    	Whether to enable reading flags from environment variables additionally to command line. Command line flag values have priority over values from environment vars. Flags are read only from command line if this flag isn't set
+  -envflag.prefix string
+    	Prefix for environment variables if -envflag.enable is set
+  -fs.disableMmap
+    	Whether to use pread() instead of mmap() for reading data files. By default mmap() is used for 64-bit arches and pread() is used for 32-bit arches, since they cannot read data files bigger than 2^32 bytes in memory. mmap() is usually faster for reading small data chunks than pread()
+  -graphiteListenAddr string
+    	TCP and UDP address to listen for Graphite plaintext data. Usually :2003 must be set. Doesn't work if empty
+  -graphiteTrimTimestamp duration
+    	Trim timestamps for Graphite data to this duration. Minimum practical duration is 1s. Higher duration (i.e. 1m) may be used for reducing disk space usage for timestamp data (default 1s)
+  -http.connTimeout duration
+    	Incoming http connections are closed after the configured timeout. This may help spreading incoming load among a cluster of services behind load balancer. Note that the real timeout may be bigger by up to 10% as a protection from Thundering herd problem (default 2m0s)
+  -http.disableResponseCompression
+    	Disable compression of HTTP responses for saving CPU resources. By default compression is enabled to save network bandwidth
+  -http.idleConnTimeout duration
+    	Timeout for incoming idle http connections (default 1m0s)
+  -http.maxGracefulShutdownDuration duration
+    	The maximum duration for graceful shutdown of HTTP server. Highly loaded server may require increased value for graceful shutdown (default 7s)
+  -http.pathPrefix string
+    	An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus
+  -http.shutdownDelay duration
+    	Optional delay before http server shutdown. During this dealy the servier returns non-OK responses from /health page, so load balancers can route new requests to other servers
+  -httpAuth.password string
+    	Password for HTTP Basic Auth. The authentication is disabled if -httpAuth.username is empty
+  -httpAuth.username string
+    	Username for HTTP Basic Auth. The authentication is disabled if empty. See also -httpAuth.password
+  -httpListenAddr string
+    	TCP address to listen for http connections. Set this flag to empty value in order to disable listening on any port. This mode may be useful for running multiple vmagent instances on the same server. Note that /targets and /metrics pages aren't available if -httpListenAddr='' (default ":8429")
+  -import.maxLineLen max_rows_per_line
+    	The maximum length in bytes of a single line accepted by /api/v1/import; the line length can be limited with max_rows_per_line query arg passed to /api/v1/export
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 104857600)
+  -influx.maxLineSize value
+    	The maximum size in bytes for a single Influx line during parsing
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 262144)
+  -influxListenAddr http://<vmagent>:8429/write
+    	TCP and UDP address to listen for Influx line protocol data. Usually :8189 must be set. Doesn't work if empty. This flag isn't needed when ingesting data over HTTP - just send it to http://<vmagent>:8429/write
+  -influxMeasurementFieldSeparator string
+    	Separator for '{measurement}{separator}{field_name}' metric name when inserted via Influx line protocol (default "_")
+  -influxSkipMeasurement
+    	Uses '{field_name}' as a metric name while ignoring '{measurement}' and '-influxMeasurementFieldSeparator'
+  -influxSkipSingleField
+    	Uses '{measurement}' instead of '{measurement}{separator}{field_name}' for metic name if Influx line contains only a single field
+  -influxTrimTimestamp duration
+    	Trim timestamps for Influx line protocol data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -insert.maxQueueDuration duration
+    	The maximum duration for waiting in the queue for insert requests due to -maxConcurrentInserts (default 1m0s)
+  -loggerDisableTimestamps
+    	Whether to disable writing timestamps in logs
+  -loggerErrorsPerSecondLimit int
+    	Per-second limit on the number of ERROR messages. If more than the given number of errors are emitted per second, then the remaining errors are suppressed. Zero value disables the rate limit
+  -loggerFormat string
+    	Format for logs. Possible values: default, json (default "default")
+  -loggerLevel string
+    	Minimum level of errors to log. Possible values: INFO, WARN, ERROR, FATAL, PANIC (default "INFO")
+  -loggerOutput string
+    	Output for the logs. Supported values: stderr, stdout (default "stderr")
+  -loggerTimezone string
+    	Timezone to use for timestamps in logs. Local timezone can be used (default "UTC")
+  -loggerWarnsPerSecondLimit int
+    	Per-second limit on the number of WARN messages. If more than the given number of warns are emitted per second, then the remaining warns are suppressed. Zero value disables the rate limit
+  -maxConcurrentInserts int
+    	The maximum number of concurrent inserts. Default value should work for most cases, since it minimizes the overhead for concurrent inserts. This option is tigthly coupled with -insert.maxQueueDuration (default 16)
+  -maxInsertRequestSize value
+    	The maximum size in bytes of a single Prometheus remote_write API request
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 33554432)
+  -memory.allowedBytes value
+    	Allowed size of system memory VictoriaMetrics caches may occupy. This option overrides -memory.allowedPercent if set to non-zero value. Too low value may increase cache miss rate, which usually results in higher CPU and disk IO usage. Too high value may evict too much data from OS page cache, which will result in higher disk IO usage
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 0)
+  -memory.allowedPercent float
+    	Allowed percent of system memory VictoriaMetrics caches may occupy. See also -memory.allowedBytes. Too low value may increase cache miss rate, which usually results in higher CPU and disk IO usage. Too high value may evict too much data from OS page cache, which will result in higher disk IO usage (default 60)
+  -metricsAuthKey string
+    	Auth key for /metrics. It overrides httpAuth settings
+  -opentsdbHTTPListenAddr string
+    	TCP address to listen for OpentTSDB HTTP put requests. Usually :4242 must be set. Doesn't work if empty
+  -opentsdbListenAddr string
+    	TCP and UDP address to listen for OpentTSDB metrics. Telnet put messages and HTTP /api/put messages are simultaneously served on TCP port. Usually :4242 must be set. Doesn't work if empty
+  -opentsdbTrimTimestamp duration
+    	Trim timestamps for OpenTSDB 'telnet put' data to this duration. Minimum practical duration is 1s. Higher duration (i.e. 1m) may be used for reducing disk space usage for timestamp data (default 1s)
+  -opentsdbhttp.maxInsertRequestSize value
+    	The maximum size of OpenTSDB HTTP put request
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 33554432)
+  -opentsdbhttpTrimTimestamp duration
+    	Trim timestamps for OpenTSDB HTTP data to this duration. Minimum practical duration is 1ms. Higher duration (i.e. 1s) may be used for reducing disk space usage for timestamp data (default 1ms)
+  -pprofAuthKey string
+    	Auth key for /debug/pprof. It overrides httpAuth settings
+  -promscrape.config string
+    	Optional path to Prometheus config file with 'scrape_configs' section containing targets to scrape. See https://victoriametrics.github.io/#how-to-scrape-prometheus-exporters-such-as-node-exporter for details
+  -promscrape.config.dryRun
+    	Checks -promscrape.config file for errors and unsupported fields and then exits. Returns non-zero exit code on parsing errors and emits these errors to stderr. See also -promscrape.config.strictParse command-line flag. Pass -loggerLevel=ERROR if you don't need to see info messages in the output.
+  -promscrape.config.strictParse
+    	Whether to allow only supported fields in -promscrape.config . By default unsupported fields are silently skipped
+  -promscrape.configCheckInterval duration
+    	Interval for checking for changes in '-promscrape.config' file. By default the checking is disabled. Send SIGHUP signal in order to force config check for changes
+  -promscrape.consulSDCheckInterval consul_sd_configs
+    	Interval for checking for changes in Consul. This works only if consul_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#consul_sd_config for details (default 30s)
+  -promscrape.disableCompression
+    	Whether to disable sending 'Accept-Encoding: gzip' request headers to all the scrape targets. This may reduce CPU usage on scrape targets at the cost of higher network bandwidth utilization. It is possible to set 'disable_compression: true' individually per each 'scrape_config' section in '-promscrape.config' for fine grained control
+  -promscrape.disableKeepAlive disable_keepalive: true
+    	Whether to disable HTTP keep-alive connections when scraping all the targets. This may be useful when targets has no support for HTTP keep-alive connection. It is possible to set disable_keepalive: true individually per each 'scrape_config` section in '-promscrape.config' for fine grained control. Note that disabling HTTP keep-alive may increase load on both vmagent and scrape targets
+  -promscrape.discovery.concurrency int
+    	The maximum number of concurrent requests to Prometheus autodiscovery API (Consul, Kubernetes, etc.) (default 100)
+  -promscrape.discovery.concurrentWaitTime duration
+    	The maximum duration for waiting to perform API requests if more than -promscrape.discovery.concurrency requests are simultaneously performed (default 1m0s)
+  -promscrape.dnsSDCheckInterval dns_sd_configs
+    	Interval for checking for changes in dns. This works only if dns_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dns_sd_config for details (default 30s)
+  -promscrape.dockerswarmSDCheckInterval dockerswarm_sd_configs
+    	Interval for checking for changes in dockerswarm. This works only if dockerswarm_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#dockerswarm_sd_config for details (default 30s)
+  -promscrape.dropOriginalLabels
+    	Whether to drop original labels for scrape targets at /targets and /api/v1/targets pages. This may be needed for reducing memory usage when original labels for big number of scrape targets occupy big amounts of memory. Note that this reduces debuggability for improper per-target relabeling configs
+  -promscrape.ec2SDCheckInterval ec2_sd_configs
+    	Interval for checking for changes in ec2. This works only if ec2_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#ec2_sd_config for details (default 1m0s)
+  -promscrape.eurekaSDCheckInterval eureka_sd_configs
+    	Interval for checking for changes in eureka. This works only if eureka_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#eureka_sd_config for details (default 30s)
+  -promscrape.fileSDCheckInterval duration
+    	Interval for checking for changes in 'file_sd_config'. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#file_sd_config for details (default 30s)
+  -promscrape.gceSDCheckInterval gce_sd_configs
+    	Interval for checking for changes in gce. This works only if gce_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#gce_sd_config for details (default 1m0s)
+  -promscrape.kubernetesSDCheckInterval kubernetes_sd_configs
+    	Interval for checking for changes in Kubernetes API server. This works only if kubernetes_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config for details (default 30s)
+  -promscrape.maxDroppedTargets droppedTargets
+    	The maximum number of droppedTargets shown at /api/v1/targets page. Increase this value if your setup drops more scrape targets during relabeling and you need investigating labels for all the dropped targets. Note that the increased number of tracked dropped targets may result in increased memory usage (default 1000)
+  -promscrape.maxScrapeSize value
+    	The maximum size of scrape response in bytes to process from Prometheus targets. Bigger responses are rejected
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 16777216)
+  -promscrape.openstackSDCheckInterval openstack_sd_configs
+    	Interval for checking for changes in openstack API server. This works only if openstack_sd_configs is configured in '-promscrape.config' file. See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#openstack_sd_config for details (default 30s)
+  -promscrape.streamParse stream_parse: true
+    	Whether to enable stream parsing for metrics obtained from scrape targets. This may be useful for reducing memory usage when millions of metrics are exposed per each scrape target. It is posible to set stream_parse: true individually per each `scrape_config` section in `-promscrape.config` for fine grained control
+  -promscrape.suppressDuplicateScrapeTargetErrors duplicate scrape target
+    	Whether to suppress duplicate scrape target errors; see https://victoriametrics.github.io/vmagent.html#troubleshooting for details
+  -promscrape.suppressScrapeErrors
+    	Whether to suppress scrape errors logging. The last error for each target is always available at '/targets' page even if scrape errors logging is suppressed
+  -remoteWrite.basicAuth.password array
+    	Optional basic auth password to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.basicAuth.username array
+    	Optional basic auth username to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.bearerToken array
+    	Optional bearer auth token to use for -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.flushInterval duration
+    	Interval for flushing the data to remote storage. Higher value reduces network bandwidth usage at the cost of delayed push of scraped data to remote storage. Minimum supported interval is 1 second (default 1s)
+  -remoteWrite.label array
+    	Optional label in the form 'name=value' to add to all the metrics before sending them to -remoteWrite.url. Pass multiple -remoteWrite.label flags in order to add multiple flags to metrics before sending them to remote storage
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.maxBlockSize value
+    	The maximum size in bytes of unpacked request to send to remote storage. It shouldn't exceed -maxInsertRequestSize from VictoriaMetrics
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 8388608)
+  -remoteWrite.maxDiskUsagePerURL value
+    	The maximum file-based buffer size in bytes at -remoteWrite.tmpDataPath for each -remoteWrite.url. When buffer size reaches the configured maximum, then old data is dropped when adding new data to the buffer. Buffered data is stored in ~500MB chunks, so the minimum practical value for this flag is 500000000. Disk usage is unlimited if the value is set to 0
+    	Supports the following optional suffixes for values: KB, MB, GB, KiB, MiB, GiB (default 0)
+  -remoteWrite.proxyURL array
+    	Optional proxy URL for writing data to -remoteWrite.url. Supported proxies: http, https, socks5. Example: -remoteWrite.proxyURL=socks5://proxy:1234
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.queues int
+    	The number of concurrent queues to each -remoteWrite.url. Set more queues if default number of queues isn't enough for sending high volume of collected data to remote storage (default 4)
+  -remoteWrite.rateLimit array
+    	Optional rate limit in bytes per second for data sent to -remoteWrite.url. By default the rate limit is disabled. It can be useful for limiting load on remote storage when big amounts of buffered data is sent after temporary unavailability of the remote storage
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.relabelConfig string
+    	Optional path to file with relabel_config entries. These entries are applied to all the metrics before sending them to -remoteWrite.url. See https://victoriametrics.github.io/vmagent.html#relabeling for details
+  -remoteWrite.roundDigits array
+    	Round metric values to this number of decimal digits after the point before writing them to remote storage. Examples: -remoteWrite.roundDigits=2 would round 1.236 to 1.24, while -remoteWrite.roundDigits=-1 would round 126.78 to 130. By default digits rounding is disabled. Set it to 100 for disabling it for a particular remote storage. This option may be used for improving data compression for the stored metrics
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.sendTimeout array
+    	Timeout for sending a single block of data to -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.showURL
+    	Whether to show -remoteWrite.url in the exported metrics. It is hidden by default, since it can contain sensitive info such as auth key
+  -remoteWrite.significantFigures array
+    	The number of significant figures to leave in metric values before writing them to remote storage. See https://en.wikipedia.org/wiki/Significant_figures . Zero value saves all the significant figures. This option may be used for improving data compression for the stored metrics. See also -remoteWrite.roundDigits
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsCAFile array
+    	Optional path to TLS CA file to use for verifying connections to -remoteWrite.url. By default system CA is used. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsCertFile array
+    	Optional path to client-side TLS certificate file to use when connecting to -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsInsecureSkipVerify array
+    	Whether to skip tls verification when connecting to -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsKeyFile array
+    	Optional path to client-side TLS certificate key to use when connecting to -remoteWrite.url. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tlsServerName array
+    	Optional TLS server name to use for connections to -remoteWrite.url. By default the server name from -remoteWrite.url is used. If multiple args are set, then they are applied independently for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.tmpDataPath string
+    	Path to directory where temporary data for remote write component is stored (default "vmagent-remotewrite-data")
+  -remoteWrite.url array
+    	Remote storage URL to write data to. It must support Prometheus remote_write API. It is recommended using VictoriaMetrics as remote storage. Example url: http://<victoriametrics-host>:8428/api/v1/write . Pass multiple -remoteWrite.url flags in order to write data concurrently to multiple remote storage systems
+    	Supports array of values separated by comma or specified via multiple flags.
+  -remoteWrite.urlRelabelConfig array
+    	Optional path to relabel config for the corresponding -remoteWrite.url
+    	Supports array of values separated by comma or specified via multiple flags.
+  -tls
+    	Whether to enable TLS (aka HTTPS) for incoming requests. -tlsCertFile and -tlsKeyFile must be set if -tls is set
+  -tlsCertFile string
+    	Path to file with TLS certificate. Used only if -tls is set. Prefer ECDSA certs instead of RSA certs, since RSA certs are slow
+  -tlsKeyFile string
+    	Path to file with TLS key. Used only if -tls is set
+  -version
+    	Show VictoriaMetrics version
+```
diff --git a/docs/vmalert.md b/docs/vmalert.md
index 1d0e785e0..1a98ed83f 100644
--- a/docs/vmalert.md
+++ b/docs/vmalert.md
@@ -82,6 +82,10 @@ name: <string>
 # up round execution speed. 
 [ concurrency: <integer> | default = 1 ]
 
+# Optional type for expressions inside the rules. Supported values: "graphite" and "prometheus".
+# By default "prometheus" rule type is used.
+[ type: <string> ]
+
 rules:
   [ - <rule> ... ]
 ```
@@ -182,7 +186,7 @@ Used as alert source in AlertManager.
 ### Graphite
 
 vmalert sends requests to `<-datasource.url>/render?format=json` during evaluation of alerting and recording rules
-if the corresponding rule contains `type: "graphite"` config option. It is expected that the `<-datasource.url>/render`
+if the corresponding group or rule contains `type: "graphite"` config option. It is expected that the `<-datasource.url>/render`
 implements [Graphite Render API](https://graphite.readthedocs.io/en/stable/render_api.html) for `format=json`.
 
 
diff --git a/lib/storage/storage.go b/lib/storage/storage.go
index fea119bfd..e8c808293 100644
--- a/lib/storage/storage.go
+++ b/lib/storage/storage.go
@@ -1019,15 +1019,15 @@ func (s *Storage) SearchGraphitePaths(tr TimeRange, query []byte, maxPaths int,
 		return nil, fmt.Errorf("more than maxPaths=%d suffixes found", maxPaths)
 	}
 	qPrefixStr := queryStr[:n]
-	qNode := queryStr[n:]
 	qTail := ""
+	qNode := queryStr[n:]
 	mustMatchLeafs := true
 	if m := strings.IndexByte(qNode, '.'); m >= 0 {
-		qNode = qNode[:m+1]
 		qTail = qNode[m+1:]
+		qNode = qNode[:m+1]
 		mustMatchLeafs = false
 	}
-	re, err := getRegexpForGraphiteNodeQuery(qNode)
+	re, err := getRegexpForGraphiteQuery(qNode)
 	if err != nil {
 		return nil, err
 	}
@@ -1054,40 +1054,61 @@ func (s *Storage) SearchGraphitePaths(tr TimeRange, query []byte, maxPaths int,
 	return paths, nil
 }
 
-func getRegexpForGraphiteNodeQuery(q string) (*regexp.Regexp, error) {
-	parts := getRegexpPartsForGraphiteNodeQuery(q)
+func getRegexpForGraphiteQuery(q string) (*regexp.Regexp, error) {
+	parts, tail := getRegexpPartsForGraphiteQuery(q)
+	if len(tail) > 0 {
+		return nil, fmt.Errorf("unexpected tail left after parsing %q: %q", q, tail)
+	}
 	reStr := "^" + strings.Join(parts, "") + "$"
 	return regexp.Compile(reStr)
 }
 
-func getRegexpPartsForGraphiteNodeQuery(q string) []string {
+func getRegexpPartsForGraphiteQuery(q string) ([]string, string) {
 	var parts []string
 	for {
-		n := strings.IndexAny(q, "*{[")
+		n := strings.IndexAny(q, "*{}[,")
 		if n < 0 {
-			return append(parts, regexp.QuoteMeta(q))
+			parts = append(parts, regexp.QuoteMeta(q))
+			return parts, ""
 		}
 		parts = append(parts, regexp.QuoteMeta(q[:n]))
 		q = q[n:]
 		switch q[0] {
+		case ',', '}':
+			return parts, q
 		case '*':
 			parts = append(parts, "[^.]*")
 			q = q[1:]
 		case '{':
-			n := strings.IndexByte(q, '}')
-			if n < 0 {
-				return append(parts, regexp.QuoteMeta(q))
-			}
 			var tmp []string
-			for _, x := range strings.Split(q[1:n], ",") {
-				tmp = append(tmp, strings.Join(getRegexpPartsForGraphiteNodeQuery(x), ""))
+			for {
+				a, tail := getRegexpPartsForGraphiteQuery(q[1:])
+				tmp = append(tmp, strings.Join(a, ""))
+				if len(tail) == 0 {
+					parts = append(parts, regexp.QuoteMeta("{"))
+					parts = append(parts, strings.Join(tmp, ","))
+					return parts, ""
+				}
+				if tail[0] == ',' {
+					q = tail
+					continue
+				}
+				if tail[0] == '}' {
+					if len(tmp) == 1 {
+						parts = append(parts, tmp[0])
+					} else {
+						parts = append(parts, "(?:"+strings.Join(tmp, "|")+")")
+					}
+					q = tail[1:]
+					break
+				}
+				logger.Panicf("BUG: unexpected first char at tail %q; want `.` or `}`", tail)
 			}
-			parts = append(parts, "(?:"+strings.Join(tmp, "|")+")")
-			q = q[n+1:]
 		case '[':
 			n := strings.IndexByte(q, ']')
 			if n < 0 {
-				return append(parts, regexp.QuoteMeta(q))
+				parts = append(parts, regexp.QuoteMeta(q))
+				return parts, ""
 			}
 			parts = append(parts, q[:n+1])
 			q = q[n+1:]
diff --git a/lib/storage/tag_filters.go b/lib/storage/tag_filters.go
index 6d35beaf7..878f7621a 100644
--- a/lib/storage/tag_filters.go
+++ b/lib/storage/tag_filters.go
@@ -246,6 +246,9 @@ func (tf *tagFilter) InitFromGraphiteQuery(commonPrefix, query []byte, paths []s
 	if len(orSuffixes) == 0 {
 		orSuffixes = append(orSuffixes, "")
 	}
+	// Sort orSuffixes for faster seek later.
+	sort.Strings(orSuffixes)
+
 	tf.key = append(tf.key[:0], "__graphite__"...)
 	tf.value = append(tf.value[:0], query...)
 	tf.isNegative = isNegative


Test patch:
diff --git a/app/vmalert/datasource/vm_test.go b/app/vmalert/datasource/vm_test.go
index 1afe0d8e3..2c6148133 100644
--- a/app/vmalert/datasource/vm_test.go
+++ b/app/vmalert/datasource/vm_test.go
@@ -69,7 +69,7 @@ func TestVMSelectQuery(t *testing.T) {
 
 	srv := httptest.NewServer(mux)
 	defer srv.Close()
-	am := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, time.Minute, 0, srv.Client())
+	am := NewVMStorage(srv.URL, basicAuthName, basicAuthPass, time.Minute, 0, false, srv.Client())
 	if _, err := am.Query(ctx, query, NewPrometheusType()); err == nil {
 		t.Fatalf("expected connection error got nil")
 	}
diff --git a/app/vmselect/graphite/metrics_api_test.go b/app/vmselect/graphite/metrics_api_test.go
index 393255085..20556f5c6 100644
--- a/app/vmselect/graphite/metrics_api_test.go
+++ b/app/vmselect/graphite/metrics_api_test.go
@@ -17,20 +17,25 @@ func TestGetRegexpForQuery(t *testing.T) {
 			t.Fatalf("unexpected regexp for query=%q, delimiter=%c; got %s; want %s", query, delimiter, reStr, reExpected)
 		}
 	}
-	f("", '.', `^(?:\.?)$`)
-	f("foobar", '.', `^(?:foobar\.?)$`)
-	f("*", '.', `^(?:[^\.]*\.?)$`)
-	f("*", '_', `^(?:[^_]*_?)$`)
-	f("foo.*.bar", '.', `^(?:foo\.[^\.]*\.bar\.?)$`)
-	f("fo*b{ar,aaa}[a-z]xx*.d", '.', `^(?:fo[^\.]*b(?:ar|aaa)[a-z]xx[^\.]*\.d\.?)$`)
-	f("fo*b{ar,aaa}[a-z]xx*_d", '_', `^(?:fo[^_]*b(?:ar|aaa)[a-z]xx[^_]*_d_?)$`)
-	f("foo.[ab]*z", '.', `^(?:foo\.[ab][^\.]*z\.?)$`)
-	f("foo_[ab]*", '_', `^(?:foo_[ab][^_]*_?)$`)
-	f("foo_[ab]_", '_', `^(?:foo_[ab]_)$`)
-	f("foo.[ab].", '.', `^(?:foo\.[ab]\.)$`)
-	f("foo{b{ar*,ba*z[1-9]}", '.', `^(?:foo(?:b\{ar[^\.]*|ba[^\.]*z[1-9])\.?)$`)
-	f("{foo*}", '.', `^(?:(?:foo[^\.]*)\.?)$`)
-	f("{foo*,}", '.', `^(?:(?:foo[^\.]*|)\.?)$`)
+	f("", '.', `^\.?$`)
+	f("foobar", '.', `^foobar\.?$`)
+	f("*", '.', `^[^\.]*\.?$`)
+	f("*", '_', `^[^_]*_?$`)
+	f("foo.*.bar", '.', `^foo\.[^\.]*\.bar\.?$`)
+	f("fo*b{ar,aaa}[a-z]xx*.d", '.', `^fo[^\.]*b(?:ar|aaa)[a-z]xx[^\.]*\.d\.?$`)
+	f("fo*b{ar,aaa}[a-z]xx*_d", '_', `^fo[^_]*b(?:ar|aaa)[a-z]xx[^_]*_d_?$`)
+	f("foo.[ab]*z", '.', `^foo\.[ab][^\.]*z\.?$`)
+	f("foo_[ab]*", '_', `^foo_[ab][^_]*_?$`)
+	f("foo_[ab]_", '_', `^foo_[ab]_$`)
+	f("foo.[ab].", '.', `^foo\.[ab]\.$`)
+	f("foo{b{ar*,ba*z[1-9]}", '.', `^foo\{b(?:ar[^\.]*|ba[^\.]*z[1-9])\.?$`)
+	f("{foo*}", '.', `^(?:foo[^\.]*)\.?$`)
+	f("{foo*,}", '.', `^(?:foo[^\.]*|)\.?$`)
+	f("foo[bar", '.', `^foo\[bar\.?$`)
+	f("foo{bar", '.', `^foo\{bar\.?$`)
+	f("foo{ba,r", '.', `^foo\{ba,r\.?$`)
+	f("[a-z]", '.', `^[a-z]\.?$`)
+	f("{foo,x*,x{y,a*b}c}a", '.', `^(?:foo|x[^\.]*|x(?:y|a[^\.]*b)c)a\.?$`)
 }
 
 func TestSortPaths(t *testing.T) {
diff --git a/lib/storage/storage_test.go b/lib/storage/storage_test.go
index faa8cd8e5..53b5949f7 100644
--- a/lib/storage/storage_test.go
+++ b/lib/storage/storage_test.go
@@ -17,7 +17,7 @@ import (
 func TestGetRegexpForGraphiteNodeQuery(t *testing.T) {
 	f := func(q, expectedRegexp string) {
 		t.Helper()
-		re, err := getRegexpForGraphiteNodeQuery(q)
+		re, err := getRegexpForGraphiteQuery(q)
 		if err != nil {
 			t.Fatalf("unexpected error for query=%q: %s", q, err)
 		}
@@ -34,6 +34,11 @@ func TestGetRegexpForGraphiteNodeQuery(t *testing.T) {
 	f(`[-a-zx.]`, `^[-a-zx.]$`)
 	f(`**`, `^[^.]*[^.]*$`)
 	f(`a*[de]{x,y}z`, `^a[^.]*[de](?:x|y)z$`)
+	f(`foo{bar`, `^foo\{bar$`)
+	f(`foo{ba,r`, `^foo\{ba,r$`)
+	f(`foo[bar`, `^foo\[bar$`)
+	f(`foo{bar}`, `^foobar$`)
+	f(`foo{bar,,b{{a,b*},z},[x-y]*z}a`, `^foo(?:bar||b(?:(?:a|b[^.]*)|z)|[x-y][^.]*z)a$`)
 }
 
 func TestDateMetricIDCacheSerial(t *testing.T) {