CAVE Overview

  • View
    8.597

  • Download
    4

  • Category

    Software

Preview:

DESCRIPTION

A quick overview of CAVE, a managed service for monitoring infrastructure, platform, and application metrics, to provide visibility into your system's performance and operational levels. CAVE is built at GILT, using Scala, Play and Akka.

Citation preview

CAVE - an overviewVal Dumitrescu

Paweł Raszewski

At GILT:● OpenTSDB + Nagios● DataDog● NewRelic● Notifications with PagerDuty

Another monitoring solution?

ContinuousAudit

VaultEnterprise

What is CAVE?

A monitoring system that is:● secure● independent● proprietary● open source

What is CAVE?

● horizontally scalable to millions of metrics, alerts● multi-tenant, multi-user● extensible HTTP-based API● flexible metric definition● data aggregation / multiple dimensions● flexible and extensible alert grammar● pluggable notification delivery system● clean user interface for graphing and dashboarding

Requirements

Architecture

Architecture

Architecture

Architecture

Architecture

Architecture

Architecture

Alert Grammar

Metric has name and tags (key-value pairs)e.g.orders [shipTo: US]response-time [svc: svc-important, env: prod]

Alert Grammar

Aggregated Metric has metric, aggregator and period of aggregation, e.g.orders [shipTo: US].sum.5mresponse-time [svc: svc-important, env: prod].p99.5m

Supported aggregators:count, min, max, mean, mode, median, sumstddev, p99, p999, p95, p90

Alert Grammar

Alert Condition contains one expression with two terms and an operator. Each term is a metric, an aggregated metric or a value.e.g.orders [shipTo: US].sum.5m < 10orders [shipTo: US].sum.5m < ordersPredictedLow [shipTo: US]

Alert Grammar

An optional number of times the threshold is broken, e.g.response-time [svc: svc-team, env: prod].p99.5m > 3000 at least 3 times

Alert Grammar

Special format for missing datae.g.orders [shipTo: US] missing for 5mheartbeat [svc: svc-important, env: prod] missing for 10m

Alert Grammartrait AlertParser extends JavaTokenParsers { sealed trait Source case class ValueSource(value: Double) extends Source case class MetricSource( metric: String, tags: Map[String, String]) extends Source case class AggregatedSource( metricSource: MetricSource, aggregator: Aggregator, duration: FiniteDuration) extends Source

sealed trait AlertEntity case class SimpleAlert( sourceLeft: Source, operator: Operator, sourceRight: Source, times: Int) extends AlertEntity case class MissingDataAlert( metricSource: MetricSource, duration: FiniteDuration) extends AlertEntity …}

Alert Grammartrait AlertParser extends JavaTokenParsers { … def valueSource: Parser[ValueSource] = decimalNumber ^^ { case num => ValueSource(num.toDouble) }

def word: Parser[String] = """[a-zA-Z][a-zA-Z0-9.-]*""".r def metricTag: Parser[(String, String)] = (word <~ ":") ~ word ^^ { case key ~ value => key -> value }

def metricTags: Parser[Map[String, String]] = repsep(metricTag, ",") ^^ { case list => list.toMap } …}

Alert Grammartrait AlertParser extends JavaTokenParsers { … def metricSourceWithTags: Parser[MetricSource] = (word <~ "[") ~ (metricTags <~ "]") ^^ { case metric ~ tagMap => MetricSource(metric, tagMap) }

def metricSourceWithoutTags: Parser[MetricSource] = word ^^ { case metric => MetricSource(metric, Map.empty[String, String]) }

def metricSource = metricSourceWithTags | metricSourceWithoutTags

…}

Alert Grammartrait AlertParser extends JavaTokenParsers { … def duration: Parser[FiniteDuration] = wholeNumber ~ ("s"|"m"|"h"|"d") ^^ { case time ~ "s" => time.toInt.seconds case time ~ "m" => time.toInt.minutes case time ~ "h" => time.toInt.hours case time ~ "d" => time.toInt.days }

def aggregatedSource: Parser[AggregatedSource] = (metricSource <~ ".") ~ (aggregator <~ ".") ~ duration ^^ { case met ~ agg ~ dur => AggregatedSource(met, agg, dur) }

def anySource: Parser[Source] = valueSource | aggregatedSource | metricSource …}

Alert Grammartrait AlertParser extends JavaTokenParsers { … def missingDataAlert: Parser[MissingDataAlert] = metricSource ~ "missing for" ~ duration ^^ { case source ~ _ ~ d => MissingDataAlert(source, d) }

def simpleAlert: Parser[SimpleAlert] = anySource ~ operator ~ anySource ^^ { case left ~ op ~ right => SimpleAlert(left, op, right, 1) }

def repeater: Parser[Int] = "at least" ~ wholeNumber ~ "times" ^^ { case _ ~ num ~ _ => num.toInt } def simpleAlertWithRepeater: Parser[SimpleAlert] = anySource ~ operator ~ anySource ~ repeater ^^ { case left ~ op ~ right ~ num => SimpleAlert(left, op, right, num) }

Alert Grammartrait AlertParser extends JavaTokenParsers { … def anyAlert: Parser[AlertEntity] = missingDataAlert | simpleAlertWithRepeater | simpleAlert}

Usage:class Something(conditionString: String) extends AlertParser {

… parseAll(anyAlert, conditionString) match {

case Success(SimpleAlert(left, op, right, times), _) => … case Success(MissingDataAlert(metric, duration), _) => … case Failure(message, _) => … }}

Functional Relational Mapping (FRM) library for Scala

Slick <> Hibernate

Slick

compile-time safetyno need to write SQL

full control over what is going on

Slick

Scala Collections APIcase class Person(id: Int, name: String)

val list = List(Person(1, "Pawel"),

Person(2, "Val"),

Person(3, "Unknown Name"))

Scala Collections APIcase class Person(id: Int, name: String)

val list = List(Person(1, "Pawel"),

Person(2, "Val"),

Person(3, "Unknown Name"))

list.filter(_.id > 1)

Scala Collections APIcase class Person(id: Int, name: String)

val list = List(Person(1, "Pawel"),

Person(2, "Val"),

Person(3, "Unknown Name"))

list.filter(_.id > 1).map(_.name)

Scala Collections APIcase class Person(id: Int, name: String)

val list = List(Person(1, "Pawel"),

Person(2, "Val"),

Person(3, "Unknown Name"))

list.filter(_.id > 1).map(_.name)

SELECT name FROM list WHERE id > 1

Schema

ORGANIZATIONS TEAMS

Entity mapping/** Table description of table orgs.*/

class OrganizationsTable(tag: Tag) extends Table[OrganizationsRow](tag,"organizations") {

...

/** Database column id AutoInc, PrimaryKey */

val id: Column[Long] = column[Long]("id", O.AutoInc, O.PrimaryKey)

/** Database column name */

val name: Column[String] = column[String]("name")

/** Database column created_at */

val createdAt: Column[java.sql.Timestamp] = column[java.sql.Timestamp]("created_at")

… /** Foreign key referencing Organizations (database name token_organization_fk) */

lazy val organizationsFk = foreignKey("token_organization_fk", organizationId,

Organizations)(r => r.id, onUpdate = ForeignKeyAction.NoAction, onDelete =

ForeignKeyAction.NoAction)

}

CRUDval organizationsTable = TableQuery[OrganizationsTable]

// SELECT * FROM ORGANIZATIONS

organizationsTable.list

// SELECT * FROM ORGANIZATIONS WHERE ID > 10 OFFSET 3 LIMIT 5

organizationsTable.filter(_.id > 10).drop(3).take(5).list

// INSERT organizationsTable += OrganizationsRow(1, "name", "email", "notificationUrl", ... , None,

None)

// UPDATE ORGANIZATIONS SET name = “new org name” WHERE ID=10

organizationsTable.filter(_.id === 10).map(_.name).update("new org name")

// DELETE FROM ORGANIZATIONS WHERE ID=10

organizationsTable.filter(_.id === 10).delete

Queries - JOINSval organizationsTable = TableQuery[OrganizationsTable]

val teamsTable = TableQuery[TeamsTable]

val name = “teamName”

val result = for {

t <- teamsTable.sortBy(_.createdAt).filter(t => t.deletedAt.isEmpty)

o <- t.organization.filter(o => o.deletedAt.isEmpty && o.name === name)

} yield (t.name, o.name)

SELECT t.name, o.name FROM TEAMS t

LEFT JOIN ORGANIZATIONS o ON t.organization_id = o.id

WHERE t.deleted_at IS NULL AND o.deleted_at IS NULL AND o.name = `teamName`

ORDER BY t.created_at

SELECT t.name, o.name FROM TEAMS t

LEFT JOIN ORGANIZATIONS o ON t.organization_id = o.id

WHERE t.deleted_at IS NULL AND o.deleted_at IS NULL AND o.name = `teamName`

ORDER BY t.created_at

val result: List[(String, String)]

Connection pool and transactionsval ds = new BoneCPDataSource

val db = {

ds.setDriverClass(rdsDriver)

ds.setJdbcUrl(rdsJdbcConnectionString)

ds.setPassword(rdsPassword)

ds.setUser(rdsUser)

Database.forDataSource(ds)

}

db.withTransaction { implicit session =>

// SLICK CODE GOES HERE

}