{"action":"created","comment":{"url":"https://api.github.com/repos/m3db/m3aggregator/pulls/comments/176863377","pull_request_review_id":106647279,"id":176863377,"diff_hunk":"@@ -315,170 +339,179 @@ func (e *Entry) recordLastAccessed(currTime time.Time) {\n \tatomic.StoreInt64(&e.lastAccessNanos, currTime.UnixNano())\n }\n \n-// NB(xichen): we assume the policies are sorted by their cutover times\n+// NB(xichen): we assume the metadatas are sorted by their cutover times\n // in ascending order.\n-func (e *Entry) activeStagedPoliciesWithLock(\n-\tpl policy.PoliciesList,\n+func (e *Entry) activeStagedMetadataWithLock(\n \tt time.Time,\n-) (policy.StagedPolicies, error) {\n-\t// If we have no policy to apply, simply bail.\n-\tif len(pl) == 0 {\n-\t\te.metrics.emptyPoliciesList.Inc(1)\n-\t\treturn policy.DefaultStagedPolicies, errEmptyPoliciesList\n+\tmetadatas metadata.StagedMetadatas,\n+) (metadata.StagedMetadata, error) {\n+\t// If we have no metadata to apply, simply bail.\n+\tif len(metadatas) == 0 {\n+\t\te.metrics.emptyMetadatas.Inc(1)\n+\t\treturn metadata.DefaultStagedMetadata, errEmptyMetadatas\n \t}\n \ttimeNanos := t.UnixNano()\n-\tfor idx := len(pl) - 1; idx >= 0; idx-- {\n-\t\tif pl[idx].CutoverNanos <= timeNanos {\n-\t\t\treturn pl[idx], nil\n+\tfor idx := len(metadatas) - 1; idx >= 0; idx-- {\n+\t\tif metadatas[idx].CutoverNanos <= timeNanos {\n+\t\t\treturn metadatas[idx], nil\n \t\t}\n \t}\n-\treturn pl[0], nil\n+\te.metrics.noApplicableMetadata.Inc(1)\n+\treturn metadata.DefaultStagedMetadata, errNoApplicableMetadata\n }\n \n-func (e *Entry) shouldUpdatePoliciesWithLock(currTime time.Time, sp policy.StagedPolicies) bool {\n-\tif e.cutoverNanos == uninitializedCutoverNanos {\n-\t\treturn true\n-\t}\n-\t// If this is a future policy, we don't update the existing policy\n-\t// and instead use the cached policy.\n-\tif currTime.UnixNano() < sp.CutoverNanos {\n-\t\te.metrics.futurePolicy.Inc(1)\n-\t\treturn false\n-\t}\n-\t// If this is a stale policy, we don't update the existing policy\n-\t// and instead use the cached policy.\n-\tif sp.CutoverNanos < e.cutoverNanos {\n-\t\te.metrics.stalePolicy.Inc(1)\n-\t\treturn false\n-\t}\n-\tif e.cutoverNanos != sp.CutoverNanos {\n-\t\treturn true\n-\t}\n-\tpolicies, useDefaultPolicies := sp.Policies()\n-\tif e.useDefaultPolicies && useDefaultPolicies {\n+// NB: The metadata passed in is guaranteed to have cut over based on the current time.\n+func (e *Entry) shouldUpdateMetadatasWithLock(\n+\tcurrTime time.Time,\n+\tsm metadata.StagedMetadata,\n+) bool {\n+\t// If this is a stale metadata, we don't update the existing metadata.\n+\tif e.cutoverNanos > sm.CutoverNanos {\n+\t\te.metrics.staleMetadata.Inc(1)\n \t\treturn false\n \t}\n-\tif useDefaultPolicies {\n-\t\tpolicies = e.opts.DefaultPolicies()\n-\t}\n-\treturn e.hasPolicyChangesWithLock(policies)\n-}\n \n-func (e *Entry) hasPolicyChangesWithLock(newPolicies []policy.Policy) bool {\n-\tif len(e.aggregations) != len(newPolicies) {\n+\t// If this is a newer metadata, we always update.\n+\tif e.cutoverNanos < sm.CutoverNanos {\n \t\treturn true\n \t}\n-\tfor _, policy := range newPolicies {\n-\t\tif _, exists := e.aggregations[policy]; !exists {\n-\t\t\treturn true\n+\n+\t// Iterate over the list of pipelines and check whether we have metadata changes.\n+\t// NB: If the incoming metadata have the same set of aggregation keys as the cached\n+\t// set but also have duplicates, there is no need to update metadatas as long as\n+\t// the cached set has all aggregation keys in the incoming metadata and vice versa.\n+\tbs := bitset.New(uint(len(e.aggregations)))\n+\tfor _, pipeline := range sm.Pipelines {\n+\t\tstoragePolicies := e.storagePolicies(pipeline.StoragePolicies)\n+\t\tfor _, storagePolicy := range storagePolicies {\n+\t\t\tkey := aggregationKey{\n+\t\t\t\taggregationID: pipeline.AggregationID,\n+\t\t\t\tstoragePolicy: storagePolicy,\n+\t\t\t\tpipeline:      pipeline.Pipeline,\n+\t\t\t}\n+\t\t\tidx := e.aggregations.index(key)\n+\t\t\tif idx < 0 {\n+\t\t\t\treturn true\n+\t\t\t}\n+\t\t\tbs.Set(uint(idx))\n \t\t}\n \t}\n-\treturn false\n+\treturn bs.All(uint(len(e.aggregations)))\n }\n \n-func (e *Entry) updatePoliciesWithLock(\n-\ttyp unaggregated.Type,\n-\tid metricid.RawID,\n-\townsID bool,\n-\thasDefaultPoliciesList bool,\n-\tsp policy.StagedPolicies,\n-) error {\n-\tpolicies, useDefaultPolicies := sp.Policies()\n-\tif useDefaultPolicies {\n-\t\tpolicies = e.opts.DefaultPolicies()\n+func (e *Entry) storagePolicies(policies []policy.StoragePolicy) []policy.StoragePolicy {\n+\tif !policy.IsDefaultStoragePolicies(policies) {\n+\t\treturn policies\n \t}\n+\treturn e.opts.DefaultStoragePolicies()\n+}\n \n-\t// Fast path to exit in case the policies didn't change.\n-\tif !e.hasPolicyChangesWithLock(policies) {\n-\t\te.hasDefaultPoliciesList = hasDefaultPoliciesList\n-\t\te.useDefaultPolicies = useDefaultPolicies\n-\t\te.cutoverNanos = sp.CutoverNanos\n-\t\te.metrics.policyUpdates.Inc(1)\n-\t\treturn nil\n+func (e *Entry) maybeCopyIDWithLock(metric unaggregated.MetricUnion) metricid.RawID {\n+\t// If we own the ID, there is no need to copy.\n+\tif metric.OwnsID {\n+\t\treturn metric.ID\n \t}\n \n-\telemID := id\n-\tif !ownsID {\n-\t\tif len(e.aggregations) > 0 {\n-\t\t\t// If there are existing elements for this id, try reusing\n-\t\t\t// the id from the elements because those are owned by us.\n-\t\t\tfor _, elem := range e.aggregations {\n-\t\t\t\telemID = elem.Value.(metricElem).ID()\n-\t\t\t\tbreak\n-\t\t\t}\n-\t\t} else {\n-\t\t\t// Otherwise this is a new id so it is necessary to make a\n-\t\t\t// copy because it's not owned by us.\n-\t\t\telemID = make(metricid.RawID, len(id))\n-\t\t\tcopy(elemID, id)\n+\t// If there are existing elements for this id, try reusing\n+\t// the id from the elements because those are owned by us.\n+\tif len(e.aggregations) > 0 {\n+\t\tfor _, aggVal := range e.aggregations {\n+\t\t\treturn aggVal.elem.Value.(metricElem).ID()\n \t\t}\n \t}\n \n-\t// We should update the policies first.\n-\tnewAggregations := make(map[policy.Policy]*list.Element, len(policies))\n-\tfor _, p := range policies {\n-\t\tif elem, exists := e.aggregations[p]; exists {\n-\t\t\tnewAggregations[p] = elem\n-\t\t} else {\n-\t\t\taggTypes, err := e.decompressor.Decompress(p.AggregationID)\n-\t\t\tif err != nil {\n-\t\t\t\treturn err\n-\t\t\t}\n+\t// Otherwise it is necessary to make a copy because it's not owned by us.\n+\telemID := make(metricid.RawID, len(metric.ID))\n+\tcopy(elemID, metric.ID)\n+\treturn elemID\n+}\n+\n+func (e *Entry) updateMetadatasWithLock(\n+\tmetric unaggregated.MetricUnion,\n+\thasDefaultMetadatas bool,\n+\tsm metadata.StagedMetadata,\n+) error {\n+\tvar (\n+\t\telemID          = e.maybeCopyIDWithLock(metric)\n+\t\tnewAggregations = make(aggregationValues, 0, initialAggregationCapacity)\n+\t)\n \n-\t\t\tvar newElem metricElem\n-\t\t\tswitch typ {\n-\t\t\tcase unaggregated.CounterType:\n-\t\t\t\tif !aggTypes.IsValidForCounter() {\n-\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Counter\", aggTypes.String())\n+\t// Update the metadatas.\n+\tfor _, pipeline := range sm.Pipelines {\n+\t\tstoragePolicies := e.storagePolicies(pipeline.StoragePolicies)\n+\t\tfor _, storagePolicy := range storagePolicies {\n+\t\t\tkey := aggregationKey{\n+\t\t\t\taggregationID: pipeline.AggregationID,\n+\t\t\t\tstoragePolicy: storagePolicy,\n+\t\t\t\tpipeline:      pipeline.Pipeline,\n+\t\t\t}\n+\t\t\t// Remove duplicate aggregation pipelines.\n+\t\t\tif newAggregations.contains(key) {\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tif idx := e.aggregations.index(key); idx >= 0 {\n+\t\t\t\tnewAggregations = append(newAggregations, e.aggregations[idx])\n+\t\t\t} else {\n+\t\t\t\taggTypes, err := e.decompressor.Decompress(key.aggregationID)\n+\t\t\t\tif err != nil {\n+\t\t\t\t\treturn err\n \t\t\t\t}\n-\t\t\t\tnewElem = e.opts.CounterElemPool().Get()\n-\t\t\tcase unaggregated.BatchTimerType:\n-\t\t\t\tif !aggTypes.IsValidForTimer() {\n-\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Timer\", aggTypes.String())\n+\t\t\t\tvar newElem metricElem\n+\t\t\t\tswitch metric.Type {\n+\t\t\t\tcase unaggregated.CounterType:\n+\t\t\t\t\tif !aggTypes.IsValidForCounter() {\n+\t\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Counter\", aggTypes.String())\n+\t\t\t\t\t}\n+\t\t\t\t\tnewElem = e.opts.CounterElemPool().Get()\n+\t\t\t\tcase unaggregated.BatchTimerType:\n+\t\t\t\t\tif !aggTypes.IsValidForTimer() {\n+\t\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Timer\", aggTypes.String())\n+\t\t\t\t\t}\n+\t\t\t\t\tnewElem = e.opts.TimerElemPool().Get()\n+\t\t\t\tcase unaggregated.GaugeType:\n+\t\t\t\t\tif !aggTypes.IsValidForGauge() {\n+\t\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Gauge\", aggTypes.String())\n+\t\t\t\t\t}\n+\t\t\t\t\tnewElem = e.opts.GaugeElemPool().Get()\n+\t\t\t\tdefault:\n+\t\t\t\t\treturn errInvalidMetricType\n \t\t\t\t}\n-\t\t\t\tnewElem = e.opts.TimerElemPool().Get()\n-\t\t\tcase unaggregated.GaugeType:\n-\t\t\t\tif !aggTypes.IsValidForGauge() {\n-\t\t\t\t\treturn fmt.Errorf(\"invalid aggregation types %s for Gauge\", aggTypes.String())\n+\t\t\t\t// NB: The pipeline may not be owned by us and as such we need to make a copy here.","path":"aggregator/entry.go","position":536,"original_position":536,"commit_id":"7d422f7338c9012aa338ce88d85b8cecf360a835","original_commit_id":"7d422f7338c9012aa338ce88d85b8cecf360a835","user":{"login":"xichen2020","id":12239421,"avatar_url":"https://avatars1.githubusercontent.com/u/12239421?v=4","gravatar_id":"","url":"https://api.github.com/users/xichen2020","html_url":"https://github.com/xichen2020","followers_url":"https://api.github.com/users/xichen2020/followers","following_url":"https://api.github.com/users/xichen2020/following{/other_user}","gists_url":"https://api.github.com/users/xichen2020/gists{/gist_id}","starred_url":"https://api.github.com/users/xichen2020/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xichen2020/subscriptions","organizations_url":"https://api.github.com/users/xichen2020/orgs","repos_url":"https://api.github.com/users/xichen2020/repos","events_url":"https://api.github.com/users/xichen2020/events{/privacy}","received_events_url":"https://api.github.com/users/xichen2020/received_events","type":"User","site_admin":false},"body":"So the thinking here is that the pipeline might be reused by the decoding iterator, just like it does with metric IDs, and as such we need to make a copy to ensure the pipeline doesn't get changed underneath us.","created_at":"2018-03-23T21:06:19Z","updated_at":"2018-03-23T21:06:37Z","html_url":"https://github.com/m3db/m3aggregator/pull/92#discussion_r176863377","pull_request_url":"https://api.github.com/repos/m3db/m3aggregator/pulls/92","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/comments/176863377"},"html":{"href":"https://github.com/m3db/m3aggregator/pull/92#discussion_r176863377"},"pull_request":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/92"}},"in_reply_to_id":176854460},"pull_request":{"url":"https://api.github.com/repos/m3db/m3aggregator/pulls/92","id":177164757,"html_url":"https://github.com/m3db/m3aggregator/pull/92","diff_url":"https://github.com/m3db/m3aggregator/pull/92.diff","patch_url":"https://github.com/m3db/m3aggregator/pull/92.patch","issue_url":"https://api.github.com/repos/m3db/m3aggregator/issues/92","number":92,"state":"open","locked":false,"title":"Ingest untimed metrics with pipeline metadata","user":{"login":"xichen2020","id":12239421,"avatar_url":"https://avatars1.githubusercontent.com/u/12239421?v=4","gravatar_id":"","url":"https://api.github.com/users/xichen2020","html_url":"https://github.com/xichen2020","followers_url":"https://api.github.com/users/xichen2020/followers","following_url":"https://api.github.com/users/xichen2020/following{/other_user}","gists_url":"https://api.github.com/users/xichen2020/gists{/gist_id}","starred_url":"https://api.github.com/users/xichen2020/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xichen2020/subscriptions","organizations_url":"https://api.github.com/users/xichen2020/orgs","repos_url":"https://api.github.com/users/xichen2020/repos","events_url":"https://api.github.com/users/xichen2020/events{/privacy}","received_events_url":"https://api.github.com/users/xichen2020/received_events","type":"User","site_admin":false},"body":"cc @cw9 @jeromefroe \r\n\r\nThis PR adds logic to ingest untimed metrics with pipeline metadata at the aggregation tier.","created_at":"2018-03-23T19:30:57Z","updated_at":"2018-03-23T21:06:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ccc226a7accf74e7bbf4ead069d80a052be317df","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/m3db/m3aggregator/pulls/92/commits","review_comments_url":"https://api.github.com/repos/m3db/m3aggregator/pulls/92/comments","review_comment_url":"https://api.github.com/repos/m3db/m3aggregator/pulls/comments{/number}","comments_url":"https://api.github.com/repos/m3db/m3aggregator/issues/92/comments","statuses_url":"https://api.github.com/repos/m3db/m3aggregator/statuses/7d422f7338c9012aa338ce88d85b8cecf360a835","head":{"label":"m3db:xichen-add-pipeline-metadata","ref":"xichen-add-pipeline-metadata","sha":"7d422f7338c9012aa338ce88d85b8cecf360a835","user":{"login":"m3db","id":19937171,"avatar_url":"https://avatars0.githubusercontent.com/u/19937171?v=4","gravatar_id":"","url":"https://api.github.com/users/m3db","html_url":"https://github.com/m3db","followers_url":"https://api.github.com/users/m3db/followers","following_url":"https://api.github.com/users/m3db/following{/other_user}","gists_url":"https://api.github.com/users/m3db/gists{/gist_id}","starred_url":"https://api.github.com/users/m3db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/m3db/subscriptions","organizations_url":"https://api.github.com/users/m3db/orgs","repos_url":"https://api.github.com/users/m3db/repos","events_url":"https://api.github.com/users/m3db/events{/privacy}","received_events_url":"https://api.github.com/users/m3db/received_events","type":"Organization","site_admin":false},"repo":{"id":74390805,"name":"m3aggregator","full_name":"m3db/m3aggregator","owner":{"login":"m3db","id":19937171,"avatar_url":"https://avatars0.githubusercontent.com/u/19937171?v=4","gravatar_id":"","url":"https://api.github.com/users/m3db","html_url":"https://github.com/m3db","followers_url":"https://api.github.com/users/m3db/followers","following_url":"https://api.github.com/users/m3db/following{/other_user}","gists_url":"https://api.github.com/users/m3db/gists{/gist_id}","starred_url":"https://api.github.com/users/m3db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/m3db/subscriptions","organizations_url":"https://api.github.com/users/m3db/orgs","repos_url":"https://api.github.com/users/m3db/repos","events_url":"https://api.github.com/users/m3db/events{/privacy}","received_events_url":"https://api.github.com/users/m3db/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/m3db/m3aggregator","description":"Metrics aggregation server","fork":false,"url":"https://api.github.com/repos/m3db/m3aggregator","forks_url":"https://api.github.com/repos/m3db/m3aggregator/forks","keys_url":"https://api.github.com/repos/m3db/m3aggregator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/m3db/m3aggregator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/m3db/m3aggregator/teams","hooks_url":"https://api.github.com/repos/m3db/m3aggregator/hooks","issue_events_url":"https://api.github.com/repos/m3db/m3aggregator/issues/events{/number}","events_url":"https://api.github.com/repos/m3db/m3aggregator/events","assignees_url":"https://api.github.com/repos/m3db/m3aggregator/assignees{/user}","branches_url":"https://api.github.com/repos/m3db/m3aggregator/branches{/branch}","tags_url":"https://api.github.com/repos/m3db/m3aggregator/tags","blobs_url":"https://api.github.com/repos/m3db/m3aggregator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/m3db/m3aggregator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/m3db/m3aggregator/git/refs{/sha}","trees_url":"https://api.github.com/repos/m3db/m3aggregator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/m3db/m3aggregator/statuses/{sha}","languages_url":"https://api.github.com/repos/m3db/m3aggregator/languages","stargazers_url":"https://api.github.com/repos/m3db/m3aggregator/stargazers","contributors_url":"https://api.github.com/repos/m3db/m3aggregator/contributors","subscribers_url":"https://api.github.com/repos/m3db/m3aggregator/subscribers","subscription_url":"https://api.github.com/repos/m3db/m3aggregator/subscription","commits_url":"https://api.github.com/repos/m3db/m3aggregator/commits{/sha}","git_commits_url":"https://api.github.com/repos/m3db/m3aggregator/git/commits{/sha}","comments_url":"https://api.github.com/repos/m3db/m3aggregator/comments{/number}","issue_comment_url":"https://api.github.com/repos/m3db/m3aggregator/issues/comments{/number}","contents_url":"https://api.github.com/repos/m3db/m3aggregator/contents/{+path}","compare_url":"https://api.github.com/repos/m3db/m3aggregator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/m3db/m3aggregator/merges","archive_url":"https://api.github.com/repos/m3db/m3aggregator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/m3db/m3aggregator/downloads","issues_url":"https://api.github.com/repos/m3db/m3aggregator/issues{/number}","pulls_url":"https://api.github.com/repos/m3db/m3aggregator/pulls{/number}","milestones_url":"https://api.github.com/repos/m3db/m3aggregator/milestones{/number}","notifications_url":"https://api.github.com/repos/m3db/m3aggregator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/m3db/m3aggregator/labels{/name}","releases_url":"https://api.github.com/repos/m3db/m3aggregator/releases{/id}","deployments_url":"https://api.github.com/repos/m3db/m3aggregator/deployments","created_at":"2016-11-21T17:58:46Z","updated_at":"2018-03-19T20:12:44Z","pushed_at":"2018-03-23T19:35:16Z","git_url":"git://github.com/m3db/m3aggregator.git","ssh_url":"git@github.com:m3db/m3aggregator.git","clone_url":"https://github.com/m3db/m3aggregator.git","svn_url":"https://github.com/m3db/m3aggregator","homepage":null,"size":811,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":8,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":8,"watchers":3,"default_branch":"master"}},"base":{"label":"m3db:dev","ref":"dev","sha":"93daddc4ca67bdd5cc452e117d80dfbaff0271f2","user":{"login":"m3db","id":19937171,"avatar_url":"https://avatars0.githubusercontent.com/u/19937171?v=4","gravatar_id":"","url":"https://api.github.com/users/m3db","html_url":"https://github.com/m3db","followers_url":"https://api.github.com/users/m3db/followers","following_url":"https://api.github.com/users/m3db/following{/other_user}","gists_url":"https://api.github.com/users/m3db/gists{/gist_id}","starred_url":"https://api.github.com/users/m3db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/m3db/subscriptions","organizations_url":"https://api.github.com/users/m3db/orgs","repos_url":"https://api.github.com/users/m3db/repos","events_url":"https://api.github.com/users/m3db/events{/privacy}","received_events_url":"https://api.github.com/users/m3db/received_events","type":"Organization","site_admin":false},"repo":{"id":74390805,"name":"m3aggregator","full_name":"m3db/m3aggregator","owner":{"login":"m3db","id":19937171,"avatar_url":"https://avatars0.githubusercontent.com/u/19937171?v=4","gravatar_id":"","url":"https://api.github.com/users/m3db","html_url":"https://github.com/m3db","followers_url":"https://api.github.com/users/m3db/followers","following_url":"https://api.github.com/users/m3db/following{/other_user}","gists_url":"https://api.github.com/users/m3db/gists{/gist_id}","starred_url":"https://api.github.com/users/m3db/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/m3db/subscriptions","organizations_url":"https://api.github.com/users/m3db/orgs","repos_url":"https://api.github.com/users/m3db/repos","events_url":"https://api.github.com/users/m3db/events{/privacy}","received_events_url":"https://api.github.com/users/m3db/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/m3db/m3aggregator","description":"Metrics aggregation server","fork":false,"url":"https://api.github.com/repos/m3db/m3aggregator","forks_url":"https://api.github.com/repos/m3db/m3aggregator/forks","keys_url":"https://api.github.com/repos/m3db/m3aggregator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/m3db/m3aggregator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/m3db/m3aggregator/teams","hooks_url":"https://api.github.com/repos/m3db/m3aggregator/hooks","issue_events_url":"https://api.github.com/repos/m3db/m3aggregator/issues/events{/number}","events_url":"https://api.github.com/repos/m3db/m3aggregator/events","assignees_url":"https://api.github.com/repos/m3db/m3aggregator/assignees{/user}","branches_url":"https://api.github.com/repos/m3db/m3aggregator/branches{/branch}","tags_url":"https://api.github.com/repos/m3db/m3aggregator/tags","blobs_url":"https://api.github.com/repos/m3db/m3aggregator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/m3db/m3aggregator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/m3db/m3aggregator/git/refs{/sha}","trees_url":"https://api.github.com/repos/m3db/m3aggregator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/m3db/m3aggregator/statuses/{sha}","languages_url":"https://api.github.com/repos/m3db/m3aggregator/languages","stargazers_url":"https://api.github.com/repos/m3db/m3aggregator/stargazers","contributors_url":"https://api.github.com/repos/m3db/m3aggregator/contributors","subscribers_url":"https://api.github.com/repos/m3db/m3aggregator/subscribers","subscription_url":"https://api.github.com/repos/m3db/m3aggregator/subscription","commits_url":"https://api.github.com/repos/m3db/m3aggregator/commits{/sha}","git_commits_url":"https://api.github.com/repos/m3db/m3aggregator/git/commits{/sha}","comments_url":"https://api.github.com/repos/m3db/m3aggregator/comments{/number}","issue_comment_url":"https://api.github.com/repos/m3db/m3aggregator/issues/comments{/number}","contents_url":"https://api.github.com/repos/m3db/m3aggregator/contents/{+path}","compare_url":"https://api.github.com/repos/m3db/m3aggregator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/m3db/m3aggregator/merges","archive_url":"https://api.github.com/repos/m3db/m3aggregator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/m3db/m3aggregator/downloads","issues_url":"https://api.github.com/repos/m3db/m3aggregator/issues{/number}","pulls_url":"https://api.github.com/repos/m3db/m3aggregator/pulls{/number}","milestones_url":"https://api.github.com/repos/m3db/m3aggregator/milestones{/number}","notifications_url":"https://api.github.com/repos/m3db/m3aggregator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/m3db/m3aggregator/labels{/name}","releases_url":"https://api.github.com/repos/m3db/m3aggregator/releases{/id}","deployments_url":"https://api.github.com/repos/m3db/m3aggregator/deployments","created_at":"2016-11-21T17:58:46Z","updated_at":"2018-03-19T20:12:44Z","pushed_at":"2018-03-23T19:35:16Z","git_url":"git://github.com/m3db/m3aggregator.git","ssh_url":"git@github.com:m3db/m3aggregator.git","clone_url":"https://github.com/m3db/m3aggregator.git","svn_url":"https://github.com/m3db/m3aggregator","homepage":null,"size":811,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":8,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":8,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/92"},"html":{"href":"https://github.com/m3db/m3aggregator/pull/92"},"issue":{"href":"https://api.github.com/repos/m3db/m3aggregator/issues/92"},"comments":{"href":"https://api.github.com/repos/m3db/m3aggregator/issues/92/comments"},"review_comments":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/92/comments"},"review_comment":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/m3db/m3aggregator/pulls/92/commits"},"statuses":{"href":"https://api.github.com/repos/m3db/m3aggregator/statuses/7d422f7338c9012aa338ce88d85b8cecf360a835"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"xichen2020"}}