diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderDbStorage.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderDbStorage.kt index c688159..103db8b 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderDbStorage.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderDbStorage.kt @@ -8,13 +8,25 @@ import androidx.room.PrimaryKey import androidx.room.Query import androidx.room.Room import androidx.room.RoomDatabase +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import remote.write.RemoteWrite.TimeSeries -//TODO +/// Room is a relational database +/// Contains the following tables: +/// - Timeseries table: +/// + labels : List sorted alphabetically and encoded in json +/// - Sample table: +/// + id +/// + timestamp +/// + value +/// + Timeseries foreign key @Entity -data class RoomTimeSeries { - -} +data class RoomTimeSeries ( + @PrimaryKey(autoGenerate = false) + val labels : String +) @Entity data class RoomSample( @@ -24,12 +36,16 @@ data class RoomSample( val value : Double, ) +@Serializable +data class TimeSeriesLabelList( + val labels: List +) + @Database( entities = [RoomTimeSeries::class, RoomSample::class], version = 1 ) abstract class RemoteWriteDatabase: RoomDatabase() { - abstract val dao: RoomDao } @@ -41,7 +57,7 @@ interface RoomDao { } @Query("") //TODO - fun getNumberOfTimeSeriesSamples(){ + fun getNumberOfTimeSeriesSamples(number : Int){ } @@ -53,31 +69,27 @@ interface RoomDao { } class RemoteWriteSenderDbStorage(getContext: () -> Context) : RemoteWriteSenderStorage(){ + companion object{ + const val dbName = "prometheus.db" + } private val roomDb by lazy { Room.databaseBuilder( getContext(), RemoteWriteDatabase::class.java, - "contacts.db" + dbName, ).build() } private fun encodeLabels(labelsList: List) : String{ - //TODO - var result : String = "" - for (label in labelsList){ - // check if label contains escape character - if (label.name.contains("-") || label.value.contains("-")){ - throw IllegalArgumentException("Time series labels should not contain \'-\'") - }else{ - //TODO - } - } - return result + /// preserve the same order + val sorted : List = labelsList.sortedBy { it.name } + val timeSeriesLabelList = TimeSeriesLabelList(labels = sorted) + return Json.encodeToString(TimeSeriesLabelList.serializer(), timeSeriesLabelList) } private fun decodeLabels(labels : String) : List { - //TODO + return Json.decodeFromString(labels).labels } override fun writeScrapedSample(metricsScrape: MetricsScrape) { TODO("Not yet implemented") @@ -98,5 +110,4 @@ class RemoteWriteSenderDbStorage(getContext: () -> Context) : RemoteWriteSenderS override fun getLength(): Int { TODO("Not yet implemented") } - } \ No newline at end of file diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderMemStorage.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderMemStorage.kt index 83f595b..901bf7b 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderMemStorage.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderMemStorage.kt @@ -8,11 +8,17 @@ import java.util.Queue // HashMap private typealias ConverterHashMap = HashMap, MutableList> -class RemoteWriteSenderMemStorage : RemoteWriteSenderStorage() { +private const val TAG : String = "REMOTE_WRITE_SENDER_MEMORY_SIMPLE_STORAGE"; + + +//TODO sort this out + +class RemoteWriteSenderSimpleMemoryStorage : RemoteWriteSenderStorage() { + private val data: Queue = LinkedList() private fun filterExpiredMetrics(metrics : MutableList){ val now : Long = System.currentTimeMillis() - val oldestMetricTimeMs : Long = now() - maxMetricsAge * 1000 + val oldestMetricTimeMs : Long = now - maxMetricsAge * 1000 var howManyMetricsRemove : Int = 0 // count how many metrics to remove @@ -33,10 +39,9 @@ class RemoteWriteSenderMemStorage : RemoteWriteSenderStorage() { } } - private fun hashMapEntryToProtobufTimeSeries( labels: List, samples: MutableList - ): TimeSeries { + ): RemoteWrite.TimeSeries { val timeSeriesBuilder: RemoteWrite.TimeSeries.Builder = RemoteWrite.TimeSeries.newBuilder() @@ -51,8 +56,8 @@ class RemoteWriteSenderMemStorage : RemoteWriteSenderStorage() { return timeSeriesBuilder.build() } - private fun hashmapToProtobufWriteRequest(hashMap: ConverterHashMap): WriteRequest { - val writeRequestBuilder: WriteRequest.Builder = WriteRequest.newBuilder() + private fun hashmapToProtobufWriteRequest(hashMap: ConverterHashMap): RemoteWrite.WriteRequest { + val writeRequestBuilder: RemoteWrite.WriteRequest.Builder = RemoteWrite.WriteRequest.newBuilder() for (entry in hashMap) { val timeSeries = hashMapEntryToProtobufTimeSeries(entry.key, entry.value) @@ -62,6 +67,47 @@ class RemoteWriteSenderMemStorage : RemoteWriteSenderStorage() { return writeRequestBuilder.build() } + override fun getScrapedSamplesCompressedProtobuf(howMany: Int): ByteArray { + if (howMany < 1) { + throw IllegalArgumentException("howMany must be bigger than zero") + } + + val scrapedMetrics: MutableList = mutableListOf() + for (i in 1..howMany) { + val oneMetric: MetricsScrape? = data.poll() + if (oneMetric == null) { + break + } else { + scrapedMetrics.add(oneMetric) + } + } + Log.d(TAG, "Getting scraped samples: ${scrapedMetrics.size} samples") + + filterExpiredMetrics(scrapedMetrics) + + val writeRequest: RemoteWrite.WriteRequest = metricsScrapeListToProtobuf(scrapedMetrics.toList()) + val bytes: ByteArray = writeRequest.toByteArray() + return RemoteWriteSenderStorage.encodeWithSnappy(bytes) + } + + private fun metricsScrapeListToProtobuf(input: List): RemoteWrite.WriteRequest { + if (input.isEmpty()) { + throw Exception("Input is empty!") + } + + val hashmap: ConverterHashMap = HashMap() + + for (metricsScrape in input) { + for (timeSeries in metricsScrape.timeSeriesList){ + processStorageTimeSeries(hashmap, timeSeries) + } + } + + val result: RemoteWrite.WriteRequest = hashmapToProtobufWriteRequest(hashmap) + + return result + } + private fun processStorageTimeSeries(hashMap: ConverterHashMap, timeSeries: StorageTimeSeries){ // add remote write label to labels @@ -80,107 +126,6 @@ class RemoteWriteSenderMemStorage : RemoteWriteSenderStorage() { } } - private fun metricsScrapeListToProtobuf(input: List): WriteRequest { - if (input.isEmpty()) { - throw Exception("Input is empty!") - } - - val hashmap: ConverterHashMap = HashMap() - - for (metricsScrape in input) { - for (timeSeries in metricsScrape.timeSeriesList){ - processStorageTimeSeries(hashmap, timeSeries) - } - } - - val result: WriteRequest = hashmapToProtobufWriteRequest(hashmap) - - return result - } - - - private val data: Queue = LinkedList() - - override fun getScrapedSamplesCompressedProtobuf(howMany: Int): ByteArray { - if (howMany < 1) { - throw IllegalArgumentException("howMany must be bigger than zero") - } - - val scrapedMetrics: MutableList = mutableListOf() - for (i in 1..howMany) { - val oneMetric: MetricsScrape? = data.poll() - if (oneMetric == null) { - break - } else { - scrapedMetrics.add(oneMetric) - } - } - Log.d(TAG, "Getting scraped samples: ${scrapedMetrics.size} samples") - - filterExpiredMetrics(scrapedMetrics) - - val writeRequest: WriteRequest = this.metricsScrapeListToProtobuf(scrapedMetrics.toList()) - val bytes: ByteArray = writeRequest.toByteArray() - return this.encodeWithSnappy(bytes) - } - - override fun removeNumberOfScrapedSamples(number: Int) { - if (number > 0) { - for (i in 1..number) { - if(data.isEmpty()){ - break; - }else{ - data.remove() - } - } - } else { - throw IllegalArgumentException("number must by higher than 0") - } - } - - override fun writeScrapedSample(metricsScrape: MetricsScrape) { - Log.d(TAG, "Writing scraped sample to storage") - data.add(metricsScrape) - } - - override fun isEmpty(): Boolean { - return data.isEmpty() - } - - override fun getLength(): Int { - return data.count() - } -} - - -//TODO sort this out - -class RemoteWriteSenderSimpleMemoryStorage : RemoteWriteSenderStorage() { - private val data: Queue = LinkedList() - - override fun getScrapedSamplesCompressedProtobuf(howMany: Int): ByteArray { - if (howMany < 1) { - throw IllegalArgumentException("howMany must be bigger than zero") - } - - val scrapedMetrics: MutableList = mutableListOf() - for (i in 1..howMany) { - val oneMetric: MetricsScrape? = data.poll() - if (oneMetric == null) { - break - } else { - scrapedMetrics.add(oneMetric) - } - } - Log.d(TAG, "Getting scraped samples: ${scrapedMetrics.size} samples") - - filterExpiredMetrics(scrapedMetrics) - - val writeRequest: RemoteWrite.WriteRequest = this.metricsScrapeListToProtobuf(scrapedMetrics.toList()) - val bytes: ByteArray = writeRequest.toByteArray() - return this.encodeWithSnappy(bytes) - } - //TODO use this thing override fun removeNumberOfScrapedSamples(number: Int) { if (number > 0) { diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderStorage.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderStorage.kt index d806601..f50a006 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderStorage.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/RemoteWriteSenderStorage.kt @@ -2,6 +2,7 @@ package com.birdthedeveloper.prometheus.android.prometheus.android.exporter.work import android.util.Log import io.prometheus.client.Collector.MetricFamilySamples +import kotlinx.serialization.Serializable import org.iq80.snappy.Snappy import remote.write.RemoteWrite.Label import remote.write.RemoteWrite.Sample @@ -78,6 +79,7 @@ data class StorageTimeSeries( val labels : List, ) +@Serializable data class TimeSeriesLabel( val name: String, val value: String,