feat(syncing-server): store per user content size utilization and item operations metrics
This commit is contained in:
parent
709aec5eeb
commit
4dd2eb9349
8 changed files with 45 additions and 0 deletions
|
@ -6,6 +6,8 @@ export class Metric extends ValueObject<MetricProps> {
|
|||
static readonly NAMES = {
|
||||
ItemCreated: 'ItemCreated',
|
||||
ItemUpdated: 'ItemUpdated',
|
||||
ContentSizeUtilized: 'ContentSizeUtilized',
|
||||
ItemOperation: 'ItemOperation',
|
||||
}
|
||||
|
||||
static create(props: MetricProps): Result<Metric> {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Metric } from './Metric'
|
||||
|
||||
export interface MetricsStoreInterface {
|
||||
storeUserBasedMetric(metric: Metric, value: number, userUuid: string): Promise<void>
|
||||
storeMetric(metric: Metric): Promise<void>
|
||||
getStatistics(
|
||||
name: string,
|
||||
|
|
|
@ -27,6 +27,7 @@ describe('SaveNewItem', () => {
|
|||
|
||||
metricsStore = {} as jest.Mocked<MetricsStoreInterface>
|
||||
metricsStore.storeMetric = jest.fn()
|
||||
metricsStore.storeUserBasedMetric = jest.fn()
|
||||
|
||||
item1 = Item.create(
|
||||
{
|
||||
|
|
|
@ -110,6 +110,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
|
|||
return Result.fail(itemOrError.getError())
|
||||
}
|
||||
const newItem = itemOrError.getValue()
|
||||
newItem.props.contentSize = Buffer.byteLength(JSON.stringify(newItem))
|
||||
|
||||
if (dto.itemHash.representsASharedVaultItem()) {
|
||||
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
|
||||
|
@ -138,6 +139,15 @@ export class SaveNewItem implements UseCaseInterface<Item> {
|
|||
|
||||
await this.itemRepository.insert(newItem)
|
||||
|
||||
await this.metricsStore.storeUserBasedMetric(
|
||||
Metric.create({
|
||||
name: Metric.NAMES.ContentSizeUtilized,
|
||||
timestamp: this.timer.getTimestampInMicroseconds(),
|
||||
}).getValue(),
|
||||
newItem.props.contentSize,
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
await this.metricsStore.storeMetric(
|
||||
Metric.create({ name: Metric.NAMES.ItemCreated, timestamp: this.timer.getTimestampInMicroseconds() }).getValue(),
|
||||
)
|
||||
|
|
|
@ -53,6 +53,7 @@ describe('UpdateExistingItem', () => {
|
|||
|
||||
metricsStore = {} as jest.Mocked<MetricsStoreInterface>
|
||||
metricsStore.storeMetric = jest.fn()
|
||||
metricsStore.storeUserBasedMetric = jest.fn()
|
||||
|
||||
item1 = Item.create(
|
||||
{
|
||||
|
|
|
@ -180,6 +180,15 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
|
|||
Metric.create({ name: Metric.NAMES.ItemUpdated, timestamp: this.timer.getTimestampInMicroseconds() }).getValue(),
|
||||
)
|
||||
|
||||
await this.metricsStore.storeUserBasedMetric(
|
||||
Metric.create({
|
||||
name: Metric.NAMES.ContentSizeUtilized,
|
||||
timestamp: this.timer.getTimestampInMicroseconds(),
|
||||
}).getValue(),
|
||||
dto.existingItem.props.contentSize,
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
/* istanbul ignore next */
|
||||
const revisionsFrequency = dto.isFreeUser ? this.freeRevisionFrequency : this.premiumRevisionFrequency
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ import { MetricsStoreInterface } from '../../Domain/Metrics/MetricsStoreInterfac
|
|||
import { Metric } from '../../Domain/Metrics/Metric'
|
||||
|
||||
export class DummyMetricStore implements MetricsStoreInterface {
|
||||
async storeUserBasedMetric(_metric: Metric, _value: number, _userUuid: string): Promise<void> {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
async storeMetric(_metric: Metric): Promise<void> {
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
@ -6,12 +6,29 @@ import { Metric } from '../../Domain/Metrics/Metric'
|
|||
|
||||
export class RedisMetricStore implements MetricsStoreInterface {
|
||||
private readonly METRIC_PREFIX = 'metric'
|
||||
private readonly METRIC_PER_USER_PREFIX = 'metric-user'
|
||||
|
||||
constructor(
|
||||
private redisClient: IORedis.Redis,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async storeUserBasedMetric(metric: Metric, value: number, userUuid: string): Promise<void> {
|
||||
const date = this.timer.convertMicrosecondsToDate(metric.props.timestamp)
|
||||
const dateToTheMinuteString = this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD HH:mm')
|
||||
const key = `${this.METRIC_PER_USER_PREFIX}:${userUuid}:${metric.props.name}:${dateToTheMinuteString}`
|
||||
|
||||
const pipeline = this.redisClient.pipeline()
|
||||
|
||||
pipeline.incrbyfloat(key, value)
|
||||
pipeline.incr(`${this.METRIC_PER_USER_PREFIX}:${userUuid}:${Metric.NAMES.ItemOperation}:${dateToTheMinuteString}`)
|
||||
|
||||
const expirationTime = 60 * 60 * 24
|
||||
pipeline.expire(key, expirationTime)
|
||||
|
||||
await pipeline.exec()
|
||||
}
|
||||
|
||||
async getStatistics(
|
||||
name: string,
|
||||
from: number,
|
||||
|
|
Loading…
Reference in a new issue