├── .cache └── .gitkeep ├── .circleci └── config.yml ├── .dockerignore ├── .editorconfig ├── .github ├── stale.yml └── workflows │ └── docker-build.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── aggregation ├── cardinality │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ └── aggregation │ │ │ └── cardinality │ │ │ ├── CardinalityAggregation.kt │ │ │ ├── CardinalityBucket.java │ │ │ ├── CardinalityInstance.kt │ │ │ ├── CardinalityMethod.kt │ │ │ ├── DistributedCardinalityInstance.kt │ │ │ ├── ExactCardinalityBucket.kt │ │ │ ├── HyperLogLogCardinalityBucket.kt │ │ │ ├── HyperLogLogPlusCardinalityBucket.kt │ │ │ ├── Module.java │ │ │ ├── ReduceHyperLogLogCardinalityBucket.kt │ │ │ └── ReduceHyperLogLogPlusCardinalityBucket.kt │ │ └── test │ │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── aggregation │ │ └── cardinality │ │ ├── AbstractCardinalityBucketTest.java │ │ ├── ExactCardinalityBucketTest.java │ │ ├── HyperLogLogCardinalityBucketTest.java │ │ └── SerializationTest.java └── simple │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── aggregation │ │ └── simple │ │ ├── AboveK.kt │ │ ├── AboveKInstance.kt │ │ ├── Aggregations.java │ │ ├── Average.kt │ │ ├── AverageBucket.kt │ │ ├── AverageInstance.kt │ │ ├── BelowK.kt │ │ ├── BelowKInstance.kt │ │ ├── BottomK.kt │ │ ├── BottomKInstance.kt │ │ ├── Count.kt │ │ ├── CountBucket.kt │ │ ├── CountInstance.kt │ │ ├── Delta.kt │ │ ├── DeltaInstance.kt │ │ ├── DeltaPerSecond.kt │ │ ├── DeltaPerSecondInstance.kt │ │ ├── DistributedBucketInstance.java │ │ ├── FilterAggregation.kt │ │ ├── FilterKAreaStrategy.kt │ │ ├── FilterKAreaType.java │ │ ├── FilterKThresholdStrategy.kt │ │ ├── FilterKThresholdType.java │ │ ├── FilterPointsThresholdStrategy.kt │ │ ├── FilterStrategy.java │ │ ├── FilterableMetrics.kt │ │ ├── GroupUnique.kt │ │ ├── GroupUniqueBucket.kt │ │ ├── GroupUniqueInstance.kt │ │ ├── Max.kt │ │ ├── MaxBucket.kt │ │ ├── MaxInstance.kt │ │ ├── MetricMappingAggregation.kt │ │ ├── MetricMappingStrategy.java │ │ ├── Min.kt │ │ ├── MinBucket.kt │ │ ├── MinInstance.kt │ │ ├── Module.java │ │ ├── NotNegative.kt │ │ ├── NotNegativeInstance.kt │ │ ├── PointPairArea.java │ │ ├── PointsAbove.kt │ │ ├── PointsAboveInstance.kt │ │ ├── PointsBelow.kt │ │ ├── PointsBelowInstance.kt │ │ ├── Quantile.kt │ │ ├── QuantileBucket.kt │ │ ├── QuantileInstance.kt │ │ ├── RatePerSecond.kt │ │ ├── RatePerSecondInstance.kt │ │ ├── Spread.kt │ │ ├── SpreadBucket.kt │ │ ├── SpreadInstance.kt │ │ ├── StdDev.kt │ │ ├── StdDevBucket.kt │ │ ├── StdDevInstance.kt │ │ ├── StripedAverageBucket.kt │ │ ├── StripedCountBucket.kt │ │ ├── StripedMaxBucket.kt │ │ ├── StripedMinBucket.kt │ │ ├── StripedStdDevBucket.kt │ │ ├── StripedSum2Bucket.kt │ │ ├── StripedSumBucket.kt │ │ ├── Sum.kt │ │ ├── Sum2.kt │ │ ├── Sum2Bucket.kt │ │ ├── Sum2Instance.kt │ │ ├── SumBucket.kt │ │ ├── SumInstance.kt │ │ ├── Tdigest.kt │ │ ├── TdigestInstance.kt │ │ ├── TdigestInstanceUtils.java │ │ ├── TdigestMergingBucket.kt │ │ ├── Template.kt │ │ ├── TemplateInstance.kt │ │ ├── TopK.kt │ │ └── TopKInstance.kt │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ ├── aggregation │ └── simple │ │ ├── AggregationSerializationTest.java │ │ ├── AverageBucketTest.java │ │ ├── CountBucketTest.java │ │ ├── DeltaPerSecondTest.java │ │ ├── DeltaTest.java │ │ ├── DistributionPointUtils.java │ │ ├── FilterAggregationTest.java │ │ ├── FilterKAreaAggregationTest.java │ │ ├── FilterKAreaStrategyTest.java │ │ ├── FilterKThresholdAggregationTest.java │ │ ├── FilterPointsThresholdStrategyTest.java │ │ ├── MaxBucketIntegrationTest.java │ │ ├── MaxBucketTest.java │ │ ├── MinBucketIntegrationTest.java │ │ ├── MinBucketTest.java │ │ ├── NotNegativeTest.java │ │ ├── OfSupplier.java │ │ ├── PointPairAreaTest.java │ │ ├── PointsAboveInstanceTest.java │ │ ├── PointsBelowInstanceTest.java │ │ ├── QuantileBucketTest.java │ │ ├── RatePerSecondTest.java │ │ ├── SamplingSupplier.java │ │ ├── StdDevBucketTest.java │ │ ├── Sum2BucketTest.java │ │ ├── SumBucketTest.java │ │ ├── TdigestAggregationTest.java │ │ ├── TdigestBucketIntegrationTest.java │ │ ├── TdigestBucketTest.java │ │ └── ValueBucketIntegrationTest.java │ └── test │ └── ValueSuppliers.java ├── assets ├── README.md ├── logo_on_dark.svg └── logo_on_light.svg ├── build.gradle ├── checkstyle.xml ├── codecov.yml ├── consumer ├── collectd │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── consumer │ │ └── collectd │ │ ├── CollectdChannelHandler.java │ │ ├── CollectdConsumer.java │ │ ├── CollectdConsumerModule.java │ │ ├── CollectdParser.java │ │ ├── CollectdSample.kt │ │ ├── CollectdScope.java │ │ ├── CollectdTypes.java │ │ ├── CollectdValue.kt │ │ ├── Module.java │ │ └── Server.java ├── kafka │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── consumer │ │ └── kafka │ │ ├── Connection.java │ │ ├── ConsumerThread.java │ │ ├── ConsumerThreadCoordinator.java │ │ ├── KafkaConnection.java │ │ ├── KafkaConsumer.java │ │ ├── KafkaConsumerModule.java │ │ ├── KafkaScope.java │ │ ├── KafkaStream.java │ │ ├── Module.java │ │ ├── RealKafkaConnection.java │ │ └── RealKafkaStream.java └── pubsub │ ├── build.gradle │ └── src │ └── main │ └── java │ └── com │ └── spotify │ └── heroic │ └── consumer │ └── pubsub │ ├── Connection.java │ ├── Module.java │ ├── PubSubConsumer.java │ ├── PubSubConsumerModule.java │ ├── PubSubScope.java │ └── Receiver.java ├── discovery └── simple │ ├── build.gradle │ └── src │ └── main │ └── java │ └── com │ └── spotify │ └── heroic │ └── cluster │ └── discovery │ └── simple │ ├── DiscoveryScope.java │ ├── Module.java │ ├── SrvRecordDiscovery.java │ ├── SrvRecordDiscoveryModule.java │ ├── StaticListDiscovery.java │ └── StaticListDiscoveryModule.java ├── docs ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yml ├── _data │ └── fta │ │ └── pubsub-consumers.json ├── _includes │ ├── api-accept.html │ ├── api-field-type.html │ ├── api-response.html │ ├── api-type.html │ ├── federation-tail.html │ ├── fields │ │ └── post-query-metrics │ │ │ └── filter.html │ ├── github-link.html │ ├── mkhash.inc │ ├── requests │ │ ├── metadata-query-body.json │ │ ├── metadata-tagkey-count.json │ │ ├── post-metadata-key-suggest.json │ │ ├── post-metadata-series-count.json │ │ ├── post-metadata-tag-suggest.json │ │ ├── post-metadata-tag-value-suggest.json │ │ ├── post-query-batch.json │ │ ├── post-query-metrics.json │ │ ├── post-write.json │ │ └── put-metadata-series.json │ └── responses │ │ ├── delete-metadata-series.json │ │ ├── get-status-failed.html │ │ ├── get-status.json │ │ ├── post-metadata-key-suggest.json │ │ ├── post-metadata-keys.json │ │ ├── post-metadata-series-count.json │ │ ├── post-metadata-series.json │ │ ├── post-metadata-tag-suggest.json │ │ ├── post-metadata-tag-value-suggest.json │ │ ├── post-metadata-tagkey-count.json │ │ ├── post-metadata-tags.json │ │ ├── post-query-batch.json │ │ ├── post-query-metrics.json │ │ ├── post-write.json │ │ └── put-metadata-series.json ├── _layouts │ ├── api-endpoint.html │ ├── api-type-structure.html │ ├── api-type.html │ ├── default.html │ ├── fault-tree.html │ └── sidebar.html ├── assets │ ├── _sass │ │ ├── _bootstrap-compass.scss │ │ ├── _bootstrap-mincer.scss │ │ ├── _bootstrap-sprockets.scss │ │ ├── _bootstrap.scss │ │ ├── _theme.scss │ │ └── bootstrap │ │ │ ├── _alerts.scss │ │ │ ├── _badges.scss │ │ │ ├── _breadcrumbs.scss │ │ │ ├── _button-groups.scss │ │ │ ├── _buttons.scss │ │ │ ├── _carousel.scss │ │ │ ├── _close.scss │ │ │ ├── _code.scss │ │ │ ├── _component-animations.scss │ │ │ ├── _dropdowns.scss │ │ │ ├── _forms.scss │ │ │ ├── _glyphicons.scss │ │ │ ├── _grid.scss │ │ │ ├── _input-groups.scss │ │ │ ├── _jumbotron.scss │ │ │ ├── _labels.scss │ │ │ ├── _list-group.scss │ │ │ ├── _media.scss │ │ │ ├── _mixins.scss │ │ │ ├── _modals.scss │ │ │ ├── _navbar.scss │ │ │ ├── _navs.scss │ │ │ ├── _normalize.scss │ │ │ ├── _pager.scss │ │ │ ├── _pagination.scss │ │ │ ├── _panels.scss │ │ │ ├── _popovers.scss │ │ │ ├── _print.scss │ │ │ ├── _progress-bars.scss │ │ │ ├── _responsive-embed.scss │ │ │ ├── _responsive-utilities.scss │ │ │ ├── _scaffolding.scss │ │ │ ├── _tables.scss │ │ │ ├── _theme.scss │ │ │ ├── _thumbnails.scss │ │ │ ├── _tooltip.scss │ │ │ ├── _type.scss │ │ │ ├── _utilities.scss │ │ │ ├── _variables.scss │ │ │ ├── _wells.scss │ │ │ └── mixins │ │ │ ├── _alerts.scss │ │ │ ├── _background-variant.scss │ │ │ ├── _border-radius.scss │ │ │ ├── _buttons.scss │ │ │ ├── _center-block.scss │ │ │ ├── _clearfix.scss │ │ │ ├── _forms.scss │ │ │ ├── _gradients.scss │ │ │ ├── _grid-framework.scss │ │ │ ├── _grid.scss │ │ │ ├── _hide-text.scss │ │ │ ├── _image.scss │ │ │ ├── _labels.scss │ │ │ ├── _list-group.scss │ │ │ ├── _nav-divider.scss │ │ │ ├── _nav-vertical-align.scss │ │ │ ├── _opacity.scss │ │ │ ├── _pagination.scss │ │ │ ├── _panels.scss │ │ │ ├── _progress-bar.scss │ │ │ ├── _reset-filter.scss │ │ │ ├── _reset-text.scss │ │ │ ├── _resize.scss │ │ │ ├── _responsive-visibility.scss │ │ │ ├── _size.scss │ │ │ ├── _tab-focus.scss │ │ │ ├── _table-row.scss │ │ │ ├── _text-emphasis.scss │ │ │ ├── _text-overflow.scss │ │ │ └── _vendor-prefixes.scss │ ├── css │ │ ├── api.scss │ │ ├── docs.scss │ │ ├── fault-tree.scss │ │ └── index.scss │ ├── fta │ │ └── pubsub-consumers.mef │ └── js │ │ ├── fault-tree-graph.js │ │ ├── prism-hql.js │ │ └── prism-ts.js ├── content │ ├── _api_types │ │ ├── aggregation.md │ │ ├── filter.md │ │ ├── match-options.html │ │ ├── metric-collection.md │ │ ├── point.md │ │ ├── query-date-range.html │ │ ├── request-error.html │ │ ├── sampling-query.md │ │ ├── series.md │ │ ├── sharded-result-group.md │ │ └── statistics.md │ ├── _docs │ │ ├── aggregations.html │ │ ├── api.html │ │ ├── architecture.html │ │ ├── bigtable.md │ │ ├── config.md │ │ ├── data_model.html │ │ ├── federation.html │ │ ├── getting_started.md │ │ ├── index.html │ │ ├── overview.html │ │ ├── profiles.html │ │ ├── query_language.html │ │ ├── releases.md │ │ ├── reliability.md │ │ ├── shell.html │ │ └── visualizations.html │ ├── _endpoints │ │ ├── delete-metadata-series.md │ │ ├── get-status.html │ │ ├── post-metadata-key-suggest.md │ │ ├── post-metadata-keys.md │ │ ├── post-metadata-series-count.md │ │ ├── post-metadata-series.md │ │ ├── post-metadata-tag-suggest.md │ │ ├── post-metadata-tag-value-suggest.md │ │ ├── post-metadata-tagkey-count.md │ │ ├── post-metadata-tags.md │ │ ├── post-query-batch.md │ │ ├── post-query-metrics.md │ │ ├── post-write.md │ │ └── put-metadata-series.md │ ├── _fault_tree │ │ └── pubsub-consumers.md │ └── _tutorials │ │ ├── index.html │ │ └── kafka_consumer.html ├── images │ ├── aggregation_average.svg │ ├── aggregation_group.svg │ ├── aggregation_group_average.svg │ ├── aggregation_max.svg │ ├── aggregation_min.svg │ ├── aggregation_sum.svg │ ├── errors.svg │ ├── filter-dsl.svg │ ├── logo_on_dark.48.png │ ├── logo_on_light.256.png │ ├── logo_on_light.400.png │ ├── sharding.svg │ ├── size_extent.svg │ ├── size_extent_3.svg │ └── template.svg └── index.html ├── example ├── heroic-memory-example.yml ├── heroic-querylog-logstash.conf ├── heroic_consumer_pubsub_local_debug.yml └── log4j2-file.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── guide-to-dagger2.md ├── heroic-component ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ ├── ExtraParameters.java │ │ │ ├── HeroicConfiguration.java │ │ │ ├── HeroicConfigurationContext.java │ │ │ ├── HeroicContext.java │ │ │ ├── HeroicCoreInstance.java │ │ │ ├── HeroicModule.java │ │ │ ├── HeroicReporterConfiguration.java │ │ │ ├── ObjectHasher.java │ │ │ ├── ParameterSpecification.java │ │ │ ├── Query.kt │ │ │ ├── QueryBuilder.java │ │ │ ├── QueryComponent.java │ │ │ ├── QueryDateRange.kt │ │ │ ├── QueryManager.java │ │ │ ├── QueryOptions.java │ │ │ ├── ShellTasks.java │ │ │ ├── aggregation │ │ │ ├── AbstractAggregationDSL.java │ │ │ ├── AbstractBucket.java │ │ │ ├── Aggregation.java │ │ │ ├── AggregationArguments.java │ │ │ ├── AggregationCombiner.java │ │ │ ├── AggregationContext.java │ │ │ ├── AggregationDSL.java │ │ │ ├── AggregationFactory.java │ │ │ ├── AggregationInstance.java │ │ │ ├── AggregationOrList.kt │ │ │ ├── AggregationOrListDeserializer.java │ │ │ ├── AggregationOutput.kt │ │ │ ├── AggregationRegistry.java │ │ │ ├── AggregationResult.kt │ │ │ ├── AggregationSession.java │ │ │ ├── Aggregations.java │ │ │ ├── AnyBucket.java │ │ │ ├── Bucket.kt │ │ │ ├── BucketAggregationInstance.kt │ │ │ ├── BucketConsumer.java │ │ │ ├── BucketStrategy.java │ │ │ ├── BucketStrategyStartEnd.kt │ │ │ ├── Chain.java │ │ │ ├── ChainInstance.java │ │ │ ├── Collapse.java │ │ │ ├── CollapseInstance.java │ │ │ ├── ComputeDistributionStat.java │ │ │ ├── DistributedAggregationCombiner.java │ │ │ ├── DoubleBucket.java │ │ │ ├── Empty.kt │ │ │ ├── EmptyInstance.java │ │ │ ├── Group.java │ │ │ ├── GroupInstance.java │ │ │ ├── GroupingAggregation.java │ │ │ ├── GroupingAggregationBuilder.java │ │ │ ├── GroupingAggregationSerializer.java │ │ │ ├── MissingAggregation.java │ │ │ ├── Module.java │ │ │ ├── Options.kt │ │ │ ├── RetainQuotaWatcher.java │ │ │ ├── SamplingAggregation.kt │ │ │ ├── SamplingAggregationDSL.java │ │ │ ├── SamplingQuery.kt │ │ │ ├── TDigestAggregationCombiner.java │ │ │ └── TDigestBucket.java │ │ │ ├── analytics │ │ │ ├── AnalyticsComponent.java │ │ │ ├── AnalyticsModule.java │ │ │ ├── MetricAnalytics.java │ │ │ └── SeriesHit.kt │ │ │ ├── async │ │ │ ├── AsyncObservable.java │ │ │ └── AsyncObserver.java │ │ │ ├── cache │ │ │ ├── CacheComponent.java │ │ │ ├── CacheModule.java │ │ │ └── QueryCache.java │ │ │ ├── cluster │ │ │ ├── ClusterComponent.java │ │ │ ├── ClusterDiscovery.java │ │ │ ├── ClusterDiscoveryComponent.java │ │ │ ├── ClusterDiscoveryModule.java │ │ │ ├── ClusterManager.kt │ │ │ ├── ClusterNode.java │ │ │ ├── ClusterNodeGroup.java │ │ │ ├── ClusterScope.java │ │ │ ├── ClusterShard.java │ │ │ ├── NodeMetadata.kt │ │ │ ├── NodeMetadataFactory.java │ │ │ ├── NodeMetadataProvider.java │ │ │ ├── RpcProtocol.java │ │ │ ├── RpcProtocolComponent.java │ │ │ └── RpcProtocolModule.java │ │ │ ├── common │ │ │ ├── BiConsumerIO.java │ │ │ ├── Collected.java │ │ │ ├── DateRange.java │ │ │ ├── Duration.kt │ │ │ ├── DynamicModuleId.java │ │ │ ├── EmptyOptionalLimit.java │ │ │ ├── FailureType.java │ │ │ ├── Feature.java │ │ │ ├── FeatureSet.kt │ │ │ ├── Features.java │ │ │ ├── FunctionIO.java │ │ │ ├── GoAwayException.java │ │ │ ├── GrokProcessor.java │ │ │ ├── GroupMember.kt │ │ │ ├── GroupSet.java │ │ │ ├── Grouped.java │ │ │ ├── Groups.kt │ │ │ ├── Histogram.kt │ │ │ ├── Initializing.java │ │ │ ├── JavaxRestFramework.java │ │ │ ├── ModuleId.java │ │ │ ├── ModuleIdBuilder.java │ │ │ ├── OptionalLimit.java │ │ │ ├── Optionals.java │ │ │ ├── QuotaViolationException.java │ │ │ ├── ReflectionUtils.java │ │ │ ├── RequestTimer.java │ │ │ ├── SelectedGroup.java │ │ │ ├── Series.java │ │ │ ├── ServiceInfo.kt │ │ │ ├── SingleLifeCycled.java │ │ │ ├── Statistics.kt │ │ │ ├── TagPair.kt │ │ │ ├── Throwing.java │ │ │ ├── TimeUtils.java │ │ │ ├── UsableGroupManager.java │ │ │ ├── Validation.java │ │ │ └── ValueOptionalLimit.java │ │ │ ├── conditionalfeatures │ │ │ └── ConditionalFeatures.java │ │ │ ├── consumer │ │ │ ├── Consumer.java │ │ │ ├── ConsumerModule.java │ │ │ ├── ConsumerSchema.java │ │ │ ├── ConsumerSchemaException.java │ │ │ ├── ConsumerSchemaScope.java │ │ │ ├── ConsumerSchemaValidationException.java │ │ │ ├── ConsumerSchemaWriteException.java │ │ │ ├── ConsumersComponent.java │ │ │ └── FatalSchemaException.java │ │ │ ├── dagger │ │ │ ├── CoreComponent.java │ │ │ ├── EarlyComponent.java │ │ │ ├── LoadingComponent.java │ │ │ └── PrimaryComponent.java │ │ │ ├── filter │ │ │ ├── AndFilter.java │ │ │ ├── FalseFilter.java │ │ │ ├── Filter.java │ │ │ ├── FilterEncoding.java │ │ │ ├── FilterEncodingComponent.kt │ │ │ ├── FilterModifier.java │ │ │ ├── FilterUtils.java │ │ │ ├── HasTagFilter.java │ │ │ ├── MatchKeyFilter.java │ │ │ ├── MatchTagFilter.java │ │ │ ├── NotFilter.java │ │ │ ├── OrFilter.java │ │ │ ├── RawFilter.java │ │ │ ├── RegexFilter.java │ │ │ ├── StartsWithFilter.java │ │ │ └── TrueFilter.java │ │ │ ├── generator │ │ │ ├── Generator.java │ │ │ ├── GeneratorComponent.java │ │ │ ├── GeneratorManager.java │ │ │ ├── MetadataGenerator.java │ │ │ └── MetricGeneratorModule.java │ │ │ ├── grammar │ │ │ ├── Context.kt │ │ │ ├── DSL.java │ │ │ ├── DateTimeExpression.kt │ │ │ ├── DefaultScope.java │ │ │ ├── DivideExpression.kt │ │ │ ├── DoubleExpression.kt │ │ │ ├── DurationExpression.kt │ │ │ ├── EmptyExpression.kt │ │ │ ├── Expression.java │ │ │ ├── FunctionExpression.kt │ │ │ ├── InstantExpression.kt │ │ │ ├── IntegerExpression.kt │ │ │ ├── LetExpression.kt │ │ │ ├── ListExpression.kt │ │ │ ├── MinusExpression.kt │ │ │ ├── MultiplyExpression.kt │ │ │ ├── NegateExpression.kt │ │ │ ├── ParseException.kt │ │ │ ├── PlusExpression.kt │ │ │ ├── QueryExpression.kt │ │ │ ├── QueryParser.java │ │ │ ├── RangeExpression.kt │ │ │ ├── ReferenceExpression.kt │ │ │ ├── StringExpression.kt │ │ │ └── TimeExpression.kt │ │ │ ├── ingestion │ │ │ ├── Ingestion.java │ │ │ ├── IngestionComponent.java │ │ │ ├── IngestionFatalException.java │ │ │ ├── IngestionGroup.java │ │ │ ├── IngestionInvalidException.java │ │ │ ├── IngestionManager.java │ │ │ └── Request.kt │ │ │ ├── lifecycle │ │ │ ├── LifeCycle.java │ │ │ ├── LifeCycleHook.java │ │ │ ├── LifeCycleManager.java │ │ │ ├── LifeCycleNamedHook.java │ │ │ ├── LifeCycleRegistry.java │ │ │ ├── LifeCycles.java │ │ │ └── ManyLifeCycle.kt │ │ │ ├── metadata │ │ │ ├── CountSeries.kt │ │ │ ├── DeleteSeries.kt │ │ │ ├── Entries.kt │ │ │ ├── FindKeys.kt │ │ │ ├── FindSeries.kt │ │ │ ├── FindSeriesIds.kt │ │ │ ├── FindSeriesIdsStream.kt │ │ │ ├── FindSeriesStream.kt │ │ │ ├── FindTags.kt │ │ │ ├── MetadataBackend.java │ │ │ ├── MetadataComponent.java │ │ │ ├── MetadataManager.java │ │ │ ├── MetadataModule.java │ │ │ └── WriteMetadata.kt │ │ │ ├── metric │ │ │ ├── AbstractMetricBackend.java │ │ │ ├── BackendEntry.kt │ │ │ ├── BackendKey.kt │ │ │ ├── BackendKeyFilter.kt │ │ │ ├── BackendKeySet.kt │ │ │ ├── CacheInfo.kt │ │ │ ├── Distribution.java │ │ │ ├── DistributionPoint.java │ │ │ ├── DistributionPointDeserialize.java │ │ │ ├── DistributionPointSerializer.java │ │ │ ├── FetchData.kt │ │ │ ├── FetchQuotaWatcher.java │ │ │ ├── FullQuery.java │ │ │ ├── HeroicDistribution.java │ │ │ ├── Metric.kt │ │ │ ├── MetricBackend.java │ │ │ ├── MetricBackendGroup.java │ │ │ ├── MetricCollection.java │ │ │ ├── MetricComponent.java │ │ │ ├── MetricGroup.kt │ │ │ ├── MetricManager.java │ │ │ ├── MetricModule.java │ │ │ ├── MetricReadResult.kt │ │ │ ├── MetricType.java │ │ │ ├── MetricsConnectionSettings.kt │ │ │ ├── NodeError.kt │ │ │ ├── Payload.kt │ │ │ ├── Point.kt │ │ │ ├── QueryError.kt │ │ │ ├── QueryMetrics.java │ │ │ ├── QueryMetricsResponse.kt │ │ │ ├── QueryMetricsResponseSerializer.java │ │ │ ├── QueryResult.kt │ │ │ ├── QueryResultPart.kt │ │ │ ├── QueryTrace.java │ │ │ ├── RequestError.java │ │ │ ├── ResultGroup.kt │ │ │ ├── ResultLimit.java │ │ │ ├── ResultLimits.kt │ │ │ ├── RuntimeNodeException.java │ │ │ ├── SeriesSetsSummarizer.kt │ │ │ ├── SeriesValues.kt │ │ │ ├── ShardError.kt │ │ │ ├── ShardedResultGroup.kt │ │ │ ├── Spread.kt │ │ │ ├── TdigestPoint.java │ │ │ ├── TdigestPointDeserialize.java │ │ │ ├── TdigestPointSerializer.java │ │ │ ├── Tracing.java │ │ │ ├── WriteMetric.kt │ │ │ └── consts │ │ │ │ └── ApiQueryConsts.java │ │ │ ├── metrics │ │ │ ├── Clock.java │ │ │ ├── EWMA.java │ │ │ └── Meter.java │ │ │ ├── querylogging │ │ │ ├── HttpContext.kt │ │ │ ├── QueryContext.java │ │ │ ├── QueryLogger.java │ │ │ ├── QueryLoggerFactory.java │ │ │ ├── QueryLoggingComponent.java │ │ │ ├── QueryLoggingModule.java │ │ │ └── QueryLoggingScope.java │ │ │ ├── requestcondition │ │ │ └── RequestCondition.java │ │ │ ├── scheduler │ │ │ ├── Scheduler.java │ │ │ └── Task.java │ │ │ ├── shell │ │ │ ├── CoreInterface.java │ │ │ ├── ServerConnection.java │ │ │ ├── ShellConnection.java │ │ │ ├── ShellIO.java │ │ │ ├── ShellTask.java │ │ │ ├── ShellTaskDefinition.java │ │ │ ├── TaskName.java │ │ │ ├── TaskParameters.java │ │ │ ├── TaskUsage.java │ │ │ └── protocol │ │ │ │ ├── MessageBuilder.java │ │ │ │ └── SimpleMessageVisitor.java │ │ │ ├── statistics │ │ │ ├── AnalyticsReporter.java │ │ │ ├── ClusteredManager.java │ │ │ ├── ConsumerReporter.java │ │ │ ├── DataInMemoryReporter.java │ │ │ ├── FutureReporter.java │ │ │ ├── HeroicReporter.java │ │ │ ├── HeroicTimer.java │ │ │ ├── IngestionManagerReporter.java │ │ │ ├── MemcachedReporter.java │ │ │ ├── MetadataBackendReporter.java │ │ │ ├── MetricBackendReporter.java │ │ │ ├── QueryReporter.java │ │ │ ├── StatisticsComponent.java │ │ │ ├── StatisticsModule.java │ │ │ ├── SuggestBackendReporter.java │ │ │ └── noop │ │ │ │ ├── NoopAnalyticsReporter.java │ │ │ │ ├── NoopConsumerReporter.java │ │ │ │ ├── NoopFutureReporterContext.java │ │ │ │ ├── NoopHeroicReporter.java │ │ │ │ ├── NoopIngestionManagerReporter.java │ │ │ │ ├── NoopMemcachedReporter.java │ │ │ │ ├── NoopMetadataBackendReporter.java │ │ │ │ ├── NoopMetricBackendReporter.java │ │ │ │ ├── NoopQueryReporter.java │ │ │ │ ├── NoopScope.java │ │ │ │ ├── NoopStatisticsComponent.java │ │ │ │ ├── NoopStatisticsModule.java │ │ │ │ └── NoopSuggestBackendReporter.java │ │ │ ├── suggest │ │ │ ├── KeySuggest.kt │ │ │ ├── MatchOptions.kt │ │ │ ├── NumSuggestionsLimit.java │ │ │ ├── SuggestBackend.java │ │ │ ├── SuggestComponent.java │ │ │ ├── SuggestManager.java │ │ │ ├── SuggestModule.java │ │ │ ├── TagKeyCount.kt │ │ │ ├── TagSuggest.kt │ │ │ ├── TagValueSuggest.kt │ │ │ ├── TagValuesSuggest.kt │ │ │ └── WriteSuggest.kt │ │ │ ├── time │ │ │ └── Clock.java │ │ │ ├── tracing │ │ │ ├── EndSpanFutureReporter.java │ │ │ └── TracingConfig.kt │ │ │ └── usagetracking │ │ │ ├── UsageTracking.kt │ │ │ ├── UsageTrackingComponent.kt │ │ │ └── UsageTrackingModule.kt │ └── proto │ │ └── shell_message.proto │ └── test │ ├── java │ └── com │ │ └── spotify │ │ └── heroic │ │ ├── AbstractReducedResultTest.java │ │ ├── ExtraParametersTest.java │ │ ├── ObjectHasherTest.java │ │ ├── aggregation │ │ ├── AggregationArgumentsTest.java │ │ ├── AggregationOrListTest.java │ │ ├── AggregationOutputTest.java │ │ ├── AggregationTest.java │ │ ├── AnyBucketTest.java │ │ ├── BucketAggregationTest.java │ │ ├── BucketStrategyTest.java │ │ ├── ChainDeserializeTest.java │ │ ├── ChainInstanceTest.java │ │ ├── ChainTest.java │ │ └── GroupingAggregationTest.java │ │ ├── common │ │ ├── DateRangeTest.java │ │ ├── DurationTest.java │ │ ├── FeatureSetTest.java │ │ ├── FeatureTest.java │ │ ├── FeaturesTest.java │ │ ├── GrokProcessorTest.java │ │ ├── GroupSetTest.java │ │ ├── HistogramTest.java │ │ ├── OptionalLimitTest.java │ │ ├── SelectedGroupTest.java │ │ ├── SeriesTest.java │ │ └── ThrowingTest.java │ │ ├── filter │ │ └── FilterTest.java │ │ ├── grammar │ │ ├── AbstractExpressionTest.java │ │ ├── ContextTest.java │ │ ├── DateTimeExpressionTest.java │ │ ├── DefaultScopeTest.java │ │ ├── DivideExpressionTest.java │ │ ├── DoubleExpressionTest.java │ │ ├── DurationExpressionTest.java │ │ ├── EmptyExpressionTest.java │ │ ├── ExpressionTest.java │ │ ├── ExpressionTests.java │ │ ├── FunctionExpressionTest.java │ │ ├── InstantExpressionTest.java │ │ ├── IntegerExpressionTest.java │ │ ├── LetExpressionTest.java │ │ ├── ListExpressionTest.java │ │ ├── MinusExpressionTest.java │ │ ├── MultiplyExpressionTest.java │ │ ├── NegateExpressionTest.java │ │ ├── PlusExpressionTest.java │ │ ├── QueryExpressionTest.java │ │ ├── RangeExpressionTest.java │ │ ├── ReferenceExpressionTest.java │ │ ├── StringExpressionTest.java │ │ └── TimeExpressionTest.java │ │ ├── lifecycle │ │ └── LifeCycleTest.java │ │ ├── metadata │ │ ├── CountSeriesTest.java │ │ ├── DeleteSeriesTest.java │ │ └── FindSeriesTest.java │ │ ├── metric │ │ ├── BackendKeyFilterTest.java │ │ ├── QueryTraceTest.java │ │ ├── ShardedResultGroupTest.java │ │ └── TracingTest.java │ │ ├── suggest │ │ ├── KeySuggestTest.java │ │ ├── NumSuggestionsLimitTest.java │ │ ├── TagKeyCountTest.java │ │ ├── TagSuggestTest.java │ │ ├── TagValueSuggestTest.java │ │ └── TagValuesSuggestTest.java │ │ └── test │ │ ├── Resources.java │ │ └── TestProperties.java │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── heroic-core ├── build.gradle └── src │ ├── main │ ├── antlr │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ └── grammar │ │ │ └── HeroicQuery.g4 │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ ├── CoreHeroicConfigurationContext.java │ │ │ ├── CoreHeroicContext.java │ │ │ ├── CoreQueryComponent.java │ │ │ ├── CoreQueryManager.java │ │ │ ├── CoreShellTasks.java │ │ │ ├── HeroicBootstrap.java │ │ │ ├── HeroicConfig.java │ │ │ ├── HeroicCore.java │ │ │ ├── HeroicMappers.java │ │ │ ├── HeroicProfile.java │ │ │ ├── HeroicStartupPinger.java │ │ │ ├── ModuleUtils.java │ │ │ ├── PingMessage.kt │ │ │ ├── QueryModule.java │ │ │ ├── QueryScope.java │ │ │ ├── aggregation │ │ │ ├── CoreAggregationFactory.java │ │ │ └── CoreAggregationRegistry.java │ │ │ ├── analytics │ │ │ ├── NullAnalyticsComponent.java │ │ │ ├── NullAnalyticsModule.java │ │ │ ├── NullMetricAnalytics.java │ │ │ └── NullScope.java │ │ │ ├── args4j │ │ │ ├── CmdLine.java │ │ │ ├── DurationOptionHandler.java │ │ │ ├── OptionalLimitOptionHandler.java │ │ │ └── OptionalOptionHandler.java │ │ │ ├── cache │ │ │ ├── CacheScope.java │ │ │ ├── Module.java │ │ │ ├── memcached │ │ │ │ ├── CachedResult.kt │ │ │ │ ├── MemcachedCacheModule.java │ │ │ │ └── MemcachedQueryCache.java │ │ │ ├── memory │ │ │ │ ├── MemoryCacheModule.java │ │ │ │ └── MemoryQueryCache.java │ │ │ └── noop │ │ │ │ ├── NoopCacheModule.java │ │ │ │ └── NoopQueryCache.java │ │ │ ├── cluster │ │ │ ├── ClusterManagerModule.java │ │ │ ├── CoreClusterComponent.java │ │ │ ├── CoreClusterManager.java │ │ │ ├── LocalClusterNode.java │ │ │ ├── NodeRegistry.java │ │ │ ├── RemovedNode.kt │ │ │ ├── TracingClusterNode.java │ │ │ └── Update.kt │ │ │ ├── common │ │ │ ├── CoreJavaxRestFramework.java │ │ │ ├── DurationSerialization.java │ │ │ ├── GroupsSerialization.java │ │ │ └── TypeNameMixin.java │ │ │ ├── conditionalfeatures │ │ │ ├── List.java │ │ │ ├── Match.java │ │ │ └── Module.java │ │ │ ├── consumer │ │ │ ├── ConsumersScope.java │ │ │ ├── CoreConsumersComponent.java │ │ │ ├── CoreConsumersModule.java │ │ │ ├── SchemaScope.java │ │ │ └── schemas │ │ │ │ ├── Spotify100.java │ │ │ │ ├── Spotify100Proto.java │ │ │ │ └── spotify100 │ │ │ │ ├── JsonMetric.kt │ │ │ │ ├── Version.kt │ │ │ │ └── v2 │ │ │ │ ├── JsonMetric.kt │ │ │ │ ├── Value.java │ │ │ │ ├── ValueDeserializer.java │ │ │ │ └── ValueSerializer.java │ │ │ ├── dagger │ │ │ ├── CoreEarlyComponent.java │ │ │ ├── CoreLoadingComponent.java │ │ │ ├── CorePrimaryComponent.java │ │ │ ├── EarlyModule.java │ │ │ ├── EarlyScope.java │ │ │ ├── LoadingModule.java │ │ │ ├── LoadingScope.java │ │ │ ├── PrimaryModule.java │ │ │ ├── PrimaryScope.java │ │ │ ├── StartupPingerComponent.java │ │ │ ├── StartupPingerModule.java │ │ │ ├── StartupPingerScope.java │ │ │ └── TaskScope.java │ │ │ ├── filter │ │ │ ├── CoreFilterModifier.java │ │ │ ├── FilterRegistry.java │ │ │ ├── MultiArgumentsFilterBase.java │ │ │ ├── NoArgumentFilterBase.java │ │ │ ├── OneArgumentFilterEncoding.java │ │ │ └── TwoArgumentFilterEncoding.java │ │ │ ├── generator │ │ │ ├── CoreGeneratorManager.java │ │ │ ├── CoreGeneratorModule.java │ │ │ ├── GeneratorScope.java │ │ │ ├── Module.java │ │ │ ├── RandomMetadataGenerator.java │ │ │ ├── random │ │ │ │ └── RandomEventScope.java │ │ │ └── sine │ │ │ │ ├── SineGenerator.java │ │ │ │ ├── SineMetricGeneratorModule.java │ │ │ │ └── SineScope.java │ │ │ ├── grammar │ │ │ ├── CoreQueryParser.java │ │ │ ├── FromDSL.kt │ │ │ ├── KeywordValue.kt │ │ │ ├── Queries.kt │ │ │ ├── QueryListener.java │ │ │ └── Statements.kt │ │ │ ├── http │ │ │ ├── CoreHttpContextFactory.java │ │ │ ├── CorsResponseFilter.kt │ │ │ ├── DataResponse.kt │ │ │ ├── HeroicResource.java │ │ │ ├── HttpResourcesComponent.java │ │ │ ├── HttpServer.java │ │ │ ├── HttpServerComponent.java │ │ │ ├── HttpServerModule.java │ │ │ ├── HttpServerScope.java │ │ │ ├── Module.java │ │ │ ├── cluster │ │ │ │ ├── ClusterNodeStatus.kt │ │ │ │ ├── ClusterResource.java │ │ │ │ └── ClusterStatus.kt │ │ │ ├── metadata │ │ │ │ ├── Grouped.kt │ │ │ │ ├── MetadataCount.kt │ │ │ │ ├── MetadataKeySuggest.kt │ │ │ │ ├── MetadataQueryBody.kt │ │ │ │ ├── MetadataResource.java │ │ │ │ ├── MetadataResourceCache.java │ │ │ │ ├── MetadataTagKeySuggest.kt │ │ │ │ ├── MetadataTagSuggest.kt │ │ │ │ ├── MetadataTagValueSuggest.kt │ │ │ │ ├── MetadataTagValuesSuggest.kt │ │ │ │ └── RequestCriteria.kt │ │ │ ├── parser │ │ │ │ └── ParserResource.java │ │ │ ├── query │ │ │ │ ├── QueryBatch.kt │ │ │ │ ├── QueryBatchResponse.kt │ │ │ │ └── QueryResource.java │ │ │ ├── render │ │ │ │ ├── RenderResource.java │ │ │ │ └── RenderUtils.java │ │ │ ├── status │ │ │ │ ├── StatusResource.java │ │ │ │ └── StatusResponse.kt │ │ │ ├── tracing │ │ │ │ ├── OpenCensusApplicationEventListener.java │ │ │ │ ├── OpenCensusFeature.java │ │ │ │ ├── OpenCensusUtils.java │ │ │ │ └── TextFormatGetter.java │ │ │ ├── utils │ │ │ │ └── UtilsResource.java │ │ │ └── write │ │ │ │ ├── WriteMetricRequest.kt │ │ │ │ └── WriteResource.java │ │ │ ├── ingestion │ │ │ ├── CoreIngestionGroup.java │ │ │ ├── IngestionManagerImpl.java │ │ │ ├── IngestionModule.java │ │ │ └── IngestionScope.java │ │ │ ├── jetty │ │ │ ├── Http2CJettyConnectionFactory.java │ │ │ ├── Http2JettyConnectionFactory.java │ │ │ ├── HttpJettyConnectionFactory.java │ │ │ ├── JettyConnectionFactory.java │ │ │ ├── JettyHttpConfiguration.java │ │ │ ├── JettyJSONErrorHandler.java │ │ │ ├── JettyServerConnector.java │ │ │ ├── Module.java │ │ │ └── TLSJettyConnectionFactory.java │ │ │ ├── lifecycle │ │ │ ├── CoreLifeCycleManager.java │ │ │ └── CoreLifeCycleRegistry.java │ │ │ ├── metadata │ │ │ ├── CoreMetadataComponent.java │ │ │ ├── LocalMetadataManager.java │ │ │ ├── MetadataBackendGroup.java │ │ │ ├── MetadataManagerModule.java │ │ │ └── MetadataScope.java │ │ │ ├── metric │ │ │ ├── CoreMetricComponent.java │ │ │ ├── LocalMetricManager.java │ │ │ ├── MetricGroupSerialization.java │ │ │ ├── MetricManagerModule.java │ │ │ ├── MetricScope.java │ │ │ ├── MetricTypeSerialization.java │ │ │ ├── PointSerialization.java │ │ │ └── SpreadSerialization.java │ │ │ ├── querylogging │ │ │ ├── Module.java │ │ │ ├── Slf4jQueryLogger.java │ │ │ ├── Slf4jQueryLoggerFactory.java │ │ │ ├── Slf4jQueryLoggingComponent.java │ │ │ ├── Slf4jQueryLoggingModule.java │ │ │ ├── format │ │ │ │ ├── LogFormat.kt │ │ │ │ └── MessageFormat.kt │ │ │ └── noop │ │ │ │ ├── NoopQueryLogger.java │ │ │ │ ├── NoopQueryLoggerFactory.java │ │ │ │ ├── NoopQueryLoggingComponent.java │ │ │ │ └── NoopQueryLoggingModule.java │ │ │ ├── requestcondition │ │ │ ├── All.java │ │ │ ├── Any.java │ │ │ ├── ClientId.java │ │ │ ├── Module.java │ │ │ ├── Noop.java │ │ │ └── UserAgent.java │ │ │ ├── scheduler │ │ │ └── DefaultScheduler.java │ │ │ ├── servlet │ │ │ ├── MandatoryClientIdFilter.java │ │ │ ├── ShutdownFilter.java │ │ │ └── SimpleFilter.java │ │ │ ├── shell │ │ │ ├── AbstractShellTaskParams.java │ │ │ ├── ShellProtocol.java │ │ │ ├── ShellServer.java │ │ │ ├── ShellServerClientThread.java │ │ │ ├── ShellServerComponent.java │ │ │ ├── ShellServerModule.java │ │ │ ├── ShellServerScope.java │ │ │ ├── ShellServerState.kt │ │ │ ├── Tasks.java │ │ │ └── task │ │ │ │ ├── AnalyticsDumpFetchSeries.java │ │ │ │ ├── AnalyticsHits.kt │ │ │ │ ├── AnalyticsReportFetchSeries.java │ │ │ │ ├── AnalyticsSeries.kt │ │ │ │ ├── BackendKeyArgument.kt │ │ │ │ ├── Configure.java │ │ │ │ ├── CountData.java │ │ │ │ ├── DeleteKeys.java │ │ │ │ ├── DeserializeKey.java │ │ │ │ ├── Fetch.java │ │ │ │ ├── IngestionFilter.java │ │ │ │ ├── Keys.java │ │ │ │ ├── ListBackends.java │ │ │ │ ├── LoadGenerated.java │ │ │ │ ├── MetadataCount.java │ │ │ │ ├── MetadataDelete.java │ │ │ │ ├── MetadataEntries.java │ │ │ │ ├── MetadataFetch.java │ │ │ │ ├── MetadataFindSeries.java │ │ │ │ ├── MetadataFindSeriesIds.java │ │ │ │ ├── MetadataLoad.java │ │ │ │ ├── MetadataMigrate.java │ │ │ │ ├── MetadataTags.java │ │ │ │ ├── MetadataWrite.java │ │ │ │ ├── ParseQuery.java │ │ │ │ ├── Pause.java │ │ │ │ ├── Query.java │ │ │ │ ├── ReadWriteTest.java │ │ │ │ ├── Refresh.java │ │ │ │ ├── Resume.java │ │ │ │ ├── SerializeKey.java │ │ │ │ ├── SerializeKeyBackendKeyArgument.kt │ │ │ │ ├── Statistics.java │ │ │ │ ├── SuggestKey.java │ │ │ │ ├── SuggestPerformance.java │ │ │ │ ├── SuggestPerformanceData.kt │ │ │ │ ├── SuggestTag.java │ │ │ │ ├── SuggestTagKeyCount.java │ │ │ │ ├── SuggestTagValue.java │ │ │ │ ├── SuggestTagValues.java │ │ │ │ ├── TestPrint.java │ │ │ │ ├── TestReadFile.java │ │ │ │ ├── Write.java │ │ │ │ ├── WritePerformance.java │ │ │ │ ├── WritePerformanceTimes.kt │ │ │ │ ├── datamigrate │ │ │ │ ├── DataMigrate.java │ │ │ │ ├── KeyObserver.kt │ │ │ │ └── RowObserver.kt │ │ │ │ └── parameters │ │ │ │ ├── DataMigrateParameters.kt │ │ │ │ ├── KeysParameters.kt │ │ │ │ ├── KeyspaceBase.kt │ │ │ │ ├── MetadataCountParameters.kt │ │ │ │ ├── MetadataDeleteParameters.kt │ │ │ │ ├── MetadataEntriesParameters.kt │ │ │ │ ├── MetadataFetchParameters.kt │ │ │ │ ├── MetadataFindSeriesIdParameters.kt │ │ │ │ ├── MetadataFindSeriesParameters.kt │ │ │ │ ├── MetadataLoadParameters.kt │ │ │ │ ├── MetadataMigrateParameters.kt │ │ │ │ ├── MetadataTagsParameters.kt │ │ │ │ ├── QueryParamsBase.kt │ │ │ │ ├── SuggestKeyParameters.kt │ │ │ │ ├── SuggestTagKeyCountParameters.kt │ │ │ │ ├── SuggestTagParameters.kt │ │ │ │ ├── SuggestTagValueParameters.kt │ │ │ │ ├── SuggestTagValuesParameters.kt │ │ │ │ └── TaskQueryParameters.kt │ │ │ ├── suggest │ │ │ ├── CoreSuggestComponent.java │ │ │ ├── LocalSuggestManager.java │ │ │ ├── SuggestBackendGroup.java │ │ │ ├── SuggestManagerModule.java │ │ │ └── SuggestScope.java │ │ │ ├── tracing │ │ │ └── EnvironmentMetadata.java │ │ │ └── ws │ │ │ ├── ErrorExceptionMapper.java │ │ │ ├── ErrorMessage.java │ │ │ ├── InternalErrorMessage.java │ │ │ ├── JacksonMessageBodyReader.java │ │ │ ├── JacksonMessageBodyWriter.java │ │ │ ├── JsonErrorMessage.java │ │ │ ├── JsonMappingExceptionMapper.java │ │ │ ├── JsonParseErrorMessage.java │ │ │ ├── JsonParseExceptionMapper.java │ │ │ ├── MandatoryClientIdErrorMessage.java │ │ │ ├── Module.java │ │ │ ├── ParseErrorMessage.java │ │ │ ├── ParseExceptionMapper.java │ │ │ ├── RestfulComponent.java │ │ │ ├── ThrowableExceptionMapper.java │ │ │ ├── ValidationBodyErrorMapper.java │ │ │ └── WebApplicationExceptionMapper.java │ └── proto │ │ └── spotify_100.proto │ └── test │ ├── java │ └── com │ │ └── spotify │ │ └── heroic │ │ ├── CoreQueryManagerTest.java │ │ ├── HeroicConfigTest.java │ │ ├── QueryTest.java │ │ ├── aggregation │ │ ├── CoreAggregationFactoryTest.java │ │ ├── CoreAggregationRegistryTest.java │ │ └── SamplingQueryDeserializationTest.java │ │ ├── cache │ │ └── memcached │ │ │ └── CachedResultTest.java │ │ ├── cluster │ │ └── CoreClusterManagerTest.java │ │ ├── common │ │ └── DurationTest.java │ │ ├── consumer │ │ └── schemas │ │ │ ├── Spotify100ProtoTest.java │ │ │ └── Spotify100Test.java │ │ ├── filter │ │ └── FilterSerializerTest.java │ │ ├── grammar │ │ └── QueryParserTest.java │ │ ├── http │ │ └── tracing │ │ │ └── TextFormatGetterTest.java │ │ ├── ingestion │ │ └── CoreIngestionGroupTest.java │ │ ├── metric │ │ ├── BasicSerializationTest.java │ │ ├── LocalMetricManagerTest.java │ │ └── MetricQueryBuilderTest.java │ │ ├── querylogging │ │ └── Slf4jQueryLoggerTest.java │ │ ├── requestcondition │ │ └── RequestConditionTest.java │ │ └── servlet │ │ └── MandatoryClientIdFilterTest.java │ └── resources │ ├── com │ └── spotify │ │ └── heroic │ │ ├── Config.Empty.yml │ │ ├── Query.AggregationCompat.1.json │ │ ├── Query.AggregationCompat.2.json │ │ ├── Query.AggregationCompat.3.json │ │ ├── aggregation │ │ ├── SamplingQuery.1.json │ │ ├── SamplingQuery.2.json │ │ ├── SamplingQuery.3.json │ │ └── SamplingQuery.4.json │ │ ├── consumer │ │ └── schemas │ │ │ └── spotify-100-tests.txt │ │ └── metric │ │ ├── Event.json │ │ ├── FullQuery.json │ │ ├── MetricCollection.Event.json │ │ ├── MetricCollection.Payload.json │ │ ├── MetricCollection.json │ │ ├── Point.json │ │ ├── QueryMetricsResponse.json │ │ └── ResultGroup.json │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── heroic-dist ├── build.gradle └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ ├── HeroicInteractiveShell.java │ │ │ ├── HeroicLogging.java │ │ │ ├── HeroicModules.java │ │ │ ├── HeroicService.java │ │ │ ├── HeroicShell.java │ │ │ ├── Parameters.kt │ │ │ ├── profile │ │ │ ├── BigtableAnalyticsProfile.java │ │ │ ├── BigtableProfile.java │ │ │ ├── CassandraProfile.java │ │ │ ├── ClusterProfile.java │ │ │ ├── CollectdConsumerProfile.java │ │ │ ├── ElasticsearchMetadataProfile.java │ │ │ ├── ElasticsearchSuggestProfile.java │ │ │ ├── HeroicProfileBase.java │ │ │ ├── KafkaConsumerProfile.java │ │ │ ├── MemoryCacheProfile.java │ │ │ ├── MemoryMetadataProfile.java │ │ │ ├── MemoryProfile.java │ │ │ ├── PubSubConsumerProfile.java │ │ │ ├── QueryLoggingProfile.java │ │ │ └── WebProfile.java │ │ │ ├── reflection │ │ │ ├── ResourceException.java │ │ │ ├── ResourceFileLoader.java │ │ │ ├── ResourceInstance.java │ │ │ ├── ResourceLineContext.java │ │ │ └── ResourcePathContext.java │ │ │ └── shell │ │ │ ├── DirectShellIO.kt │ │ │ ├── QuoteParser.java │ │ │ ├── QuoteParserException.java │ │ │ ├── RemoteCoreInterface.java │ │ │ └── ShellParameters.kt │ └── resources │ │ ├── com.spotify.heroic │ │ ├── commit │ │ └── version │ │ └── log4j2.xml │ └── test │ ├── java │ └── com │ │ └── spotify │ │ └── heroic │ │ ├── AbstractClusterQueryIT.java │ │ ├── AbstractConsumerIT.java │ │ ├── AbstractKafkaConsumerIT.java │ │ ├── AbstractLocalClusterIT.java │ │ ├── AbstractSingleNodeIT.java │ │ ├── DataVersion1.kt │ │ ├── DataVersion2.kt │ │ ├── GrpcClusterQueryIT.java │ │ ├── HeroicConfigurationTest.java │ │ ├── HeroicConfigurationTestUtils.java │ │ ├── HeroicDistributionGenerator.java │ │ ├── JvmClusterQueryIT.java │ │ ├── KafkaNonTransactionalConsumerIT.java │ │ ├── KafkaTransactionalConsumerIT.java │ │ ├── LoggingMetricModule.kt │ │ ├── PubSubConsumerIT.java │ │ ├── RandomData.kt │ │ ├── TMetric.kt │ │ ├── TaskIT.java │ │ ├── analytics │ │ └── bigtable │ │ │ └── HeroicMetricsConfigurationTest.java │ │ ├── cluster │ │ └── CoreClusterManagerIT.java │ │ ├── consumer │ │ ├── kafka │ │ │ ├── FakeKafkaConnection.kt │ │ │ └── FakeKafkaStream.kt │ │ └── pubsub │ │ │ └── EmulatorHelper.java │ │ ├── instrumentation │ │ ├── OperationsLog.java │ │ └── OperationsLogImpl.kt │ │ ├── shell │ │ └── QuoteParserTest.java │ │ └── test │ │ ├── Data.java │ │ ├── DistributionPoints.java │ │ ├── Matchers.java │ │ └── Points.java │ └── resources │ ├── heroic-all.yml │ ├── heroic-conditional-features.yml │ ├── heroic-datastax.yml │ ├── heroic-disabled-tracking.yml │ ├── heroic-kafka.yml │ ├── heroic-metrics-limits.yml │ ├── heroic-null-shell-host.yml │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── heroic-elasticsearch-utils ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── elasticsearch │ │ ├── AbstractElasticsearchBackend.java │ │ ├── AbstractElasticsearchMetadataBackend.java │ │ ├── BackendType.kt │ │ ├── ClientWrapper.kt │ │ ├── Connection.kt │ │ ├── ConnectionModule.java │ │ ├── DefaultRateLimitedCache.java │ │ ├── DisabledRateLimitedCache.java │ │ ├── DistributedRateLimitedCache.java │ │ ├── MemcachedConnection.java │ │ ├── RateLimitExceededException.java │ │ ├── RateLimitedCache.java │ │ ├── ResourceLoader.java │ │ ├── RestClientWrapper.kt │ │ ├── RestConnection.kt │ │ ├── SearchTransformResult.kt │ │ ├── TransportClientWrapper.java │ │ ├── TransportConnection.java │ │ └── index │ │ ├── IndexMapping.kt │ │ ├── NoIndexSelectedException.java │ │ ├── RotatingIndexMapping.kt │ │ └── SingleIndexMapping.kt │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ └── elasticsearch │ ├── DefaultRateLimitedCacheTest.java │ ├── DisabledRateLimitedCacheTest.java │ ├── DistributedRateLimitedCacheTest.java │ ├── MemcachedConnectionTest.java │ ├── SearchTransformStreamTest.java │ ├── SearchTransformTest.java │ └── index │ └── RotatingIndexMappingTest.java ├── heroic-test ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── test │ │ ├── AbstractMetadataBackendIT.java │ │ ├── AbstractMetadataBackendIndexResourceIT.java │ │ ├── AbstractMetricBackendIT.java │ │ ├── AbstractSuggestBackendIT.java │ │ ├── ElasticSearchTestContainer.java │ │ ├── FakeModuleLoader.java │ │ ├── Points.java │ │ └── TimestampPrepender.java │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ ├── metadata │ └── elasticsearch │ │ ├── AbstractMetadataBackendIndexResourceKVIT.java │ │ ├── AbstractMetadataBackendKVIT.java │ │ ├── MetadataBackendIndexResourceKVRestIT.java │ │ ├── MetadataBackendIndexResourceKVTransportIT.java │ │ ├── MetadataBackendKVRestIT.java │ │ └── MetadataBackendKVTransportIT.java │ └── suggest │ └── elasticsearch │ ├── AbstractSuggestBackendKVIT.java │ ├── SuggestBackendKVRestIT.java │ └── SuggestBackendKVTransportIT.java ├── idea ├── README.md └── code-style.xml ├── logo.42.png ├── metadata ├── elasticsearch │ ├── build.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ └── metadata │ │ │ └── elasticsearch │ │ │ ├── ElasticsearchMetadataModule.java │ │ │ ├── ElasticsearchScope.java │ │ │ ├── MetadataBackendKV.java │ │ │ └── Module.java │ │ └── resources │ │ └── com.spotify.heroic.metadata.elasticsearch │ │ └── kv │ │ └── metadata.json └── memory │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── metadata │ │ └── memory │ │ ├── MemoryBackend.java │ │ ├── MemoryMetadataModule.java │ │ ├── MemoryScope.java │ │ └── Module.java │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ └── metadata │ └── memory │ └── MemoryBackendIT.java ├── metric ├── bigtable │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ ├── analytics │ │ │ └── bigtable │ │ │ │ ├── BigtableAnalyticsComponent.java │ │ │ │ ├── BigtableAnalyticsMetricBackend.java │ │ │ │ ├── BigtableAnalyticsModule.java │ │ │ │ ├── BigtableMetricAnalytics.java │ │ │ │ ├── BigtableScope.java │ │ │ │ ├── Module.java │ │ │ │ ├── SeriesKey.kt │ │ │ │ └── SeriesKeyEncoding.java │ │ │ └── metric │ │ │ └── bigtable │ │ │ ├── BigtableBackend.java │ │ │ ├── BigtableByteBufferSerialReader.java │ │ │ ├── BigtableConnection.java │ │ │ ├── BigtableConnectionBuilder.java │ │ │ ├── BigtableMetricModule.java │ │ │ ├── BigtableScope.java │ │ │ ├── CredentialsBuilder.java │ │ │ ├── CustomStringSerializer.java │ │ │ ├── MetricsRowKeySerializer.java │ │ │ ├── Module.java │ │ │ ├── RowKey.kt │ │ │ ├── RowKeyMinimal.java │ │ │ ├── RowKeySerializer.java │ │ │ ├── api │ │ │ ├── BigtableConstants.java │ │ │ ├── BigtableDataClient.java │ │ │ ├── BigtableDataClientImpl.java │ │ │ ├── BigtableMutator.java │ │ │ ├── BigtableMutatorImpl.java │ │ │ ├── BigtableTableAdminClient.java │ │ │ ├── BigtableTableTableAdminClientImpl.java │ │ │ ├── ColumnFamily.kt │ │ │ ├── Family.java │ │ │ ├── LatestCellValueColumn.kt │ │ │ ├── Mutations.java │ │ │ ├── ReadModifyWriteRules.java │ │ │ ├── ReadRowsRequest.kt │ │ │ ├── Row.kt │ │ │ ├── RowFilter.java │ │ │ ├── RowRange.kt │ │ │ └── Table.kt │ │ │ └── credentials │ │ │ ├── ComputeEngineCredentialsBuilder.java │ │ │ ├── DefaultCredentialsBuilder.java │ │ │ ├── JsonCredentialsBuilder.java │ │ │ └── ServiceAccountCredentialsBuilder.java │ │ └── test │ │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ ├── analytics │ │ └── bigtable │ │ │ └── SeriesKeyFilterEncodingTest.java │ │ └── metric │ │ └── bigtable │ │ ├── BigtableBackendIT.java │ │ ├── BigtableBackendTest.java │ │ ├── MetricsRowKeySerializerTest.java │ │ ├── api │ │ └── RowFilterTest.java │ │ └── credentials │ │ └── DefaultCredentialsBuilderTest.java ├── datastax │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── spotify │ │ │ │ └── heroic │ │ │ │ └── metric │ │ │ │ └── datastax │ │ │ │ ├── AggressiveRetryPolicy.java │ │ │ │ ├── Async.java │ │ │ │ ├── Connection.java │ │ │ │ ├── DatastaxAuthentication.java │ │ │ │ ├── DatastaxBackend.java │ │ │ │ ├── DatastaxMetricModule.java │ │ │ │ ├── DatastaxPoolingOptions.java │ │ │ │ ├── DatastaxScope.java │ │ │ │ ├── Event.kt │ │ │ │ ├── ManagedSetupConnection.java │ │ │ │ ├── MetricsRowKey.java │ │ │ │ ├── Module.java │ │ │ │ ├── RowFetchResult.kt │ │ │ │ ├── TypeSerializer.java │ │ │ │ └── schema │ │ │ │ ├── AbstractCassandraSchema.java │ │ │ │ ├── AbstractSchemaInstance.java │ │ │ │ ├── BackendKeyUtils.java │ │ │ │ ├── Schema.java │ │ │ │ ├── SchemaBoundStatement.kt │ │ │ │ ├── SchemaComponent.java │ │ │ │ ├── SchemaInstance.java │ │ │ │ ├── SchemaModule.java │ │ │ │ ├── SchemaScope.java │ │ │ │ ├── SelectBuilder.java │ │ │ │ └── ng │ │ │ │ ├── MetricsRowKeySerializer.java │ │ │ │ ├── NextGenSchema.java │ │ │ │ ├── NextGenSchemaInstance.java │ │ │ │ └── NextGenSchemaModule.java │ │ └── resources │ │ │ ├── com.spotify.heroic.metric.datastax.schema.legacy │ │ │ ├── keyspace.cql │ │ │ └── tables.cql │ │ │ └── com.spotify.heroic.metric.datastax.schema.ng │ │ │ ├── keyspace.cql │ │ │ └── tables.cql │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ └── metric │ │ │ └── datastax │ │ │ ├── AbstractDatastaxBackendIT.java │ │ │ ├── DatastaxAuthenticationTest.java │ │ │ ├── NextGenDatastaxBackendIT.java │ │ │ └── schema │ │ │ └── BackendKeyUtilsTest.java │ │ └── resources │ │ └── mockito-extensions │ │ └── org.mockito.plugins.MockMaker └── memory │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── metric │ │ └── memory │ │ ├── Data.kt │ │ ├── MemoryBackend.java │ │ ├── MemoryMetricModule.kt │ │ ├── MemoryScope.java │ │ └── Module.java │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ └── metric │ └── memory │ └── MemoryBackendIT.java ├── rfcs.md ├── rpc ├── grpc │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── rpc │ │ └── grpc │ │ ├── GroupedQuery.kt │ │ ├── GrpcDescriptor.java │ │ ├── GrpcEndpointHandle.java │ │ ├── GrpcOpenCensusInterceptor.java │ │ ├── GrpcRpcClient.java │ │ ├── GrpcRpcContainer.java │ │ ├── GrpcRpcEmptyBody.java │ │ ├── GrpcRpcEndpointHandleBase.java │ │ ├── GrpcRpcEndpointSpec.java │ │ ├── GrpcRpcProtocol.java │ │ ├── GrpcRpcProtocolModule.kt │ │ ├── GrpcRpcProtocolServer.java │ │ ├── GrpcRpcScope.java │ │ └── Module.java └── jvm │ ├── README.md │ ├── build.gradle │ └── src │ └── main │ └── java │ └── com │ └── spotify │ └── heroic │ └── rpc │ └── jvm │ ├── JvmRpcContext.java │ ├── JvmRpcProtocol.java │ ├── JvmRpcProtocolModule.kt │ ├── JvmRpcProtocolServer.java │ ├── JvmRpcScope.java │ └── Module.java ├── run-heroic.sh ├── settings.gradle ├── src └── main │ └── resources │ └── .gitkeep ├── statistics └── semantic │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── statistics │ │ └── semantic │ │ ├── Module.java │ │ ├── SemanticAnalyticsReporter.java │ │ ├── SemanticConsumerReporter.java │ │ ├── SemanticFutureReporter.java │ │ ├── SemanticHeroicReporter.java │ │ ├── SemanticHeroicTimer.java │ │ ├── SemanticHeroicTimerGauge.java │ │ ├── SemanticIngestionManagerReporter.java │ │ ├── SemanticMemcachedReporter.java │ │ ├── SemanticMetadataBackendReporter.java │ │ ├── SemanticMetricBackendReporter.java │ │ ├── SemanticQueryReporter.java │ │ ├── SemanticRatioGauge.java │ │ ├── SemanticStatisticsComponent.java │ │ ├── SemanticStatisticsModule.java │ │ ├── SemanticStatisticsScope.java │ │ ├── SemanticSuggestBackendReporter.java │ │ └── Units.java │ └── test │ ├── java │ └── com │ │ └── spotify │ │ └── heroic │ │ └── statistics │ │ └── semantic │ │ └── SemanticMetadataBackendReporterTest.java │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── suggest ├── elasticsearch │ ├── build.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── spotify │ │ │ └── heroic │ │ │ └── suggest │ │ │ └── elasticsearch │ │ │ ├── ElasticsearchScope.java │ │ │ ├── ElasticsearchSuggestModule.java │ │ │ ├── Module.java │ │ │ └── SuggestBackendKV.java │ │ └── resources │ │ └── com.spotify.heroic.suggest.elasticsearch │ │ └── kv │ │ ├── series.json │ │ ├── settings.json │ │ └── tag.json └── memory │ ├── build.gradle │ └── src │ ├── main │ └── java │ │ └── com │ │ └── spotify │ │ └── heroic │ │ └── suggest │ │ └── memory │ │ ├── KeyDocument.kt │ │ ├── MemoryBackend.java │ │ ├── MemoryScope.java │ │ ├── MemorySuggestModule.java │ │ ├── Module.java │ │ ├── TagDocument.kt │ │ └── TagId.kt │ └── test │ └── java │ └── com │ └── spotify │ └── heroic │ └── suggest │ └── memory │ ├── MemoryBackendIT.java │ └── MemoryBackendTest.java ├── suppressions.xml ├── system-tests ├── docker-compose.yml ├── heroic.yaml ├── requirements.txt └── test_heroic.py ├── tools ├── README.md ├── add_license.sh ├── find-old-keys ├── generate-assets ├── heroic-shell ├── java.header ├── license_matcher.py ├── querylog ├── querylog_mappings.yaml ├── rasterize ├── requirements.txt └── visualize-suggest-performance └── usage-tracking ├── disabled ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── spotify │ └── heroic │ └── usagetracking │ └── disabled │ ├── DisabledScope.java │ ├── DisabledUsageTracking.kt │ ├── DisabledUsageTrackingComponent.kt │ ├── DisabledUsageTrackingModule.kt │ └── Module.kt └── google ├── build.gradle └── src └── main └── java └── com └── spotify └── heroic └── usagetracking └── google ├── Event.kt ├── GoogleAnalytics.kt ├── GoogleAnalyticsModule.kt ├── Module.kt └── UsageTrackingScope.java /.cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/.cache/.gitkeep -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | .gitignore 4 | .github/ 5 | .circleci/ 6 | 7 | /docs/ 8 | /.idea/ 9 | *.png 10 | *.md 11 | build/ 12 | out/ 13 | .gradle 14 | 15 | /system-tests/ 16 | venv/ 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | 5 | [*.java] 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [pom.xml] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dependency-reduced-pom.xml 2 | /.cache 3 | target 4 | *.log 5 | *~ 6 | 7 | *.pyc 8 | venv/ 9 | 10 | bin 11 | /*.json 12 | /*.png 13 | /*.log 14 | /*.gz 15 | *.iml 16 | *.p12 17 | .idea 18 | .idea-run-configurations/ 19 | /reports/ 20 | /assets/out/ 21 | .meghanada 22 | 23 | # doc build artifacts 24 | bower_components 25 | node_modules 26 | output 27 | /docs/_site/ 28 | 29 | # gradle 30 | build/ 31 | out/ 32 | gradle-app.setting 33 | .gradle 34 | 35 | # VS Code auto-created files 36 | .classpath 37 | .project 38 | .settings 39 | .java-version 40 | 41 | # direnv config file - https://direnv.net/ 42 | /.envrc 43 | logs 44 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6.3-jdk-11 as builder 2 | LABEL maintainer="prism " 3 | 4 | RUN apt-get update && apt-get install -y git 5 | COPY . . 6 | RUN _JAVA_OPTIONS=-Djdk.net.URLClassPath.disableClassPathURLCheck=true ./gradlew clean assemble 7 | 8 | 9 | # Final Image 10 | FROM openjdk:11 11 | LABEL maintainer="prism " 12 | 13 | EXPOSE 8080 14 | EXPOSE 9190 15 | 16 | COPY --from=builder heroic-dist/build/libs/heroic-dist-0.0.1-SNAPSHOT-shaded.jar /usr/share/heroic/heroic.jar 17 | COPY example/heroic-memory-example.yml /heroic.yml 18 | COPY run-heroic.sh /usr/bin/heroic.sh 19 | 20 | ENV JVM_DEFAULT_ARGS -Dcom.datastax.driver.FORCE_NIO=true 21 | ENTRYPOINT ["/usr/bin/heroic.sh"] 22 | CMD ["/heroic.yml"] 23 | -------------------------------------------------------------------------------- /aggregation/cardinality/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'com.addthis:stream-lib' 4 | implementation 'com.google.guava:guava' 5 | 6 | testImplementation project(':heroic-test') 7 | } 8 | 9 | group = 'com.spotify.heroic.aggregation' 10 | description = 'Heroic: Cardinality Aggregations' 11 | -------------------------------------------------------------------------------- /aggregation/cardinality/src/test/java/com/spotify/heroic/aggregation/cardinality/ExactCardinalityBucketTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.cardinality; 2 | 3 | public class ExactCardinalityBucketTest extends AbstractCardinalityBucketTest { 4 | @Override 5 | protected double allowedError() { 6 | return 0D; 7 | } 8 | 9 | @Override 10 | protected CardinalityBucket setupBucket(final long timestamp) { 11 | return new ExactCardinalityBucket(timestamp, true); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /aggregation/cardinality/src/test/java/com/spotify/heroic/aggregation/cardinality/HyperLogLogCardinalityBucketTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.cardinality; 2 | 3 | public class HyperLogLogCardinalityBucketTest extends AbstractCardinalityBucketTest { 4 | @Override 5 | protected double allowedError() { 6 | return 0.9D; 7 | } 8 | 9 | @Override 10 | protected CardinalityBucket setupBucket(final long timestamp) { 11 | return new HyperLogLogCardinalityBucket(42, true, 0.01D); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /aggregation/cardinality/src/test/java/com/spotify/heroic/aggregation/cardinality/SerializationTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.cardinality; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.spotify.heroic.aggregation.AggregationInstance; 7 | import com.spotify.heroic.test.FakeModuleLoader; 8 | import org.junit.Test; 9 | 10 | public class SerializationTest { 11 | private final FakeModuleLoader m = FakeModuleLoader.builder().module(Module.class).build(); 12 | private final ObjectMapper mapper = m.json(); 13 | 14 | @Test 15 | public void testCardinalityInstance() throws Exception { 16 | final String json = "{\"type\":\"cardinality\",\"size\":1,\"extent\":2,\"method\":{\"type\":\"exact\",\"includeKey\":false}}"; 17 | final CardinalityInstance aggregation = new CardinalityInstance(1, 2, 18 | new CardinalityMethod.ExactCardinalityMethod(false)); 19 | 20 | assertEquals(json, mapper.writeValueAsString(aggregation)); 21 | assertEquals(aggregation, mapper.readValue(json, AggregationInstance.class)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /aggregation/simple/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | testImplementation project(':heroic-test') 4 | implementation 'com.tdunning:t-digest' 5 | implementation 'com.google.protobuf:protobuf-java' 6 | } 7 | 8 | group = 'com.spotify.heroic.aggregation' 9 | description = 'Heroic: Simple Aggregations' 10 | -------------------------------------------------------------------------------- /aggregation/simple/src/main/java/com/spotify/heroic/aggregation/simple/PointsAboveInstance.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation.simple 23 | 24 | data class PointsAboveInstance(val threshold: Double) 25 | : MetricMappingAggregation(FilterPointsThresholdStrategy(FilterKThresholdType.ABOVE, threshold)) 26 | -------------------------------------------------------------------------------- /aggregation/simple/src/main/java/com/spotify/heroic/aggregation/simple/PointsBelowInstance.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation.simple 23 | 24 | data class PointsBelowInstance(val threshold: Double) 25 | : MetricMappingAggregation(FilterPointsThresholdStrategy(FilterKThresholdType.BELOW, threshold)) 26 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/CountBucketTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.spotify.heroic.metric.Metric; 5 | import org.junit.Test; 6 | 7 | import java.util.Map; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.mockito.Mockito.mock; 11 | 12 | public class CountBucketTest { 13 | @Test 14 | public void testCount() { 15 | final Map tags = ImmutableMap.of(); 16 | final CountBucket b = new CountBucket(0); 17 | final Metric m = mock(Metric.class); 18 | assertEquals(0L, b.count()); 19 | b.update(tags, m); 20 | assertEquals(1L, b.count()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/DistributionPointUtils.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.google.protobuf.ByteString; 4 | import com.spotify.heroic.metric.DistributionPoint; 5 | import com.spotify.heroic.metric.HeroicDistribution; 6 | import com.tdunning.math.stats.TDigest; 7 | import java.nio.ByteBuffer; 8 | import java.util.Arrays; 9 | 10 | public class DistributionPointUtils { 11 | 12 | /** 13 | * Record data and create a distribution data point. 14 | * @param data data to record 15 | * @param timestamp distribution point timestamp 16 | * @return DistributionPoint. 17 | */ 18 | public static DistributionPoint createDistributionPoint(final double [] data, long timestamp){ 19 | TDigest tDigest = TDigest.createDigest(100.0); 20 | Arrays.stream(data).forEach(tDigest::add); 21 | ByteBuffer byteBuffer = ByteBuffer.allocate(tDigest.smallByteSize()); 22 | tDigest.asSmallBytes(byteBuffer); 23 | ByteString byteString = ByteString.copyFrom(byteBuffer.array()); 24 | return DistributionPoint.create( HeroicDistribution.create(byteString), timestamp); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/FilterKAreaStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.spotify.heroic.metric.MetricCollection; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.runners.MockitoJUnitRunner; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | 14 | @RunWith(MockitoJUnitRunner.class) 15 | public class FilterKAreaStrategyTest { 16 | 17 | @Test 18 | public void testNoDatapoints() { 19 | List> metrics = Arrays.asList(new FilterableMetrics(null, 20 | () -> MetricCollection.points(Collections.EMPTY_LIST))); 21 | FilterKAreaStrategy filter = new FilterKAreaStrategy(FilterKAreaType.BOTTOM, 1); 22 | 23 | // We expect timeseries with 0 datapoints to be filtered away 24 | assertEquals(filter.filter(metrics).size(), 0); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/MaxBucketIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.spotify.heroic.aggregation.DoubleBucket; 5 | 6 | import java.util.Collection; 7 | import java.util.function.DoubleBinaryOperator; 8 | 9 | public class MaxBucketIntegrationTest extends ValueBucketIntegrationTest { 10 | public MaxBucketIntegrationTest() { 11 | super(Double.NEGATIVE_INFINITY, new DoubleBinaryOperator() { 12 | @Override 13 | public double applyAsDouble(double left, double right) { 14 | return Math.max(left, right); 15 | } 16 | }); 17 | } 18 | 19 | @Override 20 | public Collection buckets() { 21 | return ImmutableList.of(new MaxBucket(0L), new StripedMaxBucket(0L)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/MaxBucketTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.spotify.heroic.metric.Point; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class MaxBucketTest { 11 | private static final Map TAGS = new HashMap<>(); 12 | 13 | @Test 14 | public void testInitialValue() { 15 | final StripedMaxBucket b = new StripedMaxBucket(0); 16 | Assert.assertEquals(Double.NaN, b.value(), 0.0); 17 | } 18 | 19 | @Test 20 | public void testMinValues() { 21 | final StripedMaxBucket b = new StripedMaxBucket(0); 22 | b.updatePoint(TAGS, new Point(0, 20.0)); 23 | b.updatePoint(TAGS, new Point(0, 10.0)); 24 | Assert.assertEquals(20.0, b.value(), 0.0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/MinBucketIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.spotify.heroic.aggregation.DoubleBucket; 5 | 6 | import java.util.Collection; 7 | import java.util.function.DoubleBinaryOperator; 8 | 9 | public class MinBucketIntegrationTest extends ValueBucketIntegrationTest { 10 | public MinBucketIntegrationTest() { 11 | super(Double.POSITIVE_INFINITY, new DoubleBinaryOperator() { 12 | @Override 13 | public double applyAsDouble(double left, double right) { 14 | return Math.min(left, right); 15 | } 16 | }); 17 | } 18 | 19 | @Override 20 | public Collection buckets() { 21 | return ImmutableList.of(new MinBucket(0L), new StripedMinBucket(0L)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/MinBucketTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.spotify.heroic.metric.Point; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class MinBucketTest { 11 | private static final Map TAGS = new HashMap<>(); 12 | 13 | @Test 14 | public void testInitialValue() { 15 | final StripedMinBucket b = new StripedMinBucket(0); 16 | Assert.assertEquals(Double.NaN, b.value(), 0.0); 17 | } 18 | 19 | @Test 20 | public void testMinValues() { 21 | final StripedMinBucket b = new StripedMinBucket(0); 22 | b.updatePoint(TAGS, new Point(0, 10.0)); 23 | b.updatePoint(TAGS, new Point(0, 20.0)); 24 | Assert.assertEquals(10.0, b.value(), 0.0); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/OfSupplier.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.spotify.heroic.aggregation.Empty; 4 | import com.spotify.heroic.test.ValueSuppliers; 5 | 6 | import java.lang.reflect.Type; 7 | import java.util.Optional; 8 | 9 | public class OfSupplier implements ValueSuppliers.ValueSupplier { 10 | @Override 11 | public Optional supply( 12 | final Type type, final boolean secondary, final String name 13 | ) { 14 | if ("of".equals(name)) { 15 | return Optional.of(secondary ? Optional.of(Empty.INSTANCE) : Optional.empty()); 16 | } 17 | 18 | return Optional.empty(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/SamplingSupplier.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | import com.spotify.heroic.aggregation.SamplingQuery; 4 | import com.spotify.heroic.common.Duration; 5 | import com.spotify.heroic.test.ValueSuppliers; 6 | 7 | import java.lang.reflect.Type; 8 | import java.util.Optional; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | public class SamplingSupplier implements ValueSuppliers.ValueSupplier { 12 | final SamplingQuery s1 = new SamplingQuery("SECONDS", Duration.of(10, TimeUnit.MILLISECONDS), 13 | Duration.of(10, TimeUnit.MILLISECONDS)); 14 | 15 | final SamplingQuery s2 = new SamplingQuery("SECONDS", Duration.of(20, TimeUnit.MILLISECONDS), 16 | Duration.of(20, TimeUnit.MILLISECONDS)); 17 | 18 | @Override 19 | public Optional supply( 20 | final Type type, final boolean secondary, final String name 21 | ) { 22 | if ("sampling".equals(name)) { 23 | return Optional.of(Optional.of(secondary ? s1 : s2)); 24 | } 25 | 26 | return Optional.empty(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /aggregation/simple/src/test/java/com/spotify/heroic/aggregation/simple/TdigestBucketIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation.simple; 2 | 3 | 4 | import com.google.common.collect.ImmutableList; 5 | import com.spotify.heroic.aggregation.DoubleBucket; 6 | import com.spotify.heroic.aggregation.TDigestBucket; 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | public class TdigestBucketIntegrationTest extends ValueBucketIntegrationTest { 11 | 12 | 13 | public TdigestBucketIntegrationTest() { 14 | super(Double.NEGATIVE_INFINITY, null); 15 | } 16 | 17 | @Override 18 | public Collection buckets() { 19 | return List.of(); 20 | } 21 | 22 | @Override 23 | public Collection tDigestBuckets(){ 24 | return ImmutableList.of(new TdigestMergingBucket(0L)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # Assets 2 | 3 | This directory contains assets related to Heroic. 4 | 5 | It should include information about author and licensing for every Asset 6 | provided. 7 | 8 | ## Logo 9 | 10 | Copyright © 2016 Niklas Ek <ek.niklas@gmail.com> 11 | [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) 12 | 13 | * [Logo On Dark](logo_on_dark.svg) 14 | * [Logo On Light](logo_on_light.svg) 15 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: false 4 | patch: false 5 | -------------------------------------------------------------------------------- /consumer/collectd/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'io.netty:netty-transport' 4 | } 5 | 6 | group = 'com.spotify.heroic.consumer' 7 | description = 'Heroic: collectd Consumer' 8 | -------------------------------------------------------------------------------- /consumer/kafka/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation('org.apache.kafka:kafka_2.10:0.8.1.1') { 4 | exclude group: 'log4j', module: 'log4j' 5 | } 6 | } 7 | 8 | group = 'com.spotify.heroic.consumer' 9 | description = 'Heroic: Kafka Consumer' 10 | -------------------------------------------------------------------------------- /consumer/kafka/src/main/java/com/spotify/heroic/consumer/kafka/ConsumerThreadCoordinator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.consumer.kafka; 23 | 24 | public interface ConsumerThreadCoordinator { 25 | void prepareToCommitConsumerOffsets(); 26 | 27 | void commitConsumerOffsets(); 28 | } 29 | -------------------------------------------------------------------------------- /consumer/kafka/src/main/java/com/spotify/heroic/consumer/kafka/KafkaStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.consumer.kafka; 23 | 24 | public interface KafkaStream { 25 | Iterable messageIterable(); 26 | } 27 | -------------------------------------------------------------------------------- /consumer/pubsub/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation 'com.google.cloud:google-cloud-pubsub' 3 | implementation 'io.grpc:grpc-netty' 4 | implementation 'io.netty:netty-tcnative-boringssl-static' 5 | implementation project(':heroic-component') 6 | implementation 'com.fasterxml.jackson.core:jackson-annotations' 7 | implementation 'com.fasterxml.jackson.core:jackson-databind' 8 | } 9 | 10 | group = 'com.spotify.heroic.consumer' 11 | description = 'Heroic: PubSub Consumer' 12 | -------------------------------------------------------------------------------- /discovery/simple/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'dnsjava:dnsjava:2.1.7' 4 | } 5 | 6 | group = 'com.spotify.heroic.discovery' 7 | description = 'Heroic: Simple Discovery' 8 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "github-pages", group: :jekyll_plugins 4 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Heroic Documentation Development 2 | 3 | Documention is served up with GitHub pages at [https://spotify.github.io/heroic/](https://spotify.github.io/heroic/). GitHub pages uses [Jekyll](https://jekyllrb.com/docs/github-pages) to build and serve websites. Jekyll has a CLI tool that can mimic the GitHub pages setup locally. 4 | 5 | ## Installation 6 | 7 | Make sure you have ruby and bundle installed, then from the `docs` directory run `bundle install`. 8 | 9 | ## Serving 10 | 11 | Run: 12 | 13 | `bundle exec jekyll serve` 14 | 15 | This will compile the site and serve it locally. Any changes will be incrementally re-compiled while the serve command is running. The local documentation can be accessed at [http://127.0.0.1:4000/heroic/](http://127.0.0.1:4000/heroic/). 16 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | baseurl: /heroic 2 | collections_dir: content 3 | collections: 4 | docs: 5 | output: true 6 | tutorials: 7 | output: true 8 | endpoints: 9 | output: true 10 | permalink: /docs/api/:name 11 | api_types: 12 | output: true 13 | permalink: /docs/api/types/:name 14 | fault_tree: 15 | output: true 16 | permalink: /docs/fta/:name 17 | 18 | defaults: 19 | - scope: 20 | type: docs 21 | values: 22 | layout: sidebar 23 | - scope: 24 | type: tutorials 25 | values: 26 | layout: default 27 | - scope: 28 | type: endpoints 29 | values: 30 | layout: api-endpoint 31 | responses: 32 | - status: 200 33 | - scope: 34 | type: api_types 35 | values: 36 | layout: api-type 37 | - scope: 38 | type: fault_tree 39 | values: 40 | layout: fault-tree 41 | 42 | sass: 43 | sass_dir: assets/_sass 44 | style: compressed 45 | -------------------------------------------------------------------------------- /docs/_includes/api-field-type.html: -------------------------------------------------------------------------------- 1 | {% assign field = include.field %} 2 | 3 | {%- if field.type_name %} 4 | {%- assign type_page = site.api_types | where: 'type_name', field.type_name | first %} 5 | 6 | 7 | {%- if field.type_array %} 8 | [{{ field.type_name }}, ...] 9 | {%- else %} 10 | {{ field.type_name }} 11 | {%- endif %} 12 | 13 | 14 | 15 | {%- elsif field.type_json %} 16 | {{ field.type_json }} 17 | {%- else %} 18 | no type 19 | {%- endif %} 20 | -------------------------------------------------------------------------------- /docs/_includes/api-response.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Status: {{ include.status | default: '200' }}, 4 | Content-Type: {{ include.content_type | default: 'application/json' }} 5 | 6 |
7 | {{ include.response }} 8 |
9 |
10 | -------------------------------------------------------------------------------- /docs/_includes/federation-tail.html: -------------------------------------------------------------------------------- 1 |

2 | A client querying any heroic node in a federation will cause it to fan out to 3 | all known shards and merge the result. 4 |

5 | 6 | 7 | 8 |

9 | Federations tries to be as transparent as possible in the face of problems. 10 | Each request that fans out to a shard has the potential to fail, preventing that data to become unavailable. 11 |

12 | 13 |

14 | In the face of errors, successful shards will be returned as normal. 15 | The failing shards will be specifically reported as such, and it is left to 16 | the client to decide what to do next. 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/_includes/fields/post-query-metrics/filter.html: -------------------------------------------------------------------------------- 1 |

2 | A statement used to filter down the selected time series. 3 | Each individual filtering field (filter, key, tags, and hasTags) may be empty. 4 | At least one must be specified to make up a valid filter. 5 |

6 |

7 | See filtering for details on how to build a filter. 8 |

9 | -------------------------------------------------------------------------------- /docs/_includes/github-link.html: -------------------------------------------------------------------------------- 1 | {%- assign path = include.name | replace: '.', '/' %} 2 | 3 | com.spotify.heroic.{{ include.name }} 4 | 5 | -------------------------------------------------------------------------------- /docs/_includes/mkhash.inc: -------------------------------------------------------------------------------- 1 | {% assign hash = include %} 2 | -------------------------------------------------------------------------------- /docs/_includes/requests/metadata-query-body.json: -------------------------------------------------------------------------------- 1 | {"filter": ["and", ["key", "system"], ["=", "role", "database"]]} 2 | -------------------------------------------------------------------------------- /docs/_includes/requests/metadata-tagkey-count.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter": ["=", "role", "heroic"], 3 | "limit": 10 4 | } 5 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-metadata-key-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "foo", 3 | "filter": ["=", "role", "heroic"], 4 | "limit": 10 5 | } 6 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-metadata-series-count.json: -------------------------------------------------------------------------------- 1 | {"filter": ["and", ["key", "system"], ["=", "role", "database"]]} 2 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-metadata-tag-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "role", 3 | "value": "hero con", 4 | "filter": ["=", "site", "lon"], 5 | "limit": 10 6 | } 7 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-metadata-tag-value-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "role", 3 | "filter": ["=", "site", "lone"], 4 | "limit": 10 5 | } 6 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-query-batch.json: -------------------------------------------------------------------------------- 1 | { 2 | "queries": { 3 | "a": { 4 | "range": {"type": "relative", "unit": "HOURS", "value": 2}, 5 | "filter": ["and", ["key", "foo"], ["=", "foo", "bar"], ["+", "role"]], 6 | "aggregation": { 7 | "type": "group", 8 | "of": ["site"], 9 | "each": { 10 | "type": "sum" 11 | } 12 | } 13 | }, 14 | "b": { 15 | "range": {"type": "relative", "unit": "HOURS", "value": 2}, 16 | "filter": ["and", ["key", "baz"], ["=", "foo", "bar"], ["+", "role"]], 17 | "aggregation": { 18 | "type": "group", 19 | "of": ["site"], 20 | "each": { 21 | "type": "sum" 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-query-metrics.json: -------------------------------------------------------------------------------- 1 | { 2 | "range": {"type": "relative", "unit": "HOURS", "value": 2}, 3 | "filter": ["and", ["key", "foo"], ["=", "foo", "bar"], ["+", "role"]], 4 | "aggregation": { 5 | "type": "group", 6 | "of": ["site"], 7 | "each": { 8 | "type": "sum" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/_includes/requests/post-write.json: -------------------------------------------------------------------------------- 1 | { 2 | "series": {"key": "foo", "tags": {"site": "lon", "host": "www.example.com"}}, 3 | "data": {"type": "points", "data": [[1300000000000, 42.0], [1300001000000, 84.0]]} 4 | } 5 | -------------------------------------------------------------------------------- /docs/_includes/requests/put-metadata-series.json: -------------------------------------------------------------------------------- 1 | {"key": "foo", "tags": {"site": "sto"}} 2 | -------------------------------------------------------------------------------- /docs/_includes/responses/delete-metadata-series.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"key": "foo", "tags": {"site": "lon"}}, 3 | {"key": "foo", "tags": {"site": "sto"}} 4 | ] 5 | -------------------------------------------------------------------------------- /docs/_includes/responses/get-status-failed.html: -------------------------------------------------------------------------------- 1 |

2 | A 503 will still return a response body, however the root key .ok will have the value false. 3 |

4 | 5 |

6 | The below document shows that there is something wrong with the configured consumers, in this case it is because the number of consumingThreads is not the same as the totalThreads 7 |

8 | 9 |

10 |   {
11 |     "ok": false,
12 |     "consumers": {
13 |       "ok": false,
14 |       "available": 0,
15 |       "ready": 0,
16 |       "errors": 0,
17 |       "consumingThreads": 0,
18 |       "totalThreads": 1
19 |     },
20 |     ...
21 |   }
22 | 
23 | -------------------------------------------------------------------------------- /docs/_includes/responses/get-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "ok": true, 3 | "service": { 4 | "name": "The Heroic Time Series Database", 5 | "version": "master (git: aaccddee)", 6 | "id": "api" 7 | }, 8 | "consumers": { 9 | "ok": true, 10 | "available": 0, 11 | "ready": 0, 12 | "errors": 0, 13 | "consumingThreads": 1, 14 | "totalThreads": 1 15 | }, 16 | "backends": { 17 | "ok": true, 18 | "available": 0, 19 | "ready": 0 20 | }, 21 | "metadataBackends": { 22 | "ok": true, 23 | "available": 0, 24 | "ready": 0 25 | }, 26 | "cluster": { 27 | "ok": true, 28 | "onlineNodes": 1, 29 | "offlineNodes": 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-key-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "suggestions": [ 4 | {"score": 1.5, "key": "foobar"}, 5 | {"score": 0.9, "key": "folly"} 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": ["foo", "bar"], 3 | "sampleSize": 5 4 | } 5 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-series-count.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "limited": false, 4 | "count": 10001 5 | } 6 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-series.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"key": "foo", "tags": {"site": "lon"}}, 3 | {"key": "foo", "tags": {"site": "sto"}} 4 | ] 5 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-tag-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "suggestions": [ 4 | {"score": 1.5, "key": "role", "value": "heroic-consumer"} 5 | {"score": 0.9, "key": "role", "value": "heroic-api"}, 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-tag-value-suggest.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "values": [ 4 | "heroic-api", 5 | "heroic-consumer" 6 | ], 7 | "limited": false 8 | } 9 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-tagkey-count.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "suggestions": [ 4 | { 5 | "key": "cluster_name", 6 | "count": 3, 7 | "exactValues": [ 8 | "heroic-suggest-guc3", 9 | "heroic-suggest-gae2", 10 | "heroic-suggest-gew1" 11 | ] 12 | }, 13 | { 14 | "key": "component", 15 | "count": 12, 16 | "exactValues": [ 17 | "disk", 18 | "output-manager", 19 | "input-manager", 20 | "output-plugin" 21 | ] 22 | } 23 | ], 24 | "limited": false 25 | } 26 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-metadata-tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": { 3 | "role": ["database", "webfrontend"], 4 | "host": ["database.example.com", "webfrontend.example.com"] 5 | }, 6 | "sampleSize": 2 7 | } 8 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-query-batch.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": { 3 | "errors": [], 4 | "result": [ 5 | { 6 | "hash": "deadbeef", 7 | "tags": {"foo": "bar"}, 8 | "values": [[1300000000000, 42.0]] 9 | }, 10 | { 11 | "hash": "beefdead", 12 | "tags": {"foo": "baz"}, 13 | "values": [[1300000000000, 42.0]] 14 | } 15 | ], 16 | "range": { 17 | "end": 1469816790000, 18 | "start": 1469809590000 19 | }, 20 | "statistics": {} 21 | }, 22 | "b": { 23 | "errors": [], 24 | "result": [ 25 | { 26 | "hash": "deadbeef", 27 | "tags": {"foo": "bar"}, 28 | "values": [[1300000000000, 42.0]] 29 | }, 30 | { 31 | "hash": "beefdead", 32 | "tags": {"foo": "baz"}, 33 | "values": [[1300000000000, 42.0]] 34 | } 35 | ], 36 | "range": { 37 | "end": 1469816790000, 38 | "start": 1469809590000 39 | }, 40 | "statistics": {} 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-query-metrics.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [ 3 | { 4 | "type": "node", 5 | "nodeId": "abcd-efgh", 6 | "nodeUri": "http://example.com", 7 | "tags": {"site": "lon"}, 8 | "error": "Connection refused", 9 | "internal": true 10 | }, 11 | { 12 | "type": "series", 13 | "tags": {"site": "lon"}, 14 | "error": "Aggregation too heavy, too many rows from the database would have to be fetched to satisfy the request!", 15 | "internal": true 16 | } 17 | ], 18 | "result": [ 19 | { 20 | "hash": "deadbeef", 21 | "tags": {"foo": "bar"}, 22 | "values": [[1300000000000, 42.0]] 23 | }, 24 | { 25 | "hash": "beefdead", 26 | "tags": {"foo": "baz"}, 27 | "values": [[1300000000000, 42.0]] 28 | } 29 | ], 30 | "range": { 31 | "end": 1469816790000, 32 | "start": 1469809590000 33 | }, 34 | "statistics": {} 35 | } 36 | -------------------------------------------------------------------------------- /docs/_includes/responses/post-write.json: -------------------------------------------------------------------------------- 1 | { 2 | "errors": [], 3 | "times": [15603449,1383935,1389086,1393517,1408274] 4 | } 5 | -------------------------------------------------------------------------------- /docs/_includes/responses/put-metadata-series.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"key": "foo", "tags": {"site": "lon"}}, 3 | {"key": "foo", "tags": {"site": "sto"}} 4 | ] 5 | -------------------------------------------------------------------------------- /docs/_layouts/api-type-structure.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: sidebar 3 | --- 4 |
5 |

{{ page.type_name }}

6 | 7 |
8 | {{ content }} 9 |
10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | {%- for field in page.fields %} 20 | 21 | 22 | 29 | 30 | {%- endfor %} 31 |
StructurePurpose
{% include api-field-type.html field=field %} 23 | {%- if field.purpose %} 24 | {{ field.purpose }} 25 | {%- else %} 26 | {% include fields/{{ page.slug }}/{{ field.name }}.html %} 27 | {%- endif %} 28 |
32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /docs/_layouts/api-type.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: sidebar 3 | --- 4 | {% include api-type.html content=content %} 5 | -------------------------------------------------------------------------------- /docs/assets/_sass/_bootstrap-compass.scss: -------------------------------------------------------------------------------- 1 | @function twbs-font-path($path) { 2 | @return font-url($path, true); 3 | } 4 | 5 | @function twbs-image-path($path) { 6 | @return image-url($path, true); 7 | } 8 | 9 | $bootstrap-sass-asset-helper: true; 10 | -------------------------------------------------------------------------------- /docs/assets/_sass/_bootstrap-mincer.scss: -------------------------------------------------------------------------------- 1 | // Mincer asset helper functions 2 | // 3 | // This must be imported into a .css.ejs.scss file. 4 | // Then, <% %>-interpolations will be parsed as strings by Sass, and evaluated by EJS after Sass compilation. 5 | 6 | 7 | @function twbs-font-path($path) { 8 | // do something like following 9 | // from "path/to/font.ext#suffix" to "<%- asset_path(path/to/font.ext)) + #suffix %>" 10 | // from "path/to/font.ext?#suffix" to "<%- asset_path(path/to/font.ext)) + ?#suffix %>" 11 | // or from "path/to/font.ext" just "<%- asset_path(path/to/font.ext)) %>" 12 | @return "<%- asset_path("#{$path}".replace(/[#?].*$/, '')) + "#{$path}".replace(/(^[^#?]*)([#?]?.*$)/, '$2') %>"; 13 | } 14 | 15 | @function twbs-image-path($file) { 16 | @return "<%- asset_path("#{$file}") %>"; 17 | } 18 | 19 | $bootstrap-sass-asset-helper: true; 20 | -------------------------------------------------------------------------------- /docs/assets/_sass/_bootstrap-sprockets.scss: -------------------------------------------------------------------------------- 1 | @function twbs-font-path($path) { 2 | @return font-path($path); 3 | } 4 | 5 | @function twbs-image-path($path) { 6 | @return image-path($path); 7 | } 8 | 9 | $bootstrap-sass-asset-helper: true; 10 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal; 8 | margin-bottom: $line-height-computed; 9 | list-style: none; 10 | background-color: $breadcrumb-bg; 11 | border-radius: $border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | // [converter] Workaround for https://github.com/sass/libsass/issues/1115 18 | $nbsp: "\00a0"; 19 | content: "#{$breadcrumb-separator}#{$nbsp}"; // Unicode space added since inline-block means non-collapsing white-space 20 | padding: 0 5px; 21 | color: $breadcrumb-color; 22 | } 23 | } 24 | 25 | > .active { 26 | color: $breadcrumb-active-color; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: ($font-size-base * 1.5); 9 | font-weight: $close-font-weight; 10 | line-height: 1; 11 | color: $close-color; 12 | text-shadow: $close-text-shadow; 13 | @include opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: $close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | @include opacity(.5); 21 | } 22 | 23 | // [converter] extracted button& to button.close 24 | } 25 | 26 | // Additional properties for button version 27 | // iOS requires the button element instead of an anchor tag. 28 | // If you want the anchor version, it requires `href="#"`. 29 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile 30 | button.close { 31 | padding: 0; 32 | cursor: pointer; 33 | background: transparent; 34 | border: 0; 35 | -webkit-appearance: none; 36 | } 37 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_component-animations.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | @include transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | 21 | &.in { display: block; } 22 | // [converter] extracted tr&.in to tr.collapse.in 23 | // [converter] extracted tbody&.in to tbody.collapse.in 24 | } 25 | 26 | tr.collapse.in { display: table-row; } 27 | 28 | tbody.collapse.in { display: table-row-group; } 29 | 30 | .collapsing { 31 | position: relative; 32 | height: 0; 33 | overflow: hidden; 34 | @include transition-property(height, visibility); 35 | @include transition-duration(.35s); 36 | @include transition-timing-function(ease); 37 | } 38 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | 4 | // Utilities 5 | @import "mixins/hide-text"; 6 | @import "mixins/opacity"; 7 | @import "mixins/image"; 8 | @import "mixins/labels"; 9 | @import "mixins/reset-filter"; 10 | @import "mixins/resize"; 11 | @import "mixins/responsive-visibility"; 12 | @import "mixins/size"; 13 | @import "mixins/tab-focus"; 14 | @import "mixins/reset-text"; 15 | @import "mixins/text-emphasis"; 16 | @import "mixins/text-overflow"; 17 | @import "mixins/vendor-prefixes"; 18 | 19 | // Components 20 | @import "mixins/alerts"; 21 | @import "mixins/buttons"; 22 | @import "mixins/panels"; 23 | @import "mixins/pagination"; 24 | @import "mixins/list-group"; 25 | @import "mixins/nav-divider"; 26 | @import "mixins/forms"; 27 | @import "mixins/progress-bar"; 28 | @import "mixins/table-row"; 29 | 30 | // Skins 31 | @import "mixins/background-variant"; 32 | @import "mixins/border-radius"; 33 | @import "mixins/gradients"; 34 | 35 | // Layout 36 | @import "mixins/clearfix"; 37 | @import "mixins/center-block"; 38 | @import "mixins/nav-vertical-align"; 39 | @import "mixins/grid-framework"; 40 | @import "mixins/grid"; 41 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_pager.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: $line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | @include clearfix; 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: $pager-bg; 19 | border: 1px solid $pager-border; 20 | border-radius: $pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: $pager-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: $pager-disabled-color; 50 | background-color: $pager-bg; 51 | cursor: $cursor-disabled; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_responsive-embed.scss: -------------------------------------------------------------------------------- 1 | // Embeds responsive 2 | // 3 | // Credit: Nicolas Gallagher and SUIT CSS. 4 | 5 | .embed-responsive { 6 | position: relative; 7 | display: block; 8 | height: 0; 9 | padding: 0; 10 | overflow: hidden; 11 | 12 | .embed-responsive-item, 13 | iframe, 14 | embed, 15 | object, 16 | video { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | bottom: 0; 21 | height: 100%; 22 | width: 100%; 23 | border: 0; 24 | } 25 | } 26 | 27 | // Modifier class for 16:9 aspect ratio 28 | .embed-responsive-16by9 { 29 | padding-bottom: 56.25%; 30 | } 31 | 32 | // Modifier class for 4:3 aspect ratio 33 | .embed-responsive-4by3 { 34 | padding-bottom: 75%; 35 | } 36 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_thumbnails.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | display: block; 9 | padding: $thumbnail-padding; 10 | margin-bottom: $line-height-computed; 11 | line-height: $line-height-base; 12 | background-color: $thumbnail-bg; 13 | border: 1px solid $thumbnail-border; 14 | border-radius: $thumbnail-border-radius; 15 | @include transition(border .2s ease-in-out); 16 | 17 | > img, 18 | a > img { 19 | @include img-responsive; 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | // [converter] extracted a&:hover, a&:focus, a&.active to a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active 25 | 26 | // Image captions 27 | .caption { 28 | padding: $thumbnail-caption-padding; 29 | color: $thumbnail-caption-color; 30 | } 31 | } 32 | 33 | // Add a hover state for linked versions only 34 | a.thumbnail:hover, 35 | a.thumbnail:focus, 36 | a.thumbnail.active { 37 | border-color: $link-color; 38 | } 39 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_utilities.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | @include clearfix; 11 | } 12 | .center-block { 13 | @include center-block; 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | @include text-hide; 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | } 48 | 49 | 50 | // For Affix plugin 51 | // ------------------------- 52 | 53 | .affix { 54 | position: fixed; 55 | } 56 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/_wells.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: $well-bg; 12 | border: 1px solid $well-border; 13 | border-radius: $border-radius-base; 14 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: $border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: $border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_alerts.scss: -------------------------------------------------------------------------------- 1 | // Alerts 2 | 3 | @mixin alert-variant($background, $border, $text-color) { 4 | background-color: $background; 5 | border-color: $border; 6 | color: $text-color; 7 | 8 | hr { 9 | border-top-color: darken($border, 5%); 10 | } 11 | .alert-link { 12 | color: darken($text-color, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_background-variant.scss: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | 3 | // [converter] $parent hack 4 | @mixin bg-variant($parent, $color) { 5 | #{$parent} { 6 | background-color: $color; 7 | } 8 | a#{$parent}:hover, 9 | a#{$parent}:focus { 10 | background-color: darken($color, 10%); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_border-radius.scss: -------------------------------------------------------------------------------- 1 | // Single side border-radius 2 | 3 | @mixin border-top-radius($radius) { 4 | border-top-right-radius: $radius; 5 | border-top-left-radius: $radius; 6 | } 7 | @mixin border-right-radius($radius) { 8 | border-bottom-right-radius: $radius; 9 | border-top-right-radius: $radius; 10 | } 11 | @mixin border-bottom-radius($radius) { 12 | border-bottom-right-radius: $radius; 13 | border-bottom-left-radius: $radius; 14 | } 15 | @mixin border-left-radius($radius) { 16 | border-bottom-left-radius: $radius; 17 | border-top-left-radius: $radius; 18 | } 19 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_center-block.scss: -------------------------------------------------------------------------------- 1 | // Center-align a block level element 2 | 3 | @mixin center-block() { 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | // Clearfix 2 | // 3 | // For modern browsers 4 | // 1. The space content is one way to avoid an Opera bug when the 5 | // contenteditable attribute is included anywhere else in the document. 6 | // Otherwise it causes space to appear at the top and bottom of elements 7 | // that are clearfixed. 8 | // 2. The use of `table` rather than `block` is only necessary if using 9 | // `:before` to contain the top-margins of child elements. 10 | // 11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/ 12 | 13 | @mixin clearfix() { 14 | &:before, 15 | &:after { 16 | content: " "; // 1 17 | display: table; // 2 18 | } 19 | &:after { 20 | clear: both; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_hide-text.scss: -------------------------------------------------------------------------------- 1 | // CSS image replacement 2 | // 3 | // Heads up! v3 launched with only `.hide-text()`, but per our pattern for 4 | // mixins being reused as classes with the same name, this doesn't hold up. As 5 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. 6 | // 7 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 8 | 9 | // Deprecated as of v3.0.1 (has been removed in v4) 10 | @mixin hide-text() { 11 | font: 0/0 a; 12 | color: transparent; 13 | text-shadow: none; 14 | background-color: transparent; 15 | border: 0; 16 | } 17 | 18 | // New mixin to use as of v3.0.1 19 | @mixin text-hide() { 20 | @include hide-text; 21 | } 22 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_labels.scss: -------------------------------------------------------------------------------- 1 | // Labels 2 | 3 | @mixin label-variant($color) { 4 | background-color: $color; 5 | 6 | &[href] { 7 | &:hover, 8 | &:focus { 9 | background-color: darken($color, 10%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_list-group.scss: -------------------------------------------------------------------------------- 1 | // List Groups 2 | 3 | @mixin list-group-item-variant($state, $background, $color) { 4 | .list-group-item-#{$state} { 5 | color: $color; 6 | background-color: $background; 7 | 8 | // [converter] extracted a&, button& to a.list-group-item-#{$state}, button.list-group-item-#{$state} 9 | } 10 | 11 | a.list-group-item-#{$state}, 12 | button.list-group-item-#{$state} { 13 | color: $color; 14 | 15 | .list-group-item-heading { 16 | color: inherit; 17 | } 18 | 19 | &:hover, 20 | &:focus { 21 | color: $color; 22 | background-color: darken($background, 5%); 23 | } 24 | &.active, 25 | &.active:hover, 26 | &.active:focus { 27 | color: #fff; 28 | background-color: $color; 29 | border-color: $color; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_nav-divider.scss: -------------------------------------------------------------------------------- 1 | // Horizontal dividers 2 | // 3 | // Dividers (basically an hr) within dropdowns and nav lists 4 | 5 | @mixin nav-divider($color: #e5e5e5) { 6 | height: 1px; 7 | margin: (($line-height-computed / 2) - 1) 0; 8 | overflow: hidden; 9 | background-color: $color; 10 | } 11 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_nav-vertical-align.scss: -------------------------------------------------------------------------------- 1 | // Navbar vertical align 2 | // 3 | // Vertically center elements in the navbar. 4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. 5 | 6 | @mixin navbar-vertical-align($element-height) { 7 | margin-top: (($navbar-height - $element-height) / 2); 8 | margin-bottom: (($navbar-height - $element-height) / 2); 9 | } 10 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_opacity.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: alpha(opacity=$opacity-ie); 8 | } 9 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_pagination.scss: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | @mixin pagination-size($padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) { 4 | > li { 5 | > a, 6 | > span { 7 | padding: $padding-vertical $padding-horizontal; 8 | font-size: $font-size; 9 | line-height: $line-height; 10 | } 11 | &:first-child { 12 | > a, 13 | > span { 14 | @include border-left-radius($border-radius); 15 | } 16 | } 17 | &:last-child { 18 | > a, 19 | > span { 20 | @include border-right-radius($border-radius); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_panels.scss: -------------------------------------------------------------------------------- 1 | // Panels 2 | 3 | @mixin panel-variant($border, $heading-text-color, $heading-bg-color, $heading-border) { 4 | border-color: $border; 5 | 6 | & > .panel-heading { 7 | color: $heading-text-color; 8 | background-color: $heading-bg-color; 9 | border-color: $heading-border; 10 | 11 | + .panel-collapse > .panel-body { 12 | border-top-color: $border; 13 | } 14 | .badge { 15 | color: $heading-bg-color; 16 | background-color: $heading-text-color; 17 | } 18 | } 19 | & > .panel-footer { 20 | + .panel-collapse > .panel-body { 21 | border-bottom-color: $border; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_progress-bar.scss: -------------------------------------------------------------------------------- 1 | // Progress bars 2 | 3 | @mixin progress-bar-variant($color) { 4 | background-color: $color; 5 | 6 | // Deprecated parent class requirement as of v3.2.0 7 | .progress-striped & { 8 | @include gradient-striped; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_reset-filter.scss: -------------------------------------------------------------------------------- 1 | // Reset filters for IE 2 | // 3 | // When you need to remove a gradient background, do not forget to use this to reset 4 | // the IE filter for IE9 and below. 5 | 6 | @mixin reset-filter() { 7 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 8 | } 9 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_reset-text.scss: -------------------------------------------------------------------------------- 1 | @mixin reset-text() { 2 | font-family: $font-family-base; 3 | // We deliberately do NOT reset font-size. 4 | font-style: normal; 5 | font-weight: normal; 6 | letter-spacing: normal; 7 | line-break: auto; 8 | line-height: $line-height-base; 9 | text-align: left; // Fallback for where `start` is not supported 10 | text-align: start; 11 | text-decoration: none; 12 | text-shadow: none; 13 | text-transform: none; 14 | white-space: normal; 15 | word-break: normal; 16 | word-spacing: normal; 17 | word-wrap: normal; 18 | } 19 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_resize.scss: -------------------------------------------------------------------------------- 1 | // Resize anything 2 | 3 | @mixin resizable($direction) { 4 | resize: $direction; // Options: horizontal, vertical, both 5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` 6 | } 7 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_responsive-visibility.scss: -------------------------------------------------------------------------------- 1 | // Responsive utilities 2 | 3 | // 4 | // More easily include all the states for responsive-utilities.less. 5 | // [converter] $parent hack 6 | @mixin responsive-visibility($parent) { 7 | #{$parent} { 8 | display: block !important; 9 | } 10 | table#{$parent} { display: table !important; } 11 | tr#{$parent} { display: table-row !important; } 12 | th#{$parent}, 13 | td#{$parent} { display: table-cell !important; } 14 | } 15 | 16 | // [converter] $parent hack 17 | @mixin responsive-invisibility($parent) { 18 | #{$parent} { 19 | display: none !important; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_size.scss: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | @mixin size($width, $height) { 4 | width: $width; 5 | height: $height; 6 | } 7 | 8 | @mixin square($size) { 9 | @include size($size, $size); 10 | } 11 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_tab-focus.scss: -------------------------------------------------------------------------------- 1 | // WebKit-style focus 2 | 3 | @mixin tab-focus() { 4 | // WebKit-specific. Other browsers will keep their default outline style. 5 | // (Initially tried to also force default via `outline: initial`, 6 | // but that seems to erroneously remove the outline in Firefox altogether.) 7 | outline: 5px auto -webkit-focus-ring-color; 8 | outline-offset: -2px; 9 | } 10 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_table-row.scss: -------------------------------------------------------------------------------- 1 | // Tables 2 | 3 | @mixin table-row-variant($state, $background) { 4 | // Exact selectors below required to override `.table-striped` and prevent 5 | // inheritance to nested tables. 6 | .table > thead > tr, 7 | .table > tbody > tr, 8 | .table > tfoot > tr { 9 | > td.#{$state}, 10 | > th.#{$state}, 11 | &.#{$state} > td, 12 | &.#{$state} > th { 13 | background-color: $background; 14 | } 15 | } 16 | 17 | // Hover states for `.table-hover` 18 | // Note: this is not available for cells or rows within `thead` or `tfoot`. 19 | .table-hover > tbody > tr { 20 | > td.#{$state}:hover, 21 | > th.#{$state}:hover, 22 | &.#{$state}:hover > td, 23 | &:hover > .#{$state}, 24 | &.#{$state}:hover > th { 25 | background-color: darken($background, 5%); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_text-emphasis.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | 3 | // [converter] $parent hack 4 | @mixin text-emphasis-variant($parent, $color) { 5 | #{$parent} { 6 | color: $color; 7 | } 8 | a#{$parent}:hover, 9 | a#{$parent}:focus { 10 | color: darken($color, 10%); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/assets/_sass/bootstrap/mixins/_text-overflow.scss: -------------------------------------------------------------------------------- 1 | // Text overflow 2 | // Requires inline-block or block for proper styling 3 | 4 | @mixin text-overflow() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /docs/assets/css/docs.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | @import 'theme'; 4 | 5 | .nav { 6 | li { 7 | &.active { 8 | & > a { 9 | font-weight: bold; 10 | } 11 | } 12 | 13 | .nav { 14 | margin-left: 10px; 15 | 16 | li { 17 | &.active { 18 | & > a { 19 | font-weight: 100; 20 | border-left: 2px solid #eeeeee; 21 | padding-left: 13px; 22 | border-left-color: $state-info-bg; 23 | } 24 | } 25 | 26 | a { 27 | padding: 5px 15px; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/assets/css/fault-tree.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | @import 'theme'; 4 | 5 | #fault-tree { 6 | display: inline-block; 7 | position: relative; 8 | width: 100%; 9 | padding-bottom: 80%; 10 | vertical-align: top; 11 | overflow: hidden; 12 | border: 1px solid $brand-success; 13 | border-radius: 16px; 14 | } 15 | 16 | .fault-tree-content { 17 | display: inline-block; 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | } 22 | 23 | .node { 24 | cursor: pointer; 25 | 26 | text { 27 | font: 10px sans-serif; 28 | font-weight: bold; 29 | 30 | &.atleast { 31 | font: 8px sans-serif; 32 | font-weight: bold; 33 | } 34 | } 35 | } 36 | 37 | .node-link { 38 | fill: none; 39 | stroke: #ccc; 40 | stroke-width: 1.5px; 41 | } 42 | 43 | div.node-tooltip { 44 | position: absolute; 45 | text-align: center; 46 | width: 120px; 47 | padding: 2px; 48 | font: 12px sans-serif; 49 | background: lightsteelblue; 50 | border: 0px; 51 | border-radius: 8px; 52 | pointer-events: none; 53 | } 54 | -------------------------------------------------------------------------------- /docs/assets/css/index.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | @import 'theme'; 4 | @import 'bootstrap'; 5 | 6 | .jumbotron { 7 | .lead { 8 | text-align: center; 9 | margin-top: 1em; 10 | margin-bottom: 0; 11 | } 12 | } 13 | 14 | .callout { 15 | border: 1px solid #eee; 16 | border-left-width: 5px; 17 | border-radius: 3px; 18 | padding: 20px; 19 | margin: 20px 0; 20 | 21 | &-danger { 22 | border-left-color: $state-danger-bg; 23 | 24 | h4 { 25 | color: $state-danger-bg; 26 | } 27 | } 28 | 29 | &-warning { 30 | border-left-color: $state-warning-bg; 31 | 32 | h4 { 33 | color: $state-warning-bg; 34 | } 35 | } 36 | 37 | &-info { 38 | border-left-color: $state-info-bg; 39 | 40 | h4 { 41 | color: $state-info-bg; 42 | } 43 | } 44 | } 45 | 46 | p.next { 47 | margin: 1em 0; 48 | font-size: 120%; 49 | } 50 | 51 | a.link-to { 52 | margin-left: .4em; 53 | font-size: 80%; 54 | } 55 | 56 | h5 { 57 | font-weight: bold; 58 | } 59 | -------------------------------------------------------------------------------- /docs/assets/js/prism-hql.js: -------------------------------------------------------------------------------- 1 | /* global Prism */ 2 | 3 | (function () { 4 | Prism.languages.hql = { 5 | 'string': /("(?!:)(\\?[^'"])*?"(?!:)|\b[a-z\.]+\b)/g 6 | }; 7 | 8 | Prism.languages.insertBefore('hql', 'string', { 9 | 'operator': /(\+|-|!=|=|!\^|\^|!)|(\b(and|or|not|in|with|as|by)\b)/g, 10 | 'keyword': /(\$[a-z]+|\$now|<[a-z]+>|\b[0-9]+(ms|s|m|H|d|w)\b)/g 11 | }); 12 | }()); 13 | -------------------------------------------------------------------------------- /docs/assets/js/prism-ts.js: -------------------------------------------------------------------------------- 1 | /* global Prism */ 2 | 3 | (function () { 4 | Prism.languages.ts = { 5 | 'keyword': /[a-z\-]+(?=[=])/g, 6 | 'number': /[a-z\.]+(?=\s*{)/g, 7 | 'string': /(?:(?![=]))[a-z\-%\/]+/g, 8 | 'operator': /(=)/g 9 | }; 10 | }()); 11 | -------------------------------------------------------------------------------- /docs/content/_api_types/aggregation.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: Aggregation 3 | fields: 4 | - name: type 5 | required: true 6 | type_json: '"relative"' 7 | purpose: Type of the aggregation. 8 | - name: sampling 9 | type_name: SamplingQuery 10 | purpose: Sampling to use with aggregation. 11 | --- 12 | Java Class: {% include github-link.html name='aggregation.Aggregation' %} 13 | 14 | An aggregation is responsible for analysing and sampling a larger dataset into a smaller, more manageable one. 15 | For details on all available aggregations, see the [Aggregations Section]({{ "/docs/aggregations" | relative_url }}). 16 | 17 | This object tells the distance to the point in the past. 18 | -------------------------------------------------------------------------------- /docs/content/_api_types/match-options.html: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: MatchOptions 3 | fields: 4 | - name: fuzzy 5 | type_json: '<boolean>' 6 | purpose: If set, perform elasticsearch fuzzy matching (very slow). 7 | - name: tokenize 8 | type_json: '<boolean>' 9 | purpose: If set, perform additional server-side tokenization. 10 | --- 11 |

12 | com.spotify.heroic.suggest.MatchOptions 13 |

14 | 15 |
16 | These are a set of experimental match options that are typically set during 17 | suggest queries. 18 | 19 | None of them should typically be enabled unless an experiment is being 20 | performed since they might severely impact the performance of the service. 21 |
22 | -------------------------------------------------------------------------------- /docs/content/_api_types/metric-collection.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: MetricCollection 3 | fields: 4 | - name: type 5 | required: true 6 | type_json: '"points"' 7 | purpose: The type of data to write. 8 | - name: data 9 | required: true 10 | type_json: '[Point, ...]' 11 | purpose: The data to write. The type depends on the value of the type field. 12 | --- 13 | {% include github-link.html module='heroic-component' name='metric.MetricCollection' %} 14 | -------------------------------------------------------------------------------- /docs/content/_api_types/point.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: Point 3 | fields: 4 | - name: datapoint 5 | required: true 6 | type_json: '[<timestamp>, <value>]' 7 | purpose: | 8 |

A single datapoint.

9 |

The <timestamp> is the number of milliseconds since unix epoch.

10 |

The <value> is the sample value.

11 | --- 12 | com.spotify.heroic.metric.Point 13 | 14 | See the [Points section in Data Model]({{ "/docs/data_model#data-points" | relative_url }}) 15 | for more details about what a point is. 16 | -------------------------------------------------------------------------------- /docs/content/_api_types/sampling-query.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: SamplingQuery 3 | fields: 4 | - name: unit 5 | type_json: '<string>' 6 | purpose: The unit to use when building the sampling bucket. E.g. `seconds`, `minutes`, `hours`. 7 | - name: value 8 | required: true 9 | type_json: '<number> | 120' 10 | purpose: The value/size of the sample. 11 | 12 | --- 13 | A complete example is shown below. 14 | 15 | `"sampling": {"unit": "seconds", "value": 120}` 16 | 17 | com.spotify.heroic.aggregation.SamplingQuery 18 | -------------------------------------------------------------------------------- /docs/content/_api_types/series.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: Series 3 | fields: 4 | - name: key 5 | required: true 6 | type_json: '<string>' 7 | purpose: Key of the series. 8 | - name: tags 9 | required: true 10 | type_json: '{<string>: <string>, ...}' 11 | purpose: Tags of the series. 12 | --- 13 | {% include github-link.html module='heroic-component' name='common.Series' %} 14 | 15 | 16 | See the [Series section in Data Model]({{ "/docs/data_model#series" | relative_url }}) 17 | for details about what a series is. 18 | -------------------------------------------------------------------------------- /docs/content/_api_types/statistics.md: -------------------------------------------------------------------------------- 1 | --- 2 | type_name: Statistics 3 | fields: 4 | - name: counters 5 | type_json: '{<string>: <number>}' 6 | purpose: Counters containing statistics. 7 | --- 8 | com.spotify.heroic.common.Statistics 9 | -------------------------------------------------------------------------------- /docs/content/_docs/api.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: API 3 | layout: sidebar 4 | --- 5 |

{{ page.title }}

6 | 7 |

Endpoints

8 | 9 | {%- assign endpoints = site.endpoints | sort: 'endpoint' | reverse %} 10 | {%- for item in endpoints %} 11 |
12 |

{{ item.method }} {{item.endpoint}}

13 |

{{item.help}}

14 |
15 | {%- endfor %} 16 | 17 |

Types

18 | 19 | {%- for item in site.api_types %} 20 |
21 |

{{ item.type_name }}

22 | {%- if item.help %} 23 |

{{ item.help }}

24 | {%- endif %} 25 |
26 | {%- endfor %} 27 | -------------------------------------------------------------------------------- /docs/content/_docs/architecture.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/docs/content/_docs/architecture.html -------------------------------------------------------------------------------- /docs/content/_docs/federation.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Federated Clusters 3 | --- 4 |

{{ page.title }}

5 | 6 |

7 | Heroic has support for federating requests, which allows multiple independent 8 | clusters to serve clients through a single, global interface. 9 |

10 | 11 |

12 | In a federated cluster, requests are routed to shards, and each 13 | shard is responsible for a distinct chunk of the available data. 14 | If all nodes in a single shard become unavailable, the data for that shard is 15 | unavailable. 16 | Shards are identified by a set of tags, two nodes are said to belong to the 17 | same shard if their cluster tags are identical. You can see more about this 18 | in the cluster documentation. 19 |

20 | 21 | {% include federation-tail.html %} 22 | -------------------------------------------------------------------------------- /docs/content/_docs/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation 3 | --- 4 |

{{ page.title }}

5 | 6 |

7 | I don't know how to do index pages, just go to the next section. 8 |

9 | -------------------------------------------------------------------------------- /docs/content/_docs/reliability.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: sidebar 3 | --- 4 | ## {{ page.title }} 5 | 6 | The following [fault tree analyses](https://en.wikipedia.org/wiki/Fault_tree_analysis) were done to give an idea of the reliability of different Heroic components. There are many variables that make them up, so much like performance benchmarks they can change drastically depending on the specific setup. 7 | 8 | ### Fault Tree Analyses 9 | 10 | {%- assign ftas = site.fault_tree | sort: 'name' | reverse %} 11 | {%- for item in ftas %} 12 | #### [{{ item.title }}]({{ item.url | relative_url }}) 13 | {%- endfor %} 14 | -------------------------------------------------------------------------------- /docs/content/_docs/visualizations.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Visualizations 3 | --- 4 |

{{ page.title }}

5 | 6 |

7 | We recommend using Grafana to visualize your data from Heroic. 8 |

9 | 10 |

11 | There is a Heroic Datasource available for Grafana. You can find instructions on how to install and use this plugin in the README. 12 |

13 | -------------------------------------------------------------------------------- /docs/content/_endpoints/delete-metadata-series.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: DELETE 3 | endpoint: /metadata/series 4 | help: Delete time series metadata 5 | description: Delete all series metadata matching the given filter. 6 | request: metadata-query-body.json 7 | fields: 8 | - name: filter 9 | required: true 10 | type_name: Filter 11 | purpose: A filter to use when quering for tags. 12 | --- 13 | -------------------------------------------------------------------------------- /docs/content/_endpoints/get-status.html: -------------------------------------------------------------------------------- 1 | --- 2 | method: GET 3 | endpoint: /status 4 | help: Get the state of the service 5 | description: Query for the status of an instance. 6 | empty: true 7 | responses: 8 | - file: get-status.json 9 | status: 200 10 | - file: get-status-failed.html 11 | status: 503 12 | --- 13 |

14 | The status code 503 is used to indicate to load balancers that a service is 15 | not available for requests right now. 16 |

17 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-key-suggest.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/key-suggest 4 | help: Search for key suggestions 5 | description: Search for key suggestions. This endpoint is intended for use with auto-completion. 6 | fields: 7 | - name: key 8 | type_json: '<string>' 9 | purpose: Key to help limit down suggestions, this is a free text value that is analyzed by the search engine. 10 | - name: filter 11 | type_name: Filter 12 | purpose: Filter to apply 13 | - name: range 14 | type_name: QueryDateRange 15 | purpose: The time range for which to get suggestions. 16 | - name: limit 17 | type_json: '<number>' 18 | purpose: The time range for which to get suggestions. 19 | - name: match 20 | type_name: MatchOptions 21 | purpose: Temporary match options to apply, only used for experiments. 22 | response_fields: 23 | - name: 'errors' 24 | type_name: 'RequestError' 25 | type_array: true 26 | purpose: 'Errors that occured during the request.' 27 | - name: 'suggestions' 28 | required: true 29 | type_json: '[{score: <number>, key: <string>}, ...]' 30 | purpose: 'Suggest key values with scores.' 31 | --- 32 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-keys.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/keys 4 | help: Search for time series keys 5 | description: Use to query for keys. 6 | request: metadata-query-body.json 7 | fields: 8 | - name: filter 9 | required: true 10 | type_name: Filter 11 | purpose: A filter to use when quering for tags. 12 | --- 13 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-series-count.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/series-count 4 | help: Count time series metadata 5 | description: Count the number of time series matching a given filter. 6 | fields: 7 | - name: filter 8 | type_name: Filter 9 | purpose: A filter to use when counting series. 10 | - name: range 11 | type_name: QueryDateRange 12 | purpose: Range for which to count series. 13 | --- 14 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-series.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/series 4 | help: Search for time series metadata 5 | description: Get all series metadata matching the given filter. 6 | request: metadata-query-body.json 7 | fields: 8 | - name: filter 9 | required: true 10 | type_name: Filter 11 | purpose: A filter to use when quering for tags. 12 | - name: limit 13 | required: false 14 | type_name: Limit 15 | purpose: Limit the response 16 | --- 17 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-tag-value-suggest.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/tag-value-suggest 4 | help: Search for tag value suggestions 5 | description: Search for tag values. 6 | fields: 7 | - name: key 8 | type_json: '<string>' 9 | purpose: Key for where to search for tag values. 10 | - name: filter 11 | type_name: Filter 12 | purpose: Filter to apply 13 | - name: range 14 | type_name: QueryDateRange 15 | purpose: The time range for which to get suggestions. 16 | - name: limit 17 | type_json: '<number>' 18 | purpose: The time range for which to get suggestions. 19 | response_fields: 20 | - name: 'errors' 21 | type_name: 'RequestError' 22 | type_array: true 23 | purpose: 'Errors that occured during the request.' 24 | - name: values 25 | required: true 26 | type_json: '[<string>, ...]' 27 | purpose: Values of the given tag. 28 | - name: limited 29 | required: true 30 | type_json: '<boolean>' 31 | purpose: If the result is limited or not. 32 | --- 33 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-tagkey-count.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/tagkey-count 4 | help: Search for tag keys with the count of unique values per tag. 5 | description: Query for tag keys with value counts 6 | request: metadata-tagkey-count.json 7 | fields: 8 | - name: filter 9 | required: true 10 | type_name: Filter 11 | purpose: A filter to use when quering for tags. 12 | - name: limit 13 | required: false 14 | type_name: Limit 15 | purpose: Limit the response 16 | response_fields: 17 | - name: 'errors' 18 | type_name: 'RequestError' 19 | type_array: true 20 | purpose: 'Errors that occured during the request.' 21 | - name: 'suggestions' 22 | required: true 23 | type_json: '[{key: <string> count: <number>,exactValues: <string>}, ...]' 24 | purpose: 'Suggested tag keys with value counts' 25 | - name: 'limited' 26 | type_name: Limit 27 | purpose: 'Was the response limited or not.' 28 | --- 29 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-metadata-tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /metadata/tags 4 | help: Search for tags metadata 5 | description: Query for available tag combinations. 6 | request: metadata-query-body.json 7 | fields: 8 | - name: filter 9 | required: true 10 | type_name: Filter 11 | purpose: A filter to use when quering for tags. 12 | --- 13 | -------------------------------------------------------------------------------- /docs/content/_endpoints/post-query-batch.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /query/batch 4 | help: Perform a batch query 5 | description: Run multiple metrics query in a batch. 6 | fields: 7 | - name: '*' 8 | required: true 9 | type_name: Query 10 | purpose: Queries to run. 11 | response_fields: 12 | - name: '*' 13 | type_name: QueryResponse 14 | purpose: Responses to each query run. 15 | --- 16 | This accepts a JSON document where all keys are expected to map up to a Query. 17 |

18 | Note that the x-client-id: my_app_name 19 | header must be supplied since anonymous requests are not permitted. -------------------------------------------------------------------------------- /docs/content/_endpoints/post-write.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: POST 3 | endpoint: /write 4 | help: Write metrics 5 | description: Used for writing data into heroic directly. 6 | fields: 7 | - name: series 8 | required: true 9 | type_name: Series 10 | purpose: Time series to write data to. 11 | - name: data 12 | required: true 13 | type_name: MetricCollection 14 | purpose: Collection of metrics to write. 15 | response_fields: 16 | - name: errors 17 | type_name: RequestError 18 | type_array: true 19 | purpose: 'Potential errors returned either from different shards or for specific time series. The presence of an error does not cause the entire query to fail, instead it is up to the client to use this information to decide if the response is reliable enough.' 20 | - name: times 21 | type_json: '[<number>, ...]' 22 | purpose: 'Timing information for the write. Each entry indicates the amount of time it took a shard to perform the write, in nanoseconds.' 23 | --- 24 | -------------------------------------------------------------------------------- /docs/content/_endpoints/put-metadata-series.md: -------------------------------------------------------------------------------- 1 | --- 2 | method: PUT 3 | endpoint: /metadata/series 4 | help: Insert time series metadata 5 | description: Write the given series metadata. 6 | fields: 7 | - name: key 8 | type_json: '<string>' 9 | purpose: The key of the series. 10 | - name: tags 11 | type_json: '{<string>: <string>}' 12 | purpose: The tags of the series. 13 | --- 14 | -------------------------------------------------------------------------------- /docs/content/_fault_tree/pubsub-consumers.md: -------------------------------------------------------------------------------- 1 | --- 2 | components: Heroic consumers using PubSub for ingestion, and Bigtable and Elasticsearch for storage 3 | --- 4 | 5 | Some assumptions are made that negatively affect the accuracy of the model and the calculated probabilities should be seen as being quite pessimistic. The biggest assumptions are: 6 | 7 | - Numbers for Google Cloud services are taken from their SLAs. In reality, the services will typically be much more reliable than the numbers listed since SLAs are contractual agreements and skew conservatively. 8 | - Elasticsearch shard failure is modeled as any n data nodes failing in the cluster, where n is the replication factor. The actual failure would have to be all replicas failing together, not just any random nodes. 9 | -------------------------------------------------------------------------------- /docs/content/_tutorials/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | --- 4 |

{{ page.title }}

5 | 6 |

7 | The following articles are tutorials. 8 | They should act as a zero to complete guide of how to accomplish a certain task. 9 |

10 | 11 |

12 | The following tutorials are available: 13 |

14 | 15 | 18 | -------------------------------------------------------------------------------- /docs/images/logo_on_dark.48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/docs/images/logo_on_dark.48.png -------------------------------------------------------------------------------- /docs/images/logo_on_light.256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/docs/images/logo_on_light.256.png -------------------------------------------------------------------------------- /docs/images/logo_on_light.400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/docs/images/logo_on_light.400.png -------------------------------------------------------------------------------- /example/heroic-memory-example.yml: -------------------------------------------------------------------------------- 1 | port: 8080 2 | 3 | cluster: 4 | tags: 5 | site: london 6 | 7 | metrics: 8 | backends: 9 | - type: memory 10 | 11 | metadata: 12 | backends: 13 | - type: memory 14 | 15 | suggest: 16 | backends: 17 | - type: memory 18 | 19 | statistics: 20 | type: semantic 21 | 22 | shellServer: 23 | port: 9192 24 | -------------------------------------------------------------------------------- /example/heroic_consumer_pubsub_local_debug.yml: -------------------------------------------------------------------------------- 1 | port: 8081 2 | 3 | 4 | cluster: 5 | tags: 6 | site: guc 7 | 8 | metadata: 9 | backends: 10 | - type: memory 11 | 12 | metrics: 13 | seriesLimit: 20000 14 | dataLimit: 20000000 15 | groupLimit: 1000 16 | aggregationLimit: 1000000 17 | failOnLimits: true 18 | backends: 19 | - type: bigtable 20 | project: google-test-project 21 | instance: test-instance 22 | 23 | consumers: 24 | - type: pubsub 25 | schema: com.spotify.heroic.consumer.schemas.Spotify100Proto 26 | topic: test-topic 27 | project: google-test-project 28 | subscription: test-subscription 29 | maxOutstandingElementCount: 5000 30 | 31 | 32 | ingestion: 33 | updateMetrics: true 34 | updateMetadata: true 35 | updateSuggestions: false 36 | 37 | shellServer: 38 | port: 9290 39 | 40 | statistics: 41 | type: semantic 42 | 43 | queryLogging: 44 | type: slf4j 45 | name: "com.spotify.heroic.query_logging" 46 | 47 | features: 48 | - com.spotify.heroic.sliced_data_fetch 49 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 14 12:51:41 EST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 7 | -------------------------------------------------------------------------------- /heroic-component/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.google.protobuf' 2 | 3 | dependencies { 4 | implementation 'com.google.auto.value:auto-value-annotations' 5 | api 'eu.toolchain.serializer:tiny-serializer-processor' 6 | api 'eu.toolchain.async:tiny-async-api' 7 | implementation 'javax.ws.rs:javax.ws.rs-api' 8 | implementation 'com.google.guava:guava' 9 | api 'org.slf4j:slf4j-api' 10 | api 'com.fasterxml.jackson.core:jackson-annotations' 11 | api 'com.fasterxml.jackson.core:jackson-databind' 12 | api 'org.apache.commons:commons-lang3' 13 | implementation 'com.google.code.findbugs:annotations' 14 | implementation 'io.thekraken:grok' 15 | api 'io.opencensus:opencensus-api' 16 | api 'io.opencensus:opencensus-impl' 17 | implementation 'io.opencensus:opencensus-contrib-zpages' 18 | implementation 'io.opencensus:opencensus-contrib-grpc-util' 19 | implementation 'com.google.protobuf:protobuf-java' 20 | implementation 'com.tdunning:t-digest' 21 | } 22 | 23 | task testJar(type: Jar) { 24 | classifier = 'test' 25 | from sourceSets.test.output 26 | } 27 | 28 | artifacts { 29 | testRuntime testJar 30 | } 31 | 32 | description = 'Heroic: Component Project' 33 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/HeroicReporterConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic; 23 | 24 | import com.spotify.heroic.statistics.HeroicReporter; 25 | 26 | public interface HeroicReporterConfiguration { 27 | public void registerReporter(HeroicReporter reporter); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/AggregationDSL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation; 23 | 24 | public interface AggregationDSL { 25 | Aggregation build(final AggregationArguments arguments); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/AggregationResult.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation 23 | 24 | import com.spotify.heroic.common.Statistics 25 | 26 | data class AggregationResult( 27 | val result: List, 28 | val statistics: Statistics 29 | ) 30 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/BucketConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation; 23 | 24 | import com.spotify.heroic.metric.Metric; 25 | 26 | public interface BucketConsumer { 27 | void apply(B bucket, M metric); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/BucketStrategyStartEnd.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation 23 | 24 | /** 25 | * A start, and an end bucket (exclusive) selected. 26 | */ 27 | data class StartEnd(val start: Int, val end: Int) 28 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/DoubleBucket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation; 23 | 24 | public interface DoubleBucket extends Bucket { 25 | double value(); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/aggregation/TDigestBucket.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.aggregation; 23 | 24 | import com.tdunning.math.stats.TDigest; 25 | 26 | public interface TDigestBucket extends Bucket { 27 | TDigest value(); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/analytics/SeriesHit.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.analytics 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | data class SeriesHit(val series: Series, val hits: Long) 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/cluster/ClusterDiscoveryComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.cluster; 23 | 24 | public interface ClusterDiscoveryComponent { 25 | ClusterDiscovery clusterDiscovery(); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/cluster/ClusterNodeGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.cluster; 23 | 24 | import java.util.List; 25 | 26 | public interface ClusterNodeGroup extends ClusterNode.Group { 27 | List shards(); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/BiConsumerIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common; 23 | 24 | import java.io.IOException; 25 | 26 | public interface BiConsumerIO { 27 | void accept(A a, B b) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/FunctionIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common; 23 | 24 | import java.io.IOException; 25 | 26 | public interface FunctionIO { 27 | B apply(A input) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/GoAwayException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common; 23 | 24 | public class GoAwayException extends RuntimeException { 25 | public GoAwayException(String message) { 26 | super(message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/GroupMember.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"): you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common 23 | 24 | data class GroupMember( 25 | val group: String, 26 | val member: T, 27 | val groups: Groups, 28 | val defaultMember: Boolean 29 | ) 30 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/QuotaViolationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common; 23 | 24 | public class QuotaViolationException extends RuntimeException { 25 | } 26 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/ServiceInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"): you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common 23 | 24 | data class ServiceInfo( 25 | val name: String, 26 | val version: String, 27 | val id: String, 28 | val commit: String 29 | ) 30 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/common/TagPair.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"): you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common 23 | 24 | data class TagPair(val key: String, val value: String) 25 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/generator/MetadataGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.generator; 23 | 24 | import com.spotify.heroic.common.Series; 25 | 26 | import java.util.List; 27 | 28 | public interface MetadataGenerator { 29 | List generate(int count); 30 | } 31 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/lifecycle/LifeCycleHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.lifecycle; 23 | 24 | public interface LifeCycleHook { 25 | T get() throws Exception; 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/lifecycle/LifeCycleManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.lifecycle; 23 | 24 | public interface LifeCycleManager { 25 | LifeCycle build(LifeCycles instance); 26 | 27 | LifeCycle build(String id, LifeCycles instance); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/lifecycle/LifeCycleNamedHook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.lifecycle; 23 | 24 | public interface LifeCycleNamedHook extends LifeCycleHook { 25 | String id(); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/lifecycle/LifeCycles.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.lifecycle; 23 | 24 | public interface LifeCycles { 25 | void register(LifeCycleRegistry registry); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/metric/BackendEntry.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | data class BackendEntry(val series: Series, val metrics: MetricCollection) -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/metric/BackendKeySet.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric 23 | 24 | data class BackendKeySet(val keys: List, val failedKeys: Long) -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/metric/Distribution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric; 23 | 24 | import com.google.protobuf.ByteString; 25 | 26 | public interface Distribution { 27 | ByteString getValue(); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/querylogging/QueryLoggerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.querylogging; 23 | 24 | public interface QueryLoggerFactory { 25 | QueryLogger create(String component); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/querylogging/QueryLoggingComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.querylogging; 23 | 24 | public interface QueryLoggingComponent { 25 | QueryLoggerFactory queryLoggerFactory(); 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/scheduler/Task.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.scheduler; 23 | 24 | public interface Task { 25 | public void run() throws Exception; 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/statistics/StatisticsModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.statistics; 23 | 24 | import com.spotify.heroic.dagger.EarlyComponent; 25 | 26 | public interface StatisticsModule { 27 | StatisticsComponent module(EarlyComponent early); 28 | } 29 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/tracing/TracingConfig.kt: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.tracing 2 | 3 | data class TracingConfig( 4 | val probability: Double = 0.01, 5 | val zpagesPort: Int = 0, 6 | val requestHeadersToTags: List = listOf(), 7 | val tags: Map = mapOf(), 8 | val lightstep: Lightstep = Lightstep(), 9 | val squash: Squash = Squash() 10 | ) { 11 | data class Lightstep( 12 | val collectorHost: String = "", 13 | val collectorPort: Int = 80, 14 | val accessToken: String = "", 15 | val componentName: String = "heroic", 16 | val reportingIntervalMs: Int = 1000, 17 | val maxBufferedSpans: Int = 5000, 18 | val resetClient: Boolean = false 19 | ) 20 | 21 | data class Squash( 22 | val whitelist: List? = listOf(), 23 | val threshold: Int = 100 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /heroic-component/src/main/java/com/spotify/heroic/usagetracking/UsageTrackingComponent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"): you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.usagetracking 23 | 24 | interface UsageTrackingComponent { 25 | fun usageTracking(): UsageTracking 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/AbstractReducedResultTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.spotify.heroic.metric.RequestError; 5 | import org.junit.Before; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | 10 | import java.util.List; 11 | 12 | @RunWith(MockitoJUnitRunner.class) 13 | public abstract class AbstractReducedResultTest { 14 | @Mock 15 | protected RequestError e1; 16 | @Mock 17 | protected RequestError e2; 18 | 19 | protected List errors; 20 | 21 | @Before 22 | public final void abstractSetup() { 23 | errors = ImmutableList.of(e1, e2); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/aggregation/ChainDeserializeTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class ChainDeserializeTest { 10 | 11 | private ObjectMapper mapper; 12 | 13 | @Before 14 | public void setup() { 15 | mapper = new ObjectMapper(); 16 | } 17 | 18 | @Test 19 | public void testDeserialize() throws Exception { 20 | assertEquals(Chain.class, mapper.readValue("{\"chain\":[]}", Chain.class).getClass()); 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/aggregation/ChainInstanceTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.aggregation; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.mockito.Mockito.mock; 8 | import static org.mockito.Mockito.when; 9 | 10 | public class ChainInstanceTest { 11 | @Test 12 | public void pickLastValidCadence() { 13 | final AggregationInstance a = mock(AggregationInstance.class); 14 | final AggregationInstance b = mock(AggregationInstance.class); 15 | final AggregationInstance c = mock(AggregationInstance.class); 16 | when(a.cadence()).thenReturn(-1L); 17 | when(b.cadence()).thenReturn(2L); 18 | when(c.cadence()).thenReturn(-1L); 19 | 20 | final AggregationInstance chain = ChainInstance.fromList(ImmutableList.of(a, b, c)); 21 | assertEquals(2, chain.cadence()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/DateRangeTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | public class DateRangeTest { 9 | @Test 10 | public void testRounded() { 11 | final DateRange range = new DateRange(0, 1100); 12 | assertEquals(new DateRange(0, 1000), range.rounded(1000)); 13 | } 14 | 15 | @Test 16 | public void testRoundedIgnoreBadArgument() { 17 | final DateRange range = new DateRange(0, 0); 18 | assertTrue(range == range.rounded(-1)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/DurationTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class DurationTest { 10 | @Test 11 | public void testParseDuration() { 12 | assertEquals(new Duration(42, TimeUnit.MILLISECONDS), Duration.parseDuration("42ms")); 13 | assertEquals(new Duration(42, TimeUnit.SECONDS), Duration.parseDuration("42s")); 14 | assertEquals(new Duration(42, TimeUnit.MINUTES), Duration.parseDuration("42m")); 15 | assertEquals(new Duration(42, TimeUnit.HOURS), Duration.parseDuration("42h")); 16 | assertEquals(new Duration(42, TimeUnit.HOURS), Duration.parseDuration("42H")); 17 | assertEquals(new Duration(42, TimeUnit.DAYS), Duration.parseDuration("42d")); 18 | assertEquals(new Duration(42 * 7, TimeUnit.DAYS), Duration.parseDuration("42w")); 19 | } 20 | 21 | @Test(expected = IllegalArgumentException.class) 22 | public void testNegativeDuration() { 23 | Duration.parseDuration("-42ms"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/FeatureTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class FeatureTest { 9 | private final ObjectMapper m = new ObjectMapper(); 10 | 11 | @Test 12 | public void serializerTest() throws Exception { 13 | for (final Feature f : Feature.values()) { 14 | testFeature(Feature.DISTRIBUTED_AGGREGATIONS); 15 | } 16 | } 17 | 18 | private void testFeature(final Feature feature) throws Exception { 19 | assertEquals(String.format("feature (%s) is deserializable", feature), feature, 20 | m.readValue(String.format("\"%s\"", feature.id()), Feature.class)); 21 | assertEquals(String.format("feature (%s) is serializable", feature), 22 | String.format("\"%s\"", feature.id()), m.writeValueAsString(feature)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/FeaturesTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.google.common.collect.ImmutableSortedSet; 7 | import org.junit.Test; 8 | 9 | public class FeaturesTest { 10 | private final ObjectMapper m = new ObjectMapper(); 11 | 12 | @Test 13 | public void serializationTest() throws Exception { 14 | final Features f1 = Features.create(ImmutableSortedSet.of(Feature.DISTRIBUTED_AGGREGATIONS)); 15 | final String ref = "[\"com.spotify.heroic.distributed_aggregations\"]"; 16 | 17 | assertEquals(ref, m.writeValueAsString(f1)); 18 | assertEquals(f1, m.readValue(ref, Features.class)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/GrokProcessorTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class GrokProcessorTest { 8 | @Test 9 | public void testHost() throws Exception { 10 | final GrokProcessor grok = 11 | new GrokProcessor(ImmutableMap.of(), "%{pod}-%{role}-%{pool}\\.%{site}\\.%{domain}"); 12 | Assert.assertEquals( 13 | ImmutableMap.of("pod", "sto1", "role", "example", "pool", "a1", "site", "sto", "domain", 14 | "example.com"), grok.parse("sto1-example-a1.sto.example.com")); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/OptionalLimitTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class OptionalLimitTest { 11 | ObjectMapper m; 12 | 13 | @Before 14 | public void setup() { 15 | m = new ObjectMapper(); 16 | } 17 | 18 | @Test 19 | public void testSerialize() throws Exception { 20 | assertEquals("null", m.writeValueAsString(OptionalLimit.empty())); 21 | assertEquals("42", m.writeValueAsString(OptionalLimit.of(42))); 22 | } 23 | 24 | @Test 25 | public void testDeserialize() throws Exception { 26 | assertEquals(OptionalLimit.empty(), m.readValue("null", OptionalLimit.class)); 27 | assertEquals(OptionalLimit.of(42), m.readValue("42", OptionalLimit.class)); 28 | assertEquals(OptionalLimit.of(42), m.readValue("42.0", OptionalLimit.class)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/common/SelectedGroupTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.common; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import com.google.common.collect.ImmutableSortedSet; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.mockito.Mockito; 8 | 9 | public class SelectedGroupTest { 10 | private final Groups groupA = new Groups(ImmutableSet.of("foo", "bar")); 11 | private final Groups groupB = new Groups(ImmutableSet.of("bar", "baz")); 12 | 13 | @Test 14 | public void testMergedGroups() { 15 | final Grouped a = Mockito.mock(Grouped.class); 16 | final Grouped b = Mockito.mock(Grouped.class); 17 | Mockito.when(a.groups()).thenReturn(groupA); 18 | Mockito.when(b.groups()).thenReturn(groupB); 19 | 20 | final SelectedGroup g = new SelectedGroup<>(ImmutableSet.of(a, b)); 21 | 22 | Assert.assertEquals(ImmutableSortedSet.of("bar", "baz", "foo"), 23 | ImmutableSortedSet.copyOf(g.groups())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/DefaultScopeTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.mockito.runners.MockitoJUnitRunner; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.mockito.Mockito.doReturn; 11 | 12 | @RunWith(MockitoJUnitRunner.class) 13 | public class DefaultScopeTest { 14 | @Mock 15 | Context ctx; 16 | 17 | private DefaultScope scope; 18 | 19 | @Before 20 | public void setup() { 21 | scope = new DefaultScope(42L); 22 | } 23 | 24 | @Test 25 | public void lookupTest() { 26 | assertEquals(new IntegerExpression(ctx, 42L), scope.lookup(ctx, Expression.NOW)); 27 | } 28 | 29 | @Test(expected = ParseException.class) 30 | public void lookupMissingTest() { 31 | doReturn(new ParseException("", null, 0, 0, 0, 0)).when(ctx).scopeLookupError("missing"); 32 | 33 | scope.lookup(ctx, "missing"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/EmptyExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import java.util.function.BiFunction; 4 | 5 | public class EmptyExpressionTest extends AbstractExpressionTest { 6 | @Override 7 | protected EmptyExpression build(final Context ctx) { 8 | return new EmptyExpression(ctx); 9 | } 10 | 11 | @Override 12 | protected BiFunction, EmptyExpression, Void> visitorMethod() { 13 | return Expression.Visitor::visitEmpty; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/FunctionExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.ImmutableMap; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | public class FunctionExpressionTest extends AbstractExpressionTest { 9 | @Override 10 | protected FunctionExpression build(final Context ctx) { 11 | return new FunctionExpression(ctx, "foo", ImmutableList.of(), ImmutableMap.of()); 12 | } 13 | 14 | @Override 15 | protected BiFunction, FunctionExpression, Void> visitorMethod() { 16 | return Expression.Visitor::visitFunction; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/InstantExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import java.time.Instant; 4 | import java.util.function.BiFunction; 5 | 6 | public class InstantExpressionTest extends AbstractExpressionTest { 7 | private final Instant instant = Instant.ofEpochMilli(1000); 8 | 9 | @Override 10 | protected InstantExpression build(final Context ctx) { 11 | return new InstantExpression(ctx, instant); 12 | } 13 | 14 | @Override 15 | protected BiFunction, InstantExpression, Void> visitorMethod() { 16 | return Expression.Visitor::visitInstant; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/LetExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.function.BiFunction; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.mockito.Mock; 9 | import org.mockito.junit.MockitoJUnitRunner; 10 | 11 | @RunWith(MockitoJUnitRunner.class) 12 | public class LetExpressionTest extends AbstractExpressionTest { 13 | @Mock 14 | ReferenceExpression ref; 15 | 16 | @Override 17 | protected LetExpression build(final Context ctx) { 18 | return new LetExpression(ctx, ref, a); 19 | } 20 | 21 | @Override 22 | protected BiFunction, LetExpression, Void> visitorMethod() { 23 | return Expression.Visitor::visitLet; 24 | } 25 | 26 | @Test 27 | public void testAccessors() { 28 | final LetExpression e = build(); 29 | 30 | assertEquals(ref, e.getReference()); 31 | assertEquals(a, e.getExpression()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/NegateExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import static org.mockito.Mockito.doReturn; 4 | import static org.mockito.Mockito.verify; 5 | 6 | import java.util.function.BiFunction; 7 | import org.junit.Before; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.junit.MockitoJUnitRunner; 10 | 11 | @RunWith(MockitoJUnitRunner.class) 12 | public class NegateExpressionTest extends AbstractExpressionTest { 13 | @Before 14 | public void setupLocal() { 15 | doReturn(a).when(a).negate(); 16 | } 17 | 18 | @Override 19 | protected NegateExpression build(final Context ctx) { 20 | return new NegateExpression(ctx, a); 21 | } 22 | 23 | @Override 24 | protected BiFunction, NegateExpression, Void> visitorMethod() { 25 | return Expression.Visitor::visitNegate; 26 | } 27 | 28 | @Override 29 | public void evalTest() { 30 | super.evalTest(); 31 | verify(a).eval(scope); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/ReferenceExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.mockito.runners.MockitoJUnitRunner; 6 | 7 | import java.util.function.BiFunction; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | @RunWith(MockitoJUnitRunner.class) 12 | public class ReferenceExpressionTest extends AbstractExpressionTest { 13 | private final String name = "ref"; 14 | 15 | @Override 16 | protected ReferenceExpression build(final Context ctx) { 17 | return new ReferenceExpression(ctx, name); 18 | } 19 | 20 | @Override 21 | protected BiFunction, ReferenceExpression, Void> visitorMethod() { 22 | return Expression.Visitor::visitReference; 23 | } 24 | 25 | @Test 26 | public void testAccessors() { 27 | final ReferenceExpression e = build(); 28 | 29 | assertEquals(name, e.getName()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/grammar/TimeExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.grammar; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.function.BiFunction; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class TimeExpressionTest extends AbstractExpressionTest { 10 | @Override 11 | protected TimeExpression build(final Context ctx) { 12 | return new TimeExpression(ctx, 0, 0, 0, 0); 13 | } 14 | 15 | @Override 16 | protected BiFunction, TimeExpression, Void> visitorMethod() { 17 | return Expression.Visitor::visitTime; 18 | } 19 | 20 | @Override 21 | public void evalTest() { 22 | // do nothing 23 | } 24 | 25 | @Test 26 | public void parseTest() { 27 | assertEquals(build(), TimeExpression.parse(ctx, "00:00:00.000")); 28 | } 29 | 30 | @Test(expected = IllegalArgumentException.class) 31 | public void parseErrorTest() { 32 | TimeExpression.parse(ctx, "not a time"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/metadata/CountSeriesTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metadata; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.spotify.heroic.AbstractReducedResultTest; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class CountSeriesTest extends AbstractReducedResultTest { 11 | private CountSeries s1; 12 | private CountSeries s2; 13 | private CountSeries s3; 14 | 15 | @Before 16 | public void setup() { 17 | s1 = new CountSeries(errors, 3L, false); 18 | s2 = new CountSeries(ImmutableList.of(), 3L, false); 19 | s3 = new CountSeries(errors, 4L, true); 20 | } 21 | 22 | @Test 23 | public void reduceTest() throws Exception { 24 | assertEquals(new CountSeries(errors, 6L, false), 25 | CountSeries.reduce().collect(ImmutableList.of(s1, s2))); 26 | 27 | assertEquals(new CountSeries(errors, 7L, true), 28 | CountSeries.reduce().collect(ImmutableList.of(s2, s3))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/metadata/DeleteSeriesTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metadata; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.spotify.heroic.AbstractReducedResultTest; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class DeleteSeriesTest extends AbstractReducedResultTest { 11 | private DeleteSeries s1; 12 | private DeleteSeries s2; 13 | private DeleteSeries s3; 14 | 15 | @Before 16 | public void setup() { 17 | s1 = new DeleteSeries(errors, 3, 0); 18 | s2 = new DeleteSeries(ImmutableList.of(), 0, 4); 19 | } 20 | 21 | @Test 22 | public void reduceTest() throws Exception { 23 | assertEquals(new DeleteSeries(errors, 3, 4), 24 | DeleteSeries.reduce().collect(ImmutableList.of(s1, s2))); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /heroic-component/src/test/java/com/spotify/heroic/metric/ShardedResultGroupTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metric; 2 | 3 | import static org.junit.Assert.assertNotEquals; 4 | 5 | import com.google.common.collect.ImmutableList; 6 | import com.google.common.collect.ImmutableMap; 7 | import com.google.common.collect.ImmutableSet; 8 | import org.junit.Test; 9 | 10 | public class ShardedResultGroupTest { 11 | @Test 12 | public void testHashGroup() { 13 | final ShardedResultGroup g1 = 14 | new ShardedResultGroup(ImmutableMap.of("aa", "bb"), ImmutableMap.of("cc", "dd"), 15 | ImmutableSet.of(), MetricCollection.points(ImmutableList.of()), 0L); 16 | 17 | final ShardedResultGroup g2 = 18 | new ShardedResultGroup(ImmutableMap.of("", ""), ImmutableMap.of("aabb", "ccdd"), 19 | ImmutableSet.of(), MetricCollection.points(ImmutableList.of()), 0L); 20 | 21 | assertNotEquals(g1.hashGroup(), g2.hashGroup()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /heroic-component/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/PingMessage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic 23 | 24 | data class PingMessage(val port: Int, val id: String, val protocols: List) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/common/TypeNameMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.common; 23 | 24 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 25 | 26 | @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") 27 | public interface TypeNameMixin { 28 | } 29 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/dagger/CoreLoadingComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.dagger; 23 | 24 | import dagger.Component; 25 | 26 | @LoadingScope 27 | @Component(modules = LoadingModule.class) 28 | public interface CoreLoadingComponent extends LoadingComponent { 29 | } 30 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/grammar/KeywordValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | @file:JvmMultifileClass 23 | 24 | package com.spotify.heroic.grammar 25 | 26 | data class KeywordValue(val key: String, val expression: Expression) 27 | 28 | data class KeywordValues(val map: Map) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/grammar/Queries.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.grammar 23 | 24 | import com.spotify.heroic.shell.task.Query 25 | 26 | data class Queries(val queries: List) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/grammar/Statements.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.grammar 23 | 24 | data class Statements(val expressions: List) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/http/DataResponse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.http 23 | 24 | data class DataResponse(val data: T?) 25 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/http/metadata/Grouped.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.http.metadata 23 | 24 | import java.util.* 25 | 26 | data class Grouped(val group: Optional, val value: T) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/http/query/QueryBatchResponse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.http.query 23 | 24 | import com.spotify.heroic.metric.QueryMetricsResponse 25 | 26 | data class QueryBatchResponse(val results: Map) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/shell/ShellServerState.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.shell 23 | 24 | import java.net.ServerSocket 25 | 26 | data class ShellServerState(val serverSocket: ServerSocket?) 27 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/shell/task/AnalyticsHits.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.shell.task 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | internal data class AnalyticsHits(val series: Series, val id: String, val hits: Long) 27 | -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/shell/task/AnalyticsSeries.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.shell.task 23 | 24 | internal data class AnalyticsSeries( 25 | val id: String, 26 | val series: String 27 | ) -------------------------------------------------------------------------------- /heroic-core/src/main/java/com/spotify/heroic/shell/task/WritePerformanceTimes.kt: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.shell.task 2 | 3 | data class WritePerformanceTimes( 4 | val executionTimes: List, 5 | val runtime: Long 6 | ) 7 | 8 | data class WritePerformanceCollectedTimes( 9 | val runtimes: List, 10 | val executionTimes: List, 11 | val errors: Int 12 | ) -------------------------------------------------------------------------------- /heroic-core/src/main/proto/spotify_100.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package spotify.metric; 4 | option java_package = "com.spotify.proto"; 5 | 6 | option optimize_for = SPEED; 7 | 8 | message Metric { 9 | // key of metric. 10 | string key = 1; 11 | 12 | // time in ms when metric was generated. 13 | int64 time = 2; 14 | 15 | // value of metric. 16 | double value = 3; 17 | 18 | // tags associated to a metric, used to be referred to as attributes. 19 | map tags = 4; 20 | 21 | // resource "tags" associated to metric. 22 | map resource = 5; 23 | 24 | // Distribution type value 25 | Value distribution_type_value = 6; 26 | } 27 | 28 | message Batch { 29 | repeated Metric metric = 1; 30 | } 31 | 32 | 33 | message Value { 34 | oneof value { 35 | double double_value = 1; 36 | bytes distribution_value= 2; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /heroic-core/src/test/java/com/spotify/heroic/HeroicConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.Test; 5 | 6 | import java.io.InputStream; 7 | 8 | import static org.junit.Assert.assertFalse; 9 | 10 | public class HeroicConfigTest { 11 | private final ObjectMapper mapper = HeroicMappers.config(); 12 | 13 | public InputStream resource(final String name) { 14 | return getClass() 15 | .getClassLoader() 16 | .getResourceAsStream(getClass().getPackage().getName().replace('.', '/') + '/' + name); 17 | } 18 | 19 | @Test 20 | public void testConfigEmpty() throws Exception { 21 | try (final InputStream in = resource("Config.Empty.yml")) { 22 | assertFalse(HeroicConfig.loadConfig(mapper, in).isPresent()); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /heroic-core/src/test/java/com/spotify/heroic/metric/MetricQueryBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metric; 2 | 3 | public class MetricQueryBuilderTest { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/Config.Empty.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/heroic-core/src/test/resources/com/spotify/heroic/Config.Empty.yml -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/Query.AggregationCompat.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregation": { 3 | "type": "group", 4 | "each": {"type": "empty"} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/Query.AggregationCompat.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregation": { 3 | "type": "group", 4 | "each": [{"type": "empty"}] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/Query.AggregationCompat.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "aggregators": [ 3 | { 4 | "type": "group", 5 | "each": {"type": "empty"} 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/aggregation/SamplingQuery.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "unit": "minutes", 3 | "value": 10 4 | } -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/aggregation/SamplingQuery.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "unit": "minutes", 3 | "extent": 10 4 | } -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/aggregation/SamplingQuery.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "10m", 3 | "extent": "10m" 4 | } 5 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/aggregation/SamplingQuery.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "unit": "minutes", 3 | "value": 10, 4 | "extent": 10 5 | } 6 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/consumer/schemas/spotify-100-tests.txt: -------------------------------------------------------------------------------- 1 | # attributes (a.k.a tags) 2 | {"key": "foo", "attributes": {}} | {"key": "foo", "attributes": {}} 3 | {"key": "foo", "attributes": {}} | {"key": "foo", "attributes": {}} 4 | {"key": "foo", "attributes": {"foo": null}} | {"key": "foo", "attributes": {}} 5 | {"key": "foo", "attributes": {"foo": "bar"}} | {"key": "foo", "attributes": {"foo": "bar"}} 6 | {"key": "foo", "attributes": {"foo": "bar", "bar": "baz"}} | {"key": "foo", "attributes": {"foo": "bar", "bar": "baz"}} 7 | 8 | # resource tags 9 | {"key": "foo"} | {"key": "foo", "resource": {}} 10 | {"key": "foo", "resource": {}} | {"key": "foo", "resource": {}} 11 | {"key": "foo", "resource": null} | {"key": "foo", "resource": {}} 12 | {"key": "foo", "resource": {"foo": null}} | {"key": "foo", "resource": {}} 13 | {"key": "foo", "resource": {"foo": "bar"}} | {"key": "foo", "resource": {"foo": "bar"}} 14 | {"key": "foo", "resource": {"foo": "bar", "bar": "baz"}} | {"key": "foo", "resource": {"foo": "bar", "bar": "baz"}} 15 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/Event.json: -------------------------------------------------------------------------------- 1 | [1024, {"string": "foo"}] 2 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/FullQuery.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [], 3 | "errors": [], 4 | "statistics": { 5 | "counters": {} 6 | }, 7 | "trace": { 8 | "what": {"name": "test"}, 9 | "elapsed": 0, 10 | "children": [] 11 | }, 12 | "limits": [] 13 | } 14 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/MetricCollection.Event.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "events", 3 | "data": [[1000, {"foo":"bar"}], [2000, {"bar":"baz"}]] 4 | } 5 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/MetricCollection.Payload.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "cardinality", 3 | "data": [{"timestamp": 1000, "state": "Zm9vCg=="}, {"timestamp": 2000, "state": "YmFyCg=="}] 4 | } 5 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/MetricCollection.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "points", 3 | "data": [[1000, 10], [2000, 20]] 4 | } -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/Point.json: -------------------------------------------------------------------------------- 1 | [1024, 3.14] -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/QueryMetricsResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "queryId": "d11d0ad7-cc27-4667-a617-67a481f61c30", 3 | "range": { 4 | "start": 1484027520000, 5 | "end": 1484038320000 6 | }, 7 | "trace": { 8 | "what": { 9 | "name": "test" 10 | }, 11 | "elapsed": 0, 12 | "children": [] 13 | }, 14 | "limits": [], 15 | "cached": false, 16 | "cache": null, 17 | "commonTags": {}, 18 | "commonResource": {}, 19 | "result": [ 20 | { 21 | "type": "points", 22 | "hash": "2362f9de", 23 | "shard": {}, 24 | "cadence": 0, 25 | "values": [], 26 | "key": null, 27 | "tags": {}, 28 | "tagCounts": {}, 29 | "resource": {}, 30 | "resourceCounts": {} 31 | } 32 | ], 33 | "preAggregationSampleSize":null, 34 | "errors": [] 35 | } 36 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/com/spotify/heroic/metric/ResultGroup.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": {}, 3 | "series": [], 4 | "group": { 5 | "type": "points", 6 | "data": [] 7 | }, 8 | "cadence": 0 9 | } 10 | -------------------------------------------------------------------------------- /heroic-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /heroic-dist/src/main/resources/com.spotify.heroic/commit: -------------------------------------------------------------------------------- 1 | ${commit} 2 | -------------------------------------------------------------------------------- /heroic-dist/src/main/resources/com.spotify.heroic/version: -------------------------------------------------------------------------------- 1 | ${version} 2 | -------------------------------------------------------------------------------- /heroic-dist/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/GrpcClusterQueryIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | public class GrpcClusterQueryIT extends AbstractClusterQueryIT { 4 | @Override 5 | protected String protocol() { 6 | return "grpc"; 7 | } 8 | 9 | @Override 10 | protected void setupSupport() { 11 | super.setupSupport(); 12 | 13 | // TODO: over GRPC something goes wrong with the distributed cardinality support, or it's an 14 | // error. 15 | cardinalitySupport = false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/HeroicConfigurationTestUtils.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | import java.io.InputStream; 4 | import java.util.function.Supplier; 5 | 6 | /** 7 | * Provides utility functions for testing the processing of heroic.yaml. 8 | */ 9 | public class HeroicConfigurationTestUtils { 10 | 11 | public static HeroicCoreInstance testConfiguration(final String name) throws Exception { 12 | final HeroicCore.Builder builder = HeroicCore.builder(); 13 | builder.modules(HeroicModules.ALL_MODULES); 14 | builder.configStream(stream(name)); 15 | return builder.build().newInstance(); 16 | } 17 | 18 | public static Supplier stream(String name) { 19 | return () -> HeroicConfigurationTestUtils.class.getClassLoader().getResourceAsStream(name); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/JvmClusterQueryIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | public class JvmClusterQueryIT extends AbstractClusterQueryIT { 4 | @Override 5 | protected String protocol() { 6 | return "jvm"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/KafkaNonTransactionalConsumerIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | public class KafkaNonTransactionalConsumerIT extends AbstractKafkaConsumerIT { 4 | 5 | @Override 6 | boolean useTransactionalConsumer() { 7 | return false; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/KafkaTransactionalConsumerIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic; 2 | 3 | public class KafkaTransactionalConsumerIT extends AbstractKafkaConsumerIT { 4 | 5 | @Override 6 | boolean useTransactionalConsumer() { 7 | return true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/RandomData.kt: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic 2 | 3 | internal data class RandomData( 4 | val randomData: List?, 5 | val dataStat : Map? 6 | ) 7 | 8 | -------------------------------------------------------------------------------- /heroic-dist/src/test/java/com/spotify/heroic/TMetric.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic 23 | 24 | import com.spotify.heroic.consumer.schemas.spotify100.v2.Value 25 | 26 | internal data class TMetric( 27 | val timestamp: Long?, 28 | val value: Value 29 | ) 30 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-all.yml: -------------------------------------------------------------------------------- 1 | cluster: 2 | tags: 3 | role: test 4 | protocols: 5 | - type: grpc 6 | 7 | metadata: 8 | backends: 9 | - type: memory 10 | - type: elasticsearch 11 | backendType: kv 12 | 13 | suggest: 14 | backends: 15 | - type: elasticsearch 16 | numSuggestionsLimit: 100 17 | backendType: kv 18 | 19 | metrics: 20 | backends: 21 | - type: bigtable 22 | project: project 23 | maxWriteBatchSize: 250 24 | mutateRpcTimeoutMs: 135 25 | readRowsRpcTimeoutMs: 136 26 | shortRpcTimeoutMs: 137 27 | maxScanTimeoutRetries: 7 28 | maxElapsedBackoffMs: 138 29 | - type: datastax 30 | - type: memory 31 | 32 | analytics: 33 | type: bigtable 34 | project: project 35 | 36 | generator: 37 | metadata: 38 | type: random 39 | metrics: 40 | - type: sine 41 | 42 | consumers: 43 | - type: kafka 44 | schema: com.spotify.heroic.consumer.schemas.Spotify100 45 | topics: 46 | - testtopic1 47 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-conditional-features.yml: -------------------------------------------------------------------------------- 1 | conditionalFeatures: 2 | type: list 3 | list: 4 | - type: match 5 | features: 6 | - com.spotify.heroic.cache_query 7 | condition: 8 | type: clientId 9 | clientId: foobar 10 | - type: match 11 | features: 12 | - com.spotify.heroic.cache_query 13 | condition: 14 | type: any 15 | conditions: 16 | - type: clientId 17 | clientId: foo 18 | - type: clientId 19 | clientId: bar 20 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-datastax.yml: -------------------------------------------------------------------------------- 1 | metrics: 2 | backends: 3 | - type: datastax 4 | consistencyLevel: LOCAL_ONE 5 | poolingOptions: 6 | maxQueueSize: 256 7 | maxRequestsPerConnection: 8 | LOCAL: 1024 9 | REMOTE: 0 10 | coreConnectionsPerHost: 11 | LOCAL: 1 12 | REMOTE: 0 13 | maxConnectionsPerHost: 14 | LOCAL: 1 15 | REMOTE: 0 16 | seeds: 17 | - cassandra3 18 | schema: 19 | type: ng 20 | keyspace: heroic 21 | groupLimit: 10000 22 | seriesLimit: 10000 23 | dataLimit: 10000 24 | aggregationLimit: 10000 25 | failOnLimits: true 26 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-disabled-tracking.yml: -------------------------------------------------------------------------------- 1 | usageTracking: 2 | type: disabled -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-kafka.yml: -------------------------------------------------------------------------------- 1 | consumers: 2 | - type: kafka 3 | schema: com.spotify.heroic.consumer.schemas.Spotify100 4 | transactional: true 5 | topics: 6 | - testtopic1 7 | 8 | metrics: 9 | backends: 10 | - type: memory 11 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-metrics-limits.yml: -------------------------------------------------------------------------------- 1 | metrics: 2 | failOnLimits: true 3 | dataLimit: 10000 4 | seriesLimit: 10000 5 | groupLimit: 10000 6 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/heroic-null-shell-host.yml: -------------------------------------------------------------------------------- 1 | shellServer: 2 | port: 9192 3 | -------------------------------------------------------------------------------- /heroic-dist/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /heroic-elasticsearch-utils/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'com.spotify:folsom' 4 | api 'org.elasticsearch.client:transport' 5 | api 'org.elasticsearch.client:elasticsearch-rest-high-level-client' 6 | implementation 'org.elasticsearch.client:elasticsearch-rest-client-sniffer' 7 | implementation project(path: ':heroic-component', configuration: 'testRuntime') 8 | implementation 'org.apache.logging.log4j:log4j-core' 9 | } 10 | 11 | description = 'Heroic: Elasticsearch Utilities' 12 | -------------------------------------------------------------------------------- /heroic-test/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation project(':heroic-core') 4 | api group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25' 5 | 6 | implementation 'junit:junit' 7 | implementation 'org.apache.logging.log4j:log4j-core' 8 | implementation 'org.mockito:mockito-core' 9 | implementation 'org.testcontainers:testcontainers' 10 | implementation "org.testcontainers:elasticsearch" 11 | 12 | testImplementation project(':heroic-elasticsearch-utils') 13 | testImplementation project(':heroic-suggest-elasticsearch') 14 | testImplementation project(':heroic-metadata-elasticsearch') 15 | } 16 | 17 | description = 'Heroic: Test Utilities Base Project' 18 | 19 | -------------------------------------------------------------------------------- /logo.42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/logo.42.png -------------------------------------------------------------------------------- /metadata/elasticsearch/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation project(':heroic-elasticsearch-utils') 4 | implementation 'com.spotify:folsom' 5 | 6 | implementation project(':heroic-test') 7 | testImplementation 'eu.toolchain.async:tiny-async-core' 8 | testImplementation 'org.apache.logging.log4j:log4j-api' 9 | testImplementation 'org.apache.logging.log4j:log4j-core' 10 | testImplementation 'org.testcontainers:testcontainers' 11 | testImplementation "org.testcontainers:elasticsearch" 12 | 13 | } 14 | 15 | group = 'com.spotify.heroic.metadata' 16 | description = 'Heroic: Elasticsearch Metadata' 17 | -------------------------------------------------------------------------------- /metadata/elasticsearch/src/main/resources/com.spotify.heroic.metadata.elasticsearch/kv/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "key": { 4 | "type": "keyword", 5 | "doc_values": true 6 | }, 7 | "tags": { 8 | "type": "keyword", 9 | "doc_values": true 10 | }, 11 | "tag_keys": { 12 | "type": "keyword", 13 | "doc_values": true 14 | }, 15 | "hash": { 16 | "type": "keyword", 17 | "doc_values": true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /metadata/memory/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | testImplementation project(':heroic-test') 4 | } 5 | 6 | group = 'com.spotify.heroic.metadata' 7 | description = 'Heroic: In-Memory Metadata' 8 | -------------------------------------------------------------------------------- /metadata/memory/src/test/java/com/spotify/heroic/metadata/memory/MemoryBackendIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metadata.memory; 2 | 3 | import com.spotify.heroic.metadata.MetadataModule; 4 | import com.spotify.heroic.test.AbstractMetadataBackendIT; 5 | 6 | public class MemoryBackendIT extends AbstractMetadataBackendIT { 7 | @Override 8 | protected MetadataModule setupModule() throws Exception { 9 | return MemoryMetadataModule.builder().build(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /metric/bigtable/src/main/java/com/spotify/heroic/analytics/bigtable/SeriesKey.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.analytics.bigtable 23 | 24 | import com.spotify.heroic.common.Series 25 | import java.time.LocalDate 26 | 27 | data class SeriesKey(val date: LocalDate, val series: Series) -------------------------------------------------------------------------------- /metric/bigtable/src/main/java/com/spotify/heroic/metric/bigtable/RowKey.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric.bigtable 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | data class RowKey(val series: Series, val base: Long) -------------------------------------------------------------------------------- /metric/bigtable/src/main/java/com/spotify/heroic/metric/bigtable/api/ColumnFamily.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric.bigtable.api 23 | 24 | data class ColumnFamily(val clusterUri: String, val tableId: String, val name: String) -------------------------------------------------------------------------------- /metric/bigtable/src/main/java/com/spotify/heroic/metric/bigtable/api/LatestCellValueColumn.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric.bigtable.api 23 | 24 | import com.google.protobuf.ByteString 25 | 26 | data class LatestCellValueColumn(val qualifier: ByteString, val value: ByteString) 27 | -------------------------------------------------------------------------------- /metric/datastax/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'com.datastax.cassandra:cassandra-driver-core' 4 | implementation 'eu.toolchain.serializer:tiny-serializer-core' 5 | implementation 'eu.toolchain.async:tiny-async-api' 6 | implementation 'eu.toolchain.serializer:tiny-serializer-api' 7 | compileOnly 'eu.toolchain.serializer:tiny-serializer-processor' 8 | 9 | testImplementation project(':heroic-test') 10 | testImplementation project(path: ':heroic-component', configuration: 'testRuntime') 11 | testImplementation 'org.testcontainers:cassandra' 12 | } 13 | 14 | group = 'com.spotify.heroic.metric' 15 | description = 'Heroic: Datastax Backend' 16 | -------------------------------------------------------------------------------- /metric/datastax/src/main/java/com/spotify/heroic/metric/datastax/RowFetchResult.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric.datastax 23 | 24 | import com.datastax.driver.core.ExecutionInfo 25 | 26 | data class RowFetchResult( 27 | val info: List, 28 | val data: List 29 | ) -------------------------------------------------------------------------------- /metric/datastax/src/main/java/com/spotify/heroic/metric/datastax/schema/SchemaComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.metric.datastax.schema; 23 | 24 | public interface SchemaComponent { 25 | Schema schema(); 26 | } 27 | -------------------------------------------------------------------------------- /metric/datastax/src/main/resources/com.spotify.heroic.metric.datastax.schema.legacy/keyspace.cql: -------------------------------------------------------------------------------- 1 | CREATE KEYSPACE IF NOT EXISTS {{keyspace}} 2 | WITH REPLICATION = { 3 | 'class' : 'SimpleStrategy', 4 | 'replication_factor' : 3 5 | }; 6 | -------------------------------------------------------------------------------- /metric/datastax/src/main/resources/com.spotify.heroic.metric.datastax.schema.legacy/tables.cql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS {{keyspace}}.metrics ( 2 | metric_key blob, 3 | data_timestamp_offset int, 4 | data_value double, 5 | PRIMARY KEY(metric_key, data_timestamp_offset) 6 | ) WITH COMPACT STORAGE; 7 | -------------------------------------------------------------------------------- /metric/datastax/src/main/resources/com.spotify.heroic.metric.datastax.schema.ng/keyspace.cql: -------------------------------------------------------------------------------- 1 | CREATE KEYSPACE IF NOT EXISTS {{keyspace}} 2 | WITH REPLICATION = { 3 | 'class' : 'SimpleStrategy', 4 | 'replication_factor' : 3 5 | }; 6 | -------------------------------------------------------------------------------- /metric/datastax/src/main/resources/com.spotify.heroic.metric.datastax.schema.ng/tables.cql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS {{keyspace}}.metrics ( 2 | metric_key blob, 3 | data_timestamp_offset int, 4 | data_value double, 5 | PRIMARY KEY(metric_key, data_timestamp_offset) 6 | ) WITH COMPACT STORAGE; 7 | -------------------------------------------------------------------------------- /metric/datastax/src/test/java/com/spotify/heroic/metric/datastax/NextGenDatastaxBackendIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metric.datastax; 2 | 3 | import com.spotify.heroic.metric.datastax.schema.SchemaModule; 4 | import com.spotify.heroic.metric.datastax.schema.ng.NextGenSchemaInstance; 5 | import com.spotify.heroic.metric.datastax.schema.ng.NextGenSchemaModule; 6 | import java.util.Optional; 7 | 8 | public class NextGenDatastaxBackendIT extends AbstractDatastaxBackendIT { 9 | @Override 10 | protected Optional period() { 11 | return Optional.of(NextGenSchemaInstance.MAX_WIDTH); 12 | } 13 | 14 | @Override 15 | protected SchemaModule setupSchema(final String keyspace) { 16 | return NextGenSchemaModule.builder().keyspace(keyspace).build(); 17 | } 18 | 19 | @Override 20 | protected void setupSupport() { 21 | super.setupSupport(); 22 | 23 | this.brokenSegmentsPr208 = true; 24 | this.hugeRowKey = false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /metric/datastax/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /metric/memory/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | 3 | dependencies { 4 | implementation project(':heroic-component') 5 | testImplementation project(':heroic-test') 6 | kapt 'com.google.dagger:dagger-compiler' 7 | implementation 'org.apache.logging.log4j:log4j-core' 8 | } 9 | 10 | group = 'com.spotify.heroic.metric' 11 | description = 'Heroic: Memory Backend' 12 | -------------------------------------------------------------------------------- /metric/memory/src/test/java/com/spotify/heroic/metric/memory/MemoryBackendIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.metric.memory; 2 | 3 | import com.spotify.heroic.metric.MetricModule; 4 | import com.spotify.heroic.test.AbstractMetricBackendIT; 5 | 6 | public class MemoryBackendIT extends AbstractMetricBackendIT { 7 | @Override 8 | protected void setupSupport() { 9 | super.setupSupport(); 10 | 11 | this.eventSupport = true; 12 | this.hugeRowKey = false; 13 | } 14 | 15 | @Override 16 | protected MetricModule setupModule(BackendModuleMode mode) { 17 | return MemoryMetricModule.builder().build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rpc/grpc/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | 3 | dependencies { 4 | implementation project(':heroic-component') 5 | implementation project(':heroic-core') 6 | implementation 'io.grpc:grpc-stub' 7 | implementation 'io.grpc:grpc-netty' 8 | kapt 'com.google.dagger:dagger-compiler' 9 | } 10 | 11 | group = 'com.spotify.heroic.rpc' 12 | description = 'Heroic: GRPC RPC Protocol' 13 | -------------------------------------------------------------------------------- /rpc/grpc/src/main/java/com/spotify/heroic/rpc/grpc/GrpcRpcEmptyBody.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.rpc.grpc; 23 | 24 | import com.fasterxml.jackson.annotation.JsonCreator; 25 | 26 | public class GrpcRpcEmptyBody { 27 | @JsonCreator 28 | public GrpcRpcEmptyBody() { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rpc/jvm/README.md: -------------------------------------------------------------------------------- 1 | # JVM RPC for Heroic 2 | 3 | This module allows multiple Heroic instances to communicate with each other if 4 | they are on the same JVM. 5 | This is only used for integration testing. 6 | 7 | For an example of this, see the 8 | [AbstractLocalClusterIT](/heroic-dist/src/test/java/com/spotify/heroic/AbstractLocalClusterIT.java) test 9 | suite. 10 | -------------------------------------------------------------------------------- /rpc/jvm/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | 3 | dependencies { 4 | implementation project(':heroic-component') 5 | kapt 'com.google.dagger:dagger-compiler' 6 | } 7 | 8 | group = 'com.spotify.heroic.rpc' 9 | description = 'Heroic: JVM RPC Protocol' 10 | -------------------------------------------------------------------------------- /run-heroic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec java $JVM_DEFAULT_ARGS $JVM_ARGS -jar /usr/share/heroic/heroic.jar "$@" 3 | -------------------------------------------------------------------------------- /src/main/resources/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spotify/heroic/9a021a7a4acf643012cd0b2bfe8f59e7b6cfda89/src/main/resources/.gitkeep -------------------------------------------------------------------------------- /statistics/semantic/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation 'com.spotify.metrics:semantic-metrics-core' 4 | implementation 'com.spotify.metrics:semantic-metrics-ffwd-reporter' 5 | testImplementation 'org.hamcrest:java-hamcrest' 6 | } 7 | 8 | group = 'com.spotify.heroic.statistics' 9 | description = 'Heroic: Semantic Metrics Statistics' 10 | -------------------------------------------------------------------------------- /statistics/semantic/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /suggest/elasticsearch/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | implementation project(':heroic-elasticsearch-utils') 4 | implementation 'com.spotify:folsom' 5 | 6 | implementation project(':heroic-test') 7 | testImplementation 'org.apache.logging.log4j:log4j-api' 8 | testImplementation 'org.apache.logging.log4j:log4j-core' 9 | testImplementation 'org.testcontainers:testcontainers' 10 | testImplementation "org.testcontainers:elasticsearch" 11 | } 12 | 13 | group = 'com.spotify.heroic.suggest' 14 | description = 'Heroic: Elasticsearch Suggest' 15 | -------------------------------------------------------------------------------- /suggest/elasticsearch/src/main/resources/com.spotify.heroic.suggest.elasticsearch/kv/series.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "key": { 4 | "type": "keyword", 5 | "doc_values": true, 6 | "fields": { 7 | "analyzed": { 8 | "type": "text", 9 | "analyzer": "bag_analyzer" 10 | }, 11 | "prefix": { 12 | "type": "text", 13 | "analyzer": "prefix_analyzer" 14 | } 15 | } 16 | }, 17 | "tags": { 18 | "type": "keyword", 19 | "doc_values": true 20 | }, 21 | "tag_keys": { 22 | "type": "keyword", 23 | "doc_values": true 24 | }, 25 | "series_id": { 26 | "type": "keyword", 27 | "doc_values": true 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /suggest/elasticsearch/src/main/resources/com.spotify.heroic.suggest.elasticsearch/kv/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "analysis": { 3 | "analyzer": { 4 | "bag_analyzer": { 5 | "type": "custom", 6 | "tokenizer": "keyword", 7 | "filter": ["heroic_tag_word", "lowercase", "heroic_bag_edge_ngram", "unique"] 8 | }, 9 | "prefix_analyzer": { 10 | "type": "custom", 11 | "tokenizer": "keyword", 12 | "filter": ["lowercase", "heroic_prefix_edge_ngram"] 13 | } 14 | }, 15 | "filter": { 16 | "heroic_tag_word": { 17 | "type": "word_delimiter", 18 | "split_on_numerics": false 19 | }, 20 | "heroic_bag_edge_ngram": { 21 | "type": "edgeNGram", 22 | "max_gram": "10", 23 | "min_gram": "2" 24 | }, 25 | "heroic_prefix_edge_ngram": { 26 | "type": "edgeNGram", 27 | "max_gram": "20", 28 | "min_gram": "2" 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /suggest/memory/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | implementation project(':heroic-component') 3 | 4 | testImplementation project(':heroic-test') 5 | } 6 | 7 | group = 'com.spotify.heroic.suggest' 8 | description = 'Heroic: In-Memory Suggest' 9 | -------------------------------------------------------------------------------- /suggest/memory/src/main/java/com/spotify/heroic/suggest/memory/KeyDocument.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.suggest.memory 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | data class KeyDocument(val id: String, val series: Series) -------------------------------------------------------------------------------- /suggest/memory/src/main/java/com/spotify/heroic/suggest/memory/TagDocument.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.suggest.memory 23 | 24 | import com.spotify.heroic.common.Series 25 | 26 | data class TagDocument(val id: TagId, val series: Series) -------------------------------------------------------------------------------- /suggest/memory/src/main/java/com/spotify/heroic/suggest/memory/TagId.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | package com.spotify.heroic.suggest.memory 23 | 24 | data class TagId(val key: String, val value: String) -------------------------------------------------------------------------------- /suggest/memory/src/test/java/com/spotify/heroic/suggest/memory/MemoryBackendIT.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.suggest.memory; 2 | 3 | import com.spotify.heroic.suggest.SuggestModule; 4 | import com.spotify.heroic.test.AbstractSuggestBackendIT; 5 | 6 | public class MemoryBackendIT extends AbstractSuggestBackendIT { 7 | @Override 8 | protected SuggestModule setupModule() { 9 | return MemorySuggestModule.builder().build(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /suggest/memory/src/test/java/com/spotify/heroic/suggest/memory/MemoryBackendTest.java: -------------------------------------------------------------------------------- 1 | package com.spotify.heroic.suggest.memory; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class MemoryBackendTest { 9 | @Test 10 | public void testAnalyze() { 11 | assertEquals( 12 | ImmutableSet.of("w", "wo", "wor", "worl", "world", "h", "he", "hel", "hell", "hello"), 13 | MemoryBackend.analyze("HelloWorld")); 14 | 15 | assertEquals(ImmutableSet.of("a", "b"), MemoryBackend.analyze("a-b")); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /system-tests/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest-docker-compose 2 | requests 3 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools for working with Heroic 2 | 3 | ## querylog 4 | 5 | A utility used for configuring an Elasticsearch cluster with the templates necessary to index 6 | query logs as emitted by Heroic. 7 | 8 | #### Usage 9 | 10 | ```bash 11 | tools/querylog --host http://:9200 [--base-index ] [--logstash] \ 12 | --commit 13 | ``` 14 | 15 | The value provided to the `--base-index` option is what will be used as a prefix to the index 16 | template. 17 | -------------------------------------------------------------------------------- /tools/add_license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TMPFILE=$(mktemp) 3 | 4 | cleanup() { 5 | rm "$TMPFILE" 6 | } 7 | 8 | trap cleanup 0 9 | 10 | for FILE in $(find . -type f -name "*.java" | grep -v '/src/test/' | grep -v '/target/generated-sources/'); do 11 | # check if file has license. 12 | # if not, prepend it 13 | if ! tools/license_matcher.py "$FILE" >/dev/null;then 14 | echo "Prepend license to $FILE" 15 | cat tools/java.header "$FILE" > "$TMPFILE" 16 | cat "$TMPFILE" > "$FILE" 17 | fi 18 | 19 | done 20 | -------------------------------------------------------------------------------- /tools/generate-assets: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | root=$(dirname $(dirname $0)) 6 | rasterize=$root/tools/rasterize 7 | assets=$root/assets 8 | out=$root/assets/out 9 | 10 | mkdir -p $out 11 | 12 | for f in $assets/*.svg; do 13 | echo "Rasterize: $f" 14 | $rasterize $f $out 15 | done 16 | -------------------------------------------------------------------------------- /tools/heroic-shell: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | root=$(dirname $(dirname $0)) 4 | dist="$root/heroic-dist" 5 | 6 | JVM_OPTS=${JVM_OPTS:-""} 7 | 8 | GRADLE=${GRADLE:-./gradlew} 9 | GRADLE_OPTS=${GRADLE_OPTS:-"--console=plain"} 10 | DIST_TASK="heroic-dist:shadowJar" 11 | 12 | dist_jar="$dist/build/libs/heroic-dist-0.0.1-SNAPSHOT-shaded.jar" 13 | CLASSPATH="$dist_jar" 14 | 15 | if [[ $1 == '-debug' ]]; then 16 | JVM_OPTS="$JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=y" 17 | JVM_OPTS="$JVM_OPTS -Dloglevel=TRACE" 18 | shift 19 | fi 20 | 21 | if [[ -d $dist ]]; then 22 | if [[ ! -f $dist_jar ]]; then 23 | (cd $root && $GRADLE $GRADLE_OPTS clean $DIST_TASK) 24 | fi 25 | 26 | if [[ ! -f $dist_jar ]]; then 27 | echo "No such jar: $dist_jar" 1>&2 28 | exit 1 29 | fi 30 | fi 31 | 32 | exec java -cp "$CLASSPATH" $JVM_OPTS com.spotify.heroic.HeroicShell "$@" 33 | -------------------------------------------------------------------------------- /tools/java.header: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Spotify AB. 3 | * 4 | * Licensed to the Apache Software Foundation (ASF) under one 5 | * or more contributor license agreements. See the NOTICE file 6 | * distributed with this work for additional information 7 | * regarding copyright ownership. The ASF licenses this file 8 | * to you under the Apache License, Version 2.0 (the 9 | * "License"); you may not use this file except in compliance 10 | * with the License. You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, 15 | * software distributed under the License is distributed on an 16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | * KIND, either express or implied. See the License for the 18 | * specific language governing permissions and limitations 19 | * under the License. 20 | */ 21 | 22 | -------------------------------------------------------------------------------- /tools/rasterize: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SIZES="42 64 128 256" 6 | 7 | in="$1" 8 | out="$2" 9 | 10 | if [[ -z $in || -z $out ]]; then 11 | echo "Usage: $0 " 12 | exit 1 13 | fi 14 | 15 | b=$(basename $in) 16 | b=${b%.svg} 17 | 18 | for s in $SIZES; do 19 | o=$out/$b.$s.png 20 | echo "Writing: $o" 21 | convert $in -resize ${s}x $o 22 | done 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /tools/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | PyYAML 3 | -------------------------------------------------------------------------------- /usage-tracking/disabled/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | 3 | dependencies { 4 | kapt 'com.google.dagger:dagger-compiler' 5 | implementation project(':heroic-component') 6 | } 7 | 8 | group = 'com.spotify.heroic.usagetracking' 9 | description = 'Heroic: Disabled Usage Tracking' -------------------------------------------------------------------------------- /usage-tracking/google/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'kotlin-kapt' 2 | 3 | dependencies { 4 | kapt 'com.google.dagger:dagger-compiler' 5 | implementation project(':heroic-component') 6 | implementation 'com.squareup.okhttp3:okhttp' 7 | } 8 | 9 | group = 'com.spotify.heroic.usagetracking' 10 | description = 'Heroic: Google Analytics' 11 | --------------------------------------------------------------------------------