blob: 6f2bb55f2823613260ace3ae90fa0482f2025fc6 [file]
// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tsmon
import (
"fmt"
"time"
"golang.org/x/net/context"
ds "github.com/luci/gae/service/datastore"
"github.com/luci/gae/service/info"
"github.com/luci/luci-go/common/clock"
)
const (
// targetDataCenter is the value set on the "data_center" field in the
// ts_mon.proto.Task message.
targetDataCenter = "appengine"
// instanceNamespace is the namespace to use for datastore instances.
instanceNamespace = "ts_mon_instance_namespace"
// prodXEndpoint is endpoint to send metrics to.
prodXEndpoint = "https://prodxmon-pa.googleapis.com/v1:insert"
instanceExpirationTimeout = 30 * time.Minute
instanceExpectedToHaveTaskNum = 5 * time.Minute
flushTimeout = 5 * time.Second
)
type instance struct {
_kind string `gae:"$kind,Instance"`
ID string `gae:"$id"`
TaskNum int `gae:"task_num"` // Field names should match Python
LastUpdated time.Time `gae:"last_updated"` // implementation.
}
// instanceEntityID returns a string unique to this appengine module, version
// and instance, to be used as the datastore ID for an "instance" entity.
func instanceEntityID(c context.Context) string {
return fmt.Sprintf("%s.%s.%s", info.InstanceID(c), info.VersionID(c), info.ModuleName(c))
}
// getOrCreateInstanceEntity returns the instance entity for this appengine
// instance, adding a default one to the datastore if it doesn't exist.
//
// We need to register an entity ASAP to allow housekeepingHandler to
// discover the new instance.
func getOrCreateInstanceEntity(c context.Context) (*instance, error) {
entity := instance{
ID: instanceEntityID(c),
TaskNum: -1,
LastUpdated: clock.Get(c).Now().UTC(),
}
err := ds.Get(c, &entity)
if err == ds.ErrNoSuchEntity {
err = ds.RunInTransaction(c, func(c context.Context) error {
switch err := ds.Get(c, &entity); err {
case nil:
return nil
case ds.ErrNoSuchEntity:
// Insert it into datastore if it didn't exist.
return ds.Put(c, &entity)
default:
return err
}
}, nil)
}
return &entity, err
}
// refreshLastUpdatedTime updates LastUpdated field in the instance entity.
//
// It does it in a transaction to avoid overwriting TaskNum.
func refreshLastUpdatedTime(c context.Context, t time.Time) error {
entity := instance{ID: instanceEntityID(c)}
return ds.RunInTransaction(c, func(c context.Context) error {
if err := ds.Get(c, &entity); err != nil {
return err
}
entity.LastUpdated = t.UTC()
return ds.Put(c, &entity)
}, nil)
}