From: Christos KK Loverdos Date: Fri, 8 Jun 2012 12:11:50 +0000 (+0300) Subject: WIP Resource event handling X-Git-Url: https://code.grnet.gr/git/aquarium/commitdiff_plain/6453d1cfda6abdbad4d3a9112e17a95931604d88 WIP Resource event handling --- diff --git a/pom.xml b/pom.xml index 9daddfa..d02b3d7 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,6 @@ org.scala-lang scala-compiler ${scala.version} - test junit @@ -361,6 +360,9 @@ ${basedir}/src/main/resources + + aquarium.properties + ${project.build.directory}/generated-resources diff --git a/src/main/resources/aquarium.properties b/src/main/resources/aquarium.properties index aad478b..eab7427 100644 --- a/src/main/resources/aquarium.properties +++ b/src/main/resources/aquarium.properties @@ -64,13 +64,14 @@ mongodb.dbschema=aquarium mongodb.connection.pool.size=20 # Relative to AQUARIUM_HOME or an absolute path -events.store.folder=../events-store +# DO NOT set this in production +#events.store.folder=../events-store # Store resource events to events.store.folder as well -events.store.save.rc.events=true +events.store.save.rc.events=false # Store IM events to events.store.folder as well -events.store.save.im.events=true +events.store.save.im.events=false # How often do we attempt a reconnection to the store(s)? anystore.reconnect.period.millis=1000 diff --git a/src/main/scala/gr/grnet/aquarium/Aquarium.scala b/src/main/scala/gr/grnet/aquarium/Aquarium.scala index 8654577..1c6b64b 100644 --- a/src/main/scala/gr/grnet/aquarium/Aquarium.scala +++ b/src/main/scala/gr/grnet/aquarium/Aquarium.scala @@ -52,6 +52,7 @@ import gr.grnet.aquarium.computation.UserStateComputations import gr.grnet.aquarium.logic.accounting.algorithm.{SimpleCostPolicyAlgorithmCompiler, CostPolicyAlgorithmCompiler} import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap import gr.grnet.aquarium.logic.accounting.Policy +import org.slf4j.{Logger, LoggerFactory} /** * This is the Aquarium entry point. @@ -67,16 +68,27 @@ final class Aquarium(val props: Props) extends Lifecycle with Loggable { aquariu def isStopping() = _isStopping.get() + @inline + def getClientLogger(client: AnyRef): Logger = { + client match { + case null ⇒ + this.logger + + case _ ⇒ + LoggerFactory.getLogger(client.getClass) + } + } + def debug(client: AnyRef, fmt: String, args: Any*) = { - logger.debug("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*))) + getClientLogger(client).debug(fmt.format(args: _*)) } def info(client: AnyRef, fmt: String, args: Any*) = { - logger.info("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*))) + getClientLogger(client).info(fmt.format(args: _*)) } def warn(client: AnyRef, fmt: String, args: Any*) = { - logger.warn("[%s] %s".format(shortClassNameOf(client), fmt.format(args: _*))) + getClientLogger(client).warn(fmt.format(args: _*)) } /** @@ -569,9 +581,9 @@ object Aquarium { final val aquarium_role_agreement_map = "aquarium.role-agreement.map" /** - * A time period in milliseconds for which we can tolerate stale data regarding user state. + * A time period in milliseconds for which we can tolerate stale parts regarding user state. * - * The smaller the value, the more accurate the user credits and other state data are. + * The smaller the value, the more accurate the user credits and other state parts are. * * If a request for user state (e.g. balance) is received and the request timestamp exceeds * the timestamp of the last known balance amount by this value, then a re-computation for diff --git a/src/main/scala/gr/grnet/aquarium/DataSnapshot.scala b/src/main/scala/gr/grnet/aquarium/DataSnapshot.scala index a41f19c..53e69a8 100644 --- a/src/main/scala/gr/grnet/aquarium/DataSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/DataSnapshot.scala @@ -36,15 +36,15 @@ package gr.grnet.aquarium /** - * Timely snapshots of data. + * Timely snapshots of parts. * - * We are interested only on the time aspect here, the data part is left open to the implementors. + * We are interested only on the time aspect here, the parts part is left open to the implementors. * * @author Christos KK Loverdos */ trait DataSnapshot { /** - * The unix time (millis) when the data was actually calculated/obtained. + * The unix time (millis) when the parts was actually calculated/obtained. */ def snapshotTime: Long } \ No newline at end of file diff --git a/src/main/scala/gr/grnet/aquarium/actor/message/GetUserStateResponse.scala b/src/main/scala/gr/grnet/aquarium/actor/message/GetUserStateResponse.scala index 1d4904c..b506e49 100644 --- a/src/main/scala/gr/grnet/aquarium/actor/message/GetUserStateResponse.scala +++ b/src/main/scala/gr/grnet/aquarium/actor/message/GetUserStateResponse.scala @@ -35,7 +35,7 @@ package gr.grnet.aquarium.actor.message -import gr.grnet.aquarium.computation.UserState +import gr.grnet.aquarium.computation.state.UserState /** * diff --git a/src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala b/src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala index 7c16fb8..95d04fe 100644 --- a/src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala +++ b/src/main/scala/gr/grnet/aquarium/actor/service/user/UserActor.scala @@ -41,15 +41,17 @@ import gr.grnet.aquarium.actor._ import akka.config.Supervision.Temporary import gr.grnet.aquarium.actor.message.event.{ProcessResourceEvent, ProcessIMEvent} -import gr.grnet.aquarium.computation.data.IMStateSnapshot import gr.grnet.aquarium.actor.message.config.{InitializeUserState, ActorProviderConfigured, AquariumPropertiesLoaded} -import gr.grnet.aquarium.computation.{BillingMonthInfo, UserStateBootstrappingData, UserState} import gr.grnet.aquarium.util.date.TimeHelpers import gr.grnet.aquarium.event.model.im.IMEventModel import gr.grnet.aquarium.actor.message.{GetUserStateResponse, GetUserBalanceResponseData, GetUserBalanceResponse, GetUserStateRequest, GetUserBalanceRequest} import gr.grnet.aquarium.util.{LogHelpers, shortClassNameOf, shortNameOfClass, shortNameOfType} -import gr.grnet.aquarium.computation.reason.{RealtimeBillingCalculation, NoSpecificChangeReason, InitialUserActorSetup, UserStateChangeReason, IMEventArrival, InitialUserStateSetup} -import gr.grnet.aquarium.{AquariumInternalError, AquariumException, Aquarium} +import gr.grnet.aquarium.computation.reason.{RealtimeBillingCalculation, InitialUserActorSetup, UserStateChangeReason, IMEventArrival, InitialUserStateSetup} +import gr.grnet.aquarium.{AquariumInternalError, Aquarium} +import gr.grnet.aquarium.computation.state.parts.IMStateSnapshot +import gr.grnet.aquarium.computation.BillingMonthInfo +import gr.grnet.aquarium.computation.state.{UserStateBootstrap, UserState} +import gr.grnet.aquarium.event.model.resource.ResourceEventModel /** * @@ -82,8 +84,13 @@ class UserActor extends ReflectiveRoleableActor { def role = UserActorRole private[this] def aquarium: Aquarium = Aquarium.Instance + private[this] def userStateComputations = aquarium.userStateComputations + private[this] def stdUserStateStoreFunc = (userState: UserState) ⇒ { + aquarium.userStateStore.insertUserState(userState) + } + private[this] def _timestampTheshold = { aquarium.props.getLong(Aquarium.Keys.user_state_timestamp_threshold).getOr(1000L * 60 * 5 /* 5 minutes */) } @@ -157,7 +164,7 @@ class UserActor extends ReflectiveRoleableActor { val userCreationMillis = this._imState.userCreationMillis.get val initialRole = this._imState.roleHistory.firstRole.get.name - val userStateBootstrap = UserStateBootstrappingData( + val userStateBootstrap = UserStateBootstrap( this._userID, userCreationMillis, initialRole, @@ -165,13 +172,14 @@ class UserActor extends ReflectiveRoleableActor { aquarium.initialBalanceForRole(initialRole, userCreationMillis) ) - val userState = userStateComputations.doBillingForMonth( + val now = TimeHelpers.nowMillis() + val userState = userStateComputations.doMonthBillingUpTo( + BillingMonthInfo.fromMillis(now), + now, userStateBootstrap, - BillingMonthInfo.fromMillis(TimeHelpers.nowMillis()), - false, - TimeHelpers.nowMillis(), aquarium.currentResourcesMap, - InitialUserStateSetup(None), + InitialUserActorSetup(), + stdUserStateStoreFunc, None ) @@ -221,12 +229,8 @@ class UserActor extends ReflectiveRoleableActor { * call this. */ private[this] def newUserStateWithUpdatedRoleHistory(stateChangeReason: UserStateChangeReason): UserState = { - this._userState.copy( - roleHistory = this._imState.roleHistory, - // FIXME: Also update agreement - stateChangeCounter = this._userState.stateChangeCounter + 1, - lastChangeReason = stateChangeReason - ) + // FIXME: Also update agreement + this._userState.newWithRoleHistory(this._imState.roleHistory, stateChangeReason) } /** @@ -317,26 +321,27 @@ class UserActor extends ReflectiveRoleableActor { val initialRole = this._imState.roleHistory.firstRoleName.getOrElse(aquarium.defaultInitialUserRole) val initialAgreement = aquarium.initialAgreementForRole(initialRole, userCreationMillis) val initialCredits = aquarium.initialBalanceForRole(initialRole, userCreationMillis) - val userStateBootstrap = UserStateBootstrappingData( + val userStateBootstrap = UserStateBootstrap( userID, userCreationMillis, initialRole, initialAgreement, initialCredits ) - val billingMonthInfoNow =BillingMonthInfo.fromMillis(now) + val billingMonthInfo = BillingMonthInfo.fromMillis(now) val currentResourcesMap = aquarium.currentResourcesMap val calculationReason = RealtimeBillingCalculation(None, now) + val eventOccurredMillis = rcEvent.occurredMillis DEBUG("Using %s", currentResourcesMap) - this._userState = aquarium.userStateComputations.doBillingForMonth( + this._userState = aquarium.userStateComputations.doMonthBillingUpTo( + billingMonthInfo, + now max eventOccurredMillis, // take into account that the event may be out-of-sync userStateBootstrap, - billingMonthInfoNow, - false, - now, currentResourcesMap, - calculationReason + calculationReason, + stdUserStateStoreFunc ) this._latestResourceEventOccurredMillis = event.rcEvent.occurredMillis diff --git a/src/main/scala/gr/grnet/aquarium/computation/BillingMonthInfo.scala b/src/main/scala/gr/grnet/aquarium/computation/BillingMonthInfo.scala index 69ca62e..57d6e96 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/BillingMonthInfo.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/BillingMonthInfo.scala @@ -36,7 +36,7 @@ package gr.grnet.aquarium.computation import gr.grnet.aquarium.util.shortClassNameOf -import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc} +import gr.grnet.aquarium.util.date.MutableDateCalc /** * Provides information about the billing month and related calculation utilities. @@ -91,6 +91,8 @@ final class BillingMonthInfo( override def toString = "%s(%s-%02d-01)".format(shortClassNameOf(this), year, month) + def toDebugString = toString + def toShortDebugString = "%s-%02d-01".format(year, month) } diff --git a/src/main/scala/gr/grnet/aquarium/computation/UserStateComputations.scala b/src/main/scala/gr/grnet/aquarium/computation/UserStateComputations.scala index bd3262d..ca613d1 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/UserStateComputations.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/UserStateComputations.scala @@ -39,11 +39,12 @@ import scala.collection.mutable import gr.grnet.aquarium.util.{ContextualLogger, Loggable} import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc} import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap -import gr.grnet.aquarium.computation.data._ +import gr.grnet.aquarium.computation.state.parts._ import gr.grnet.aquarium.event.model.NewWalletEntry import gr.grnet.aquarium.event.model.resource.ResourceEventModel import gr.grnet.aquarium.{Aquarium, AquariumInternalError} import gr.grnet.aquarium.computation.reason.{MonthlyBillingCalculation, InitialUserStateSetup, UserStateChangeReason} +import gr.grnet.aquarium.computation.state.{UserStateWorker, UserStateBootstrap, UserState} /** * @@ -52,19 +53,22 @@ import gr.grnet.aquarium.computation.reason.{MonthlyBillingCalculation, InitialU final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { lazy val aquarium = _aquarium - lazy val storeProvider = aquarium.storeProvider - lazy val timeslotComputations = new TimeslotComputations {} - lazy val algorithmCompiler = aquarium.algorithmCompiler - lazy val policyStore = storeProvider.policyStore - lazy val userStateStore = storeProvider.userStateStore - lazy val resourceEventStore = storeProvider.resourceEventStore - - def findUserStateAtEndOfBillingMonth(userStateBootstrap: UserStateBootstrappingData, - billingMonthInfo: BillingMonthInfo, - billingTimeMillis: Long, - defaultResourcesMap: DSLResourcesMap, - calculationReason: UserStateChangeReason, - clogOpt: Option[ContextualLogger] = None): UserState = { + + lazy val storeProvider = aquarium.storeProvider + lazy val timeslotComputations = new TimeslotComputations {} + lazy val algorithmCompiler = aquarium.algorithmCompiler + lazy val policyStore = storeProvider.policyStore + lazy val userStateStoreForRead = storeProvider.userStateStore + lazy val resourceEventStore = storeProvider.resourceEventStore + + def findUserStateAtEndOfBillingMonth( + userStateBootstrap: UserStateBootstrap, + billingMonthInfo: BillingMonthInfo, + defaultResourcesMap: DSLResourcesMap, + calculationReason: UserStateChangeReason, + storeFunc: UserState ⇒ UserState, + clogOpt: Option[ContextualLogger] = None + ): UserState = { val clog = ContextualLogger.fromOther( clogOpt, @@ -72,14 +76,24 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { "findUserStateAtEndOfBillingMonth(%s)", billingMonthInfo.toShortDebugString) clog.begin() - def computeFullMonthlyBilling(): UserState = { - doFullMonthlyBilling( + def computeFullMonthBillingAndSaveState(): UserState = { + val userState0 = doFullMonthBilling( userStateBootstrap, billingMonthInfo, - billingTimeMillis, defaultResourcesMap, - MonthlyBillingCalculation(calculationReason, billingMonthInfo), - Some(clog)) + calculationReason, + storeFunc, + Some(clog) + ) + + // We always save the state when it is a full month billing + val userState1 = storeFunc.apply( + userState0.newWithChangeReason( + MonthlyBillingCalculation(calculationReason, billingMonthInfo)) + ) + + clog.debug("Stored full %s %s", billingMonthInfo.toDebugString, userState1.toJsonString) + userState1 } val userID = userStateBootstrap.userID @@ -92,30 +106,33 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { // If the user did not exist for this billing month, piece of cake clog.debug("User did not exist before %s", userCreationDateCalc) + // TODO: The initial user state might have already been created. + // First ask if it exists and compute only if not val initialUserState0 = UserState.createInitialUserStateFromBootstrap( userStateBootstrap, TimeHelpers.nowMillis(), InitialUserStateSetup(Some(calculationReason)) // we record the originating calculation reason ) - val initialUserState1 = userStateStore.insertUserState(initialUserState0) - clog.debug("Initial state %s".format(initialUserState1)) + // We always save the initial state + val initialUserState1 = storeFunc.apply(initialUserState0) + + clog.debug("Stored initial state = %s", initialUserState1.toJsonString) clog.end() return initialUserState1 } // Ask DB cache for the latest known user state for this billing period - val latestUserStateOpt = userStateStore.findLatestUserStateForEndOfBillingMonth( + val latestUserStateOpt = userStateStoreForRead.findLatestUserStateForFullMonthBilling( userID, - billingMonthInfo.year, - billingMonthInfo.month) + billingMonthInfo) latestUserStateOpt match { case None ⇒ // Not found, must compute clog.debug("No user state found from cache, will have to (re)compute") - val result = computeFullMonthlyBilling + val result = computeFullMonthBillingAndSaveState clog.end() result @@ -139,7 +156,7 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { case n if n > 0 ⇒ clog.debug( "Found %s out of sync events (%s more), will have to (re)compute user state", actualOOSEventsCounter, n) - val result = computeFullMonthlyBilling + val result = computeFullMonthBillingAndSaveState clog.end() result @@ -158,13 +175,15 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { } //- Utility methods - def processResourceEvent(startingUserState: UserState, - userStateWorker: UserStateWorker, - currentResourceEvent: ResourceEventModel, - stateChangeReason: UserStateChangeReason, - billingMonthInfo: BillingMonthInfo, - walletEntriesBuffer: mutable.Buffer[NewWalletEntry], - clogOpt: Option[ContextualLogger] = None): UserState = { + def processResourceEvent( + startingUserState: UserState, + userStateWorker: UserStateWorker, + currentResourceEvent: ResourceEventModel, + stateChangeReason: UserStateChangeReason, + billingMonthInfo: BillingMonthInfo, + walletEntriesBuffer: mutable.Buffer[NewWalletEntry], + clogOpt: Option[ContextualLogger] = None + ): UserState = { val clog = ContextualLogger.fromOther(clogOpt, logger, "processResourceEvent(%s)", currentResourceEvent.id) @@ -297,13 +316,15 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { _workingUserState } - def processResourceEvents(resourceEvents: Traversable[ResourceEventModel], - startingUserState: UserState, - userStateWorker: UserStateWorker, - stateChangeReason: UserStateChangeReason, - billingMonthInfo: BillingMonthInfo, - walletEntriesBuffer: mutable.Buffer[NewWalletEntry], - clogOpt: Option[ContextualLogger] = None): UserState = { + def processResourceEvents( + resourceEvents: Traversable[ResourceEventModel], + startingUserState: UserState, + userStateWorker: UserStateWorker, + stateChangeReason: UserStateChangeReason, + billingMonthInfo: BillingMonthInfo, + walletEntriesBuffer: mutable.Buffer[NewWalletEntry], + clogOpt: Option[ContextualLogger] = None + ): UserState = { var _workingUserState = startingUserState @@ -323,55 +344,66 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { _workingUserState } - def doFullMonthlyBilling(userStateBootstrap: UserStateBootstrappingData, - billingMonthInfo: BillingMonthInfo, - billingTimeMillis: Long, - defaultResourcesMap: DSLResourcesMap, - calculationReason: UserStateChangeReason, - clogOpt: Option[ContextualLogger] = None): UserState = { - doBillingForMonth( - userStateBootstrap, + def doFullMonthBilling( + userStateBootstrap: UserStateBootstrap, + billingMonthInfo: BillingMonthInfo, + defaultResourcesMap: DSLResourcesMap, + calculationReason: UserStateChangeReason, + storeFunc: UserState ⇒ UserState, + clogOpt: Option[ContextualLogger] = None + ): UserState = { + + doMonthBillingUpTo( billingMonthInfo, - true, - billingTimeMillis, + billingMonthInfo.monthStopMillis, + userStateBootstrap, defaultResourcesMap, calculationReason, + storeFunc, clogOpt ) } - def doBillingForMonth(userStateBootstrap: UserStateBootstrappingData, - billingMonthInfo: BillingMonthInfo, - fullBillingMonthState: Boolean, // See UserState#isFullBillingMonthState - billingTimeMillis: Long, // See UserState$occurredMillis - defaultResourcesMap: DSLResourcesMap, - calculationReason: UserStateChangeReason, - clogOpt: Option[ContextualLogger] = None): UserState = { - + def doMonthBillingUpTo( + /** + * Which month to bill. + */ + billingMonthInfo: BillingMonthInfo, + /** + * Bill from start of month up to (and including) this time. + */ + billingEndTimeMillis: Long, + userStateBootstrap: UserStateBootstrap, + defaultResourcesMap: DSLResourcesMap, + calculationReason: UserStateChangeReason, + storeFunc: UserState ⇒ UserState, + clogOpt: Option[ContextualLogger] = None + ): UserState = { + + val isFullMonthBilling = billingEndTimeMillis == billingMonthInfo.monthStopMillis val userID = userStateBootstrap.userID val clog = ContextualLogger.fromOther( clogOpt, logger, - "doFullMonthlyBilling(%s)", billingMonthInfo.toShortDebugString) + "doMonthBillingUpTo(%s)", new MutableDateCalc(billingEndTimeMillis).toYYYYMMDDHHMMSSSSS) clog.begin() + clog.debug("calculationReason = %s", calculationReason) + val clogSome = Some(clog) val previousBillingMonthUserState = findUserStateAtEndOfBillingMonth( userStateBootstrap, billingMonthInfo.previousMonth, - billingTimeMillis, defaultResourcesMap, - calculationReason.forBillingMonthInfo(billingMonthInfo.previousMonth), + calculationReason, + storeFunc, clogSome ) val startingUserState = previousBillingMonthUserState - val billingMonthStartMillis = billingMonthInfo.monthStartMillis - val billingMonthEndMillis = billingMonthInfo.monthStopMillis - // Keep the working (current) user state. This will get updated as we proceed with billing for the month // specified in the parameters. // NOTE: The calculation reason is not the one we get from the previous user state but the one our caller specifies @@ -384,8 +416,9 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { // First, find and process the actual resource events from DB val allResourceEventsForMonth = resourceEventStore.findAllRelevantResourceEventsForBillingPeriod( userID, - billingMonthStartMillis, - billingMonthEndMillis) + billingMonthInfo.monthStartMillis, // from start of month + billingEndTimeMillis // to requested time + ) val newWalletEntries = scala.collection.mutable.ListBuffer[NewWalletEntry]() @@ -399,43 +432,46 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { clogSome ) - // Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs - // ... in order to generate an implicit ON later - val (specialEvents, theirImplicitEnds) = userStateWorker. - findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthEndMillis) - if(specialEvents.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) { - clog.debug("") - clog.debug("Process implicitly issued events") - clog.debugSeq("specialEvents", specialEvents, 0) - clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0) - } - - // Now, the previous and implicitly started must be our base for the following computation, so we create an - // appropriate worker - val specialUserStateWorker = UserStateWorker( - userStateWorker.userID, - LatestResourceEventsWorker.fromList(specialEvents), - ImplicitlyIssuedResourceEventsWorker.Empty, - IgnoredFirstResourceEventsWorker.Empty, - userStateWorker.resourcesMap - ) + if(isFullMonthBilling) { + // Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs + // ... in order to generate an implicit ON later (during the next billing cycle). + val (specialEvents, theirImplicitEnds) = userStateWorker. + findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthInfo.monthStopMillis) + + if(specialEvents.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) { + clog.debug("") + clog.debug("Process implicitly issued events") + clog.debugSeq("specialEvents", specialEvents, 0) + clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0) + } + + // Now, the previous and implicitly started must be our base for the following computation, so we create an + // appropriate worker + val specialUserStateWorker = UserStateWorker( + userStateWorker.userID, + LatestResourceEventsWorker.fromList(specialEvents), + ImplicitlyIssuedResourceEventsWorker.Empty, + IgnoredFirstResourceEventsWorker.Empty, + userStateWorker.resourcesMap + ) - _workingUserState = processResourceEvents( - theirImplicitEnds, - _workingUserState, - specialUserStateWorker, - calculationReason, - billingMonthInfo, - newWalletEntries, - clogSome - ) + _workingUserState = processResourceEvents( + theirImplicitEnds, + _workingUserState, + specialUserStateWorker, + calculationReason, + billingMonthInfo, + newWalletEntries, + clogSome + ) + } val lastUpdateTime = TimeHelpers.nowMillis() _workingUserState = _workingUserState.copy( - isFullBillingMonthState = fullBillingMonthState, + isFullBillingMonthState = isFullMonthBilling, - theFullBillingMonth = if(fullBillingMonthState) + theFullBillingMonth = if(isFullMonthBilling) Some(billingMonthInfo) else _workingUserState.theFullBillingMonth, @@ -451,15 +487,6 @@ final class UserStateComputations(_aquarium: => Aquarium) extends Loggable { newWalletEntries = newWalletEntries.toList ) - clog.debug("calculationReason = %s", calculationReason) - - if(calculationReason.shouldStoreUserState) { - val storedUserState = userStateStore.insertUserState(_workingUserState) - clog.debug("Saved [_id=%s] %s", storedUserState._id, storedUserState) - _workingUserState = storedUserState - } - - clog.debug("RETURN %s", _workingUserState) clog.end() _workingUserState } diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala deleted file mode 100644 index 7ec81d9..0000000 --- a/src/main/scala/gr/grnet/aquarium/computation/data/CreditSnapshot.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2011-2012 GRNET S.A. All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and - * documentation are those of the authors and should not be - * interpreted as representing official policies, either expressed - * or implied, of GRNET S.A. - */ - -package gr.grnet.aquarium.computation.data - - -/** - * - * @author Christos KK Loverdos - */ - -case class CreditSnapshot(creditAmount: Double) diff --git a/src/main/scala/gr/grnet/aquarium/computation/reason/UserStateChangeReason.scala b/src/main/scala/gr/grnet/aquarium/computation/reason/UserStateChangeReason.scala index cc156da..0f9d072 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/reason/UserStateChangeReason.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/reason/UserStateChangeReason.scala @@ -40,23 +40,24 @@ import gr.grnet.aquarium.event.model.im.IMEventModel import gr.grnet.aquarium.util.shortClassNameOf /** - * Provides information explaining the reason Aquarium calculated a new [[gr.grnet.aquarium.computation.UserState]]. + * Provides information explaining the reason Aquarium calculated a new + * [[gr.grnet.aquarium.computation.state.UserState]]. */ case class UserStateChangeReason( - parentReason: Option[UserStateChangeReason], + details: Map[String, String], billingMonthInfo: Option[BillingMonthInfo], - details: Map[String, Any] + parentReason: Option[UserStateChangeReason] ) { require( - details.contains(UserStateChangeReason.Names.`type`), - "No type present in the details of %s".format(shortClassNameOf(this)) + details.contains(UserStateChangeReason.Names.name), + "No name present in the details of %s".format(shortClassNameOf(this)) ) private[this] def booleanFromDetails(name: String, default: Boolean) = { details.get(name) match { - case Some(value: Boolean) ⇒ - value + case Some(value) ⇒ + value.toBoolean case _ ⇒ false @@ -68,8 +69,8 @@ case class UserStateChangeReason( * [[gr.grnet.aquarium.store.UserStateStore]]. * */ - def shouldStoreUserState: Boolean = - booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false) +// def shouldStoreUserState: Boolean = +// booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false) def shouldStoreCalculatedWalletEntries: Boolean = booleanFromDetails(UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries, false) @@ -84,15 +85,15 @@ case class UserStateChangeReason( ) } - def `type`: String = { + def name: String = { // This must be always present - details(UserStateChangeReason.Names.`type`).asInstanceOf[String] + details.get(UserStateChangeReason.Names.name).getOrElse("") } } object UserStateChangeReason { object Names { - final val `type` = "type" + final val name = "name" final val imEvent = "imEvent" final val forWhenMillis = "forWhenMillis" @@ -121,109 +122,112 @@ sealed trait UserStateChangeReason_ { def code: UserStateChangeReasonCodes.ChangeReasonCode } +/** + * Used when the very first user state is saved. + */ object InitialUserStateSetup { - def `type` = "InitialUserStateSetup" + def name = "InitialUserStateSetup" /** * When the user state is initially set up. */ def apply(parentReason: Option[UserStateChangeReason]) = { UserStateChangeReason( - parentReason, - None, Map( - UserStateChangeReason.Names.`type` -> `type`, - UserStateChangeReason.Names.shouldStoreUserState -> true - ) + UserStateChangeReason.Names.name -> name//, +// UserStateChangeReason.Names.shouldStoreUserState -> true + ), + None, + parentReason ) } } object InitialUserActorSetup { - def `type` = "InitialUserActorSetup" + def name = "InitialUserActorSetup" /** * When the user processing unit (actor) is initially set up. */ def apply() = { UserStateChangeReason( - None, - None, Map( - UserStateChangeReason.Names.`type` -> `type`, - UserStateChangeReason.Names.shouldStoreUserState -> true - ) + UserStateChangeReason.Names.name -> name//, +// UserStateChangeReason.Names.shouldStoreUserState -> true + ), + None, + None ) } } object NoSpecificChangeReason { - def `type` = "NoSpecificChangeReason" + def name = "NoSpecificChangeReason" /** * A calculation made for no specific reason. Can be for testing, for example. */ def apply() = { UserStateChangeReason( - None, - None, Map( - UserStateChangeReason.Names.`type` -> `type` - ) + UserStateChangeReason.Names.name -> name + ), + None, + None ) } } object MonthlyBillingCalculation { - def `type` = "MonthlyBillingCalculation" + def name = "MonthlyBillingCalculation" /** * An authoritative calculation for the billing period. */ def apply(parentReason: UserStateChangeReason, billingMongthInfo: BillingMonthInfo) = { UserStateChangeReason( - Some(parentReason), - Some(billingMongthInfo), Map( - UserStateChangeReason.Names.`type` -> `type`, - UserStateChangeReason.Names.shouldStoreUserState -> true, - UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true, - UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true - ) + UserStateChangeReason.Names.name -> name, +// UserStateChangeReason.Names.shouldStoreUserState -> true, + UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true.toString, + UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true.toString + ), + Some(billingMongthInfo), + Some(parentReason) ) } } object RealtimeBillingCalculation { - def `type` = "RealtimeBillingCalculation" + def name = "RealtimeBillingCalculation" /** * Used for the real-time billing calculation. */ def apply(parentReason: Option[UserStateChangeReason], forWhenMillis: Long) = { UserStateChangeReason( - parentReason, - None, Map( - UserStateChangeReason.Names.`type` -> `type`, - UserStateChangeReason.Names.forWhenMillis -> forWhenMillis - ) + UserStateChangeReason.Names.name -> name, + UserStateChangeReason.Names.forWhenMillis -> forWhenMillis.toString + ), + None, + parentReason ) } } object IMEventArrival { - def `type` = "IMEventArrival" + def name = "IMEventArrival" def apply(imEvent: IMEventModel) = { UserStateChangeReason( - None, - None, Map( - UserStateChangeReason.Names.`type` -> `type`, - UserStateChangeReason.Names.imEvent -> imEvent, - UserStateChangeReason.Names.shouldStoreUserState -> true - ) + UserStateChangeReason.Names.name -> name, + UserStateChangeReason.Names.imEvent -> imEvent.toJsonString//, +// UserStateChangeReason.Names.shouldStoreUserState -> true + ), + None, + None ) } } diff --git a/src/main/scala/gr/grnet/aquarium/computation/UserState.scala b/src/main/scala/gr/grnet/aquarium/computation/state/UserState.scala similarity index 79% rename from src/main/scala/gr/grnet/aquarium/computation/UserState.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/UserState.scala index be838f3..e84a23b 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/UserState.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/UserState.scala @@ -33,20 +33,22 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation +package gr.grnet.aquarium.computation.state import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters} import gr.grnet.aquarium.event.model.NewWalletEntry import gr.grnet.aquarium.util.json.JsonSupport import gr.grnet.aquarium.logic.accounting.dsl.DSLAgreement import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, UserStateChangeReason, InitialUserStateSetup} -import gr.grnet.aquarium.computation.data.{OwnedResourcesMap, RoleHistory, ResourceInstanceSnapshot, OwnedResourcesSnapshot, AgreementHistory, LatestResourceEventsSnapshot, ImplicitlyIssuedResourceEventsSnapshot} import gr.grnet.aquarium.event.model.resource.ResourceEventModel +import gr.grnet.aquarium.computation.BillingMonthInfo +import gr.grnet.aquarium.computation.parts.RoleHistory +import gr.grnet.aquarium.computation.state.parts.{OwnedResourcesMap, ResourceInstanceSnapshot, OwnedResourcesSnapshot, AgreementHistory, ImplicitlyIssuedResourceEventsSnapshot, LatestResourceEventsSnapshot} /** * A comprehensive representation of the User's state. * - * Note that it is made of autonomous parts that are actually data snapshots. + * Note that it is made of autonomous parts that are actually parts snapshots. * * The different snapshots need not agree on the snapshot time, ie. some state * part may be stale, while other may be fresh. @@ -54,15 +56,15 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel * The user state is meant to be partially updated according to relevant events landing on Aquarium. * * @define communicatedByIM - * This is communicated to Aquarium from the `IM` system. + * This is communicated to Aquarium from the `IM` system. * * * @param userID - * The user ID. $communicatedByIM + * The user ID. $communicatedByIM * @param userCreationMillis - * When the user was created. - * $communicatedByIM - * Set to zero if unknown. + * When the user was created. + * $communicatedByIM + * Set to zero if unknown. * @param stateChangeCounter * @param isFullBillingMonthState * @param theFullBillingMonth @@ -72,15 +74,15 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel * @param agreementHistory * @param ownedResourcesSnapshot * @param newWalletEntries - * The wallet entries computed. Not all user states need to holds wallet entries, - * only those that refer to billing periods (end of billing period). - * @param lastChangeReason - * The [[gr.grnet.aquarium.computation.reason.UserStateChangeReason]] for which the usr state has changed. + * The wallet entries computed. Not all user states need to holds wallet entries, + * only those that refer to billing periods (end of billing period). + * @param lastChangeReason + * The [[gr.grnet.aquarium.computation.reason.UserStateChangeReason]] for which the usr state has changed. * @param parentUserStateIDInStore - * The `ID` of the parent state. The parent state is the one used as a reference point in order to calculate - * this user state. + * The `ID` of the parent state. The parent state is the one used as a reference point in order to calculate + * this user state. * @param _id - * The unique `ID` given by the store. + * The unique `ID` given by the store. * * @author Christos KK Loverdos */ @@ -153,12 +155,12 @@ case class UserState( def idInStore: Option[String] = _id match { case null ⇒ None - case _id ⇒ Some(_id.toString) + case _id ⇒ Some(_id.toString) } -// def userCreationDate = new Date(userCreationMillis) -// -// def userCreationFormatedDate = new MutableDateCalc(userCreationMillis).toString + // def userCreationDate = new Date(userCreationMillis) + // + // def userCreationFormatedDate = new MutableDateCalc(userCreationMillis).toString def findDSLAgreementForTime(at: Long): Option[DSLAgreement] = { agreementHistory.findForTime(at) @@ -172,7 +174,7 @@ case class UserState( ownedResourcesSnapshot.getResourceInstanceAmount(resource, instanceId, defaultValue) } - def newWithResourcesSnapshotUpdate(resource: String, // resource name + def newWithResourcesSnapshotUpdate(resource: String, // resource name instanceId: String, // resource instance id newAmount: Double, snapshotTime: Long): UserState = { @@ -183,7 +185,8 @@ case class UserState( this.copy( isInitial = false, ownedResourcesSnapshot = newResources, - stateChangeCounter = this.stateChangeCounter + 1) + stateChangeCounter = this.stateChangeCounter + 1 + ) } def newWithChangeReason(changeReason: UserStateChangeReason) = { @@ -194,6 +197,16 @@ case class UserState( ) } + def newWithRoleHistory(newRoleHistory: RoleHistory, changeReason: UserStateChangeReason) = { + // FIXME: Also update agreement + this.copy( + isInitial = false, + stateChangeCounter = this.stateChangeCounter + 1, + roleHistory = newRoleHistory, + lastChangeReason = changeReason + ) + } + def resourcesMap: OwnedResourcesMap = { ownedResourcesSnapshot.toResourcesMap } @@ -206,12 +219,12 @@ case class UserState( latestResourceEventsSnapshot.findTheLatestID } -// def toShortString = "UserState(%s, %s, %s, %s, %s)".format( -// userId, -// _id, -// parentUserStateId, -// totalEventsProcessedCounter, -// calculationReason) + // def toShortString = "UserState(%s, %s, %s, %s, %s)".format( + // userId, + // _id, + // parentUserStateId, + // totalEventsProcessedCounter, + // calculationReason) } object UserState { @@ -220,17 +233,18 @@ object UserState { } object JsonNames { - final val _id = "_id" + final val _id = "_id" final val userID = "userID" final val isFullBillingMonthState = "isFullBillingMonthState" final val occurredMillis = "occurredMillis" + final val theFullBillingMonth_year = "theFullBillingMonth.year" // FQN + final val theFullBillingMonth_month = "theFullBillingMonth.month" // FQN object theFullBillingMonth { - final val year = "year" + final val year = "year" final val month = "month" - final val monthStartMillis = "monthStartMillis" - final val monthStopMillis = "monthStopMillis" } + } def createInitialUserState( @@ -264,7 +278,7 @@ object UserState { } def createInitialUserStateFromBootstrap( - usb: UserStateBootstrappingData, + usb: UserStateBootstrap, occurredMillis: Long, calculationReason: UserStateChangeReason ): UserState = { diff --git a/src/main/scala/gr/grnet/aquarium/computation/UserStateBootstrappingData.scala b/src/main/scala/gr/grnet/aquarium/computation/state/UserStateBootstrap.scala similarity index 85% rename from src/main/scala/gr/grnet/aquarium/computation/UserStateBootstrappingData.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/UserStateBootstrap.scala index 63adac2..244470c 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/UserStateBootstrappingData.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/UserStateBootstrap.scala @@ -33,18 +33,18 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation +package gr.grnet.aquarium.computation.state /** - * This is used to bootstrap the user state. + * This is used to bootstrap the [[gr.grnet.aquarium.computation.state.UserState]]. * * @author Christos KK Loverdos */ -case class UserStateBootstrappingData( - userID: String, +case class UserStateBootstrap( + userID: String, userCreationMillis: Long, - initialRole: String, - initialAgreement: String, - initialCredits: Double + initialRole: String, + initialAgreement: String, + initialCredits: Double ) diff --git a/src/main/scala/gr/grnet/aquarium/computation/UserStateWorker.scala b/src/main/scala/gr/grnet/aquarium/computation/state/UserStateWorker.scala similarity index 90% rename from src/main/scala/gr/grnet/aquarium/computation/UserStateWorker.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/UserStateWorker.scala index c39eecf..6d213f8 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/UserStateWorker.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/UserStateWorker.scala @@ -34,12 +34,13 @@ */ package gr.grnet.aquarium.computation +package state import scala.collection.mutable import gr.grnet.aquarium.logic.accounting.dsl.DSLResourcesMap -import gr.grnet.aquarium.computation.data.{LatestResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, IgnoredFirstResourceEventsWorker} import gr.grnet.aquarium.util.ContextualLogger import gr.grnet.aquarium.event.model.resource.ResourceEventModel +import gr.grnet.aquarium.computation.state.parts.{IgnoredFirstResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, LatestResourceEventsWorker} /** * A helper object holding intermediate state/results during resource event processing. @@ -57,11 +58,13 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel * * @author Christos KK Loverdos */ -case class UserStateWorker(userID: String, - previousResourceEvents: LatestResourceEventsWorker, - implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker, - ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker, - resourcesMap: DSLResourcesMap) { +case class UserStateWorker( + userID: String, + previousResourceEvents: LatestResourceEventsWorker, + implicitlyIssuedStartEvents: ImplicitlyIssuedResourceEventsWorker, + ignoredFirstResourceEvents: IgnoredFirstResourceEventsWorker, + resourcesMap: DSLResourcesMap + ) { /** * Finds the previous resource event by checking two possible sources: a) The implicitly terminated resource @@ -138,6 +141,10 @@ case class UserStateWorker(userID: String, * @see [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]] */ def findAndRemoveGeneratorsOfImplicitEndEvents( + /** + * The `occurredMillis` that will be recorded in the synthetic implicit OFFs. + * Normally, this will be the end of a billing month. + */ newOccuredMillis: Long ): (List[ResourceEventModel], List[ResourceEventModel]) = { diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistory.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistory.scala similarity index 97% rename from src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistory.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistory.scala index 8a8177f..c08ff65 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistory.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistory.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import java.util.Date @@ -42,7 +44,7 @@ import gr.grnet.aquarium.logic.accounting.Policy import scala.collection.immutable.{SortedMap, TreeMap} /** - * User agreement data that will be part of UserState. + * User agreement parts that will be part of UserState. * The provided list of agreements cannot have time gaps. This is checked at object creation type. * * @author Christos KK Loverdos diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistoryItem.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistoryItem.scala similarity index 97% rename from src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistoryItem.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistoryItem.scala index 26c5815..12e376e 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/AgreementHistoryItem.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/AgreementHistoryItem.scala @@ -33,10 +33,11 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.util.date.MutableDateCalc -import java.util.Date import gr.grnet.aquarium.logic.accounting.dsl.Timeslot /** diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/IMStateSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IMStateSnapshot.scala similarity index 87% rename from src/main/scala/gr/grnet/aquarium/computation/data/IMStateSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/IMStateSnapshot.scala index 1b3d90e..1495857 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/IMStateSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IMStateSnapshot.scala @@ -33,11 +33,14 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.event.model.im.IMEventModel import gr.grnet.aquarium.util.shortClassNameOf import gr.grnet.aquarium.util.date.MutableDateCalc +import gr.grnet.aquarium.computation.parts.RoleHistory /** * @@ -45,25 +48,26 @@ import gr.grnet.aquarium.util.date.MutableDateCalc */ case class IMStateSnapshot( - /** - * This is the latest processed IMEvent - */ - latestIMEvent: IMEventModel, - - /** - * The earliest activation time, if it exists. - */ - userEarliestActivationMillis: Option[Long], - - /** - * The user creation time, if it exists - */ - userCreationMillis: Option[Long], - - /** - * This is the recorded role history - */ - roleHistory: RoleHistory) { + /** + * This is the latest processed IMEvent + */ + latestIMEvent: IMEventModel, + + /** + * The earliest activation time, if it exists. + */ + userEarliestActivationMillis: Option[Long], + + /** + * The user creation time, if it exists + */ + userCreationMillis: Option[Long], + + /** + * This is the recorded role history + */ + roleHistory: RoleHistory +) { /** * True iff the user has ever been activated even once. diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsSnapshot.scala similarity index 96% rename from src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsSnapshot.scala index 00c9f0f..ec8c77d 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsSnapshot.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.event.model.resource.ResourceEventModel diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsWorker.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsWorker.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsWorker.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsWorker.scala index ee640b9..aaede1a 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/IgnoredFirstResourceEventsWorker.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/IgnoredFirstResourceEventsWorker.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.util.findAndRemoveFromMap import gr.grnet.aquarium.event.model.resource.ResourceEventModel @@ -57,7 +59,7 @@ case class IgnoredFirstResourceEventsWorker(ignoredFirstEventsMap: FullMutableRe def size = ignoredFirstEventsMap.size - def foreach[U](f: ResourceEventModel => U): Unit = { + def foreach[U](f: ResourceEventModel ⇒ U): Unit = { ignoredFirstEventsMap.valuesIterator.foreach(f) } } diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsSnapshot.scala similarity index 94% rename from src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsSnapshot.scala index bb37cec..9955e29 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsSnapshot.scala @@ -33,13 +33,15 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.event.model.resource.ResourceEventModel /** * Keeps the implicit OFF events when a billing period ends. - * This is normally recorded in the [[gr.grnet.aquarium.computation.UserState]]. + * This is normally recorded in the [[gr.grnet.aquarium.computation.state.UserState]]. * * @author Christos KK Loverdos */ @@ -47,7 +49,7 @@ case class ImplicitlyIssuedResourceEventsSnapshot(implicitlyIssuedEvents: List[R /** * The gateway to playing with mutable state. * - * @return A fresh instance of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsWorker]]. + * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.ImplicitlyIssuedResourceEventsWorker]]. */ def toMutableWorker = { val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]() diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsWorker.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsWorker.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsWorker.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsWorker.scala index e05c9a3..0c67c9f 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/ImplicitlyIssuedResourceEventsWorker.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ImplicitlyIssuedResourceEventsWorker.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.util.findAndRemoveFromMap import gr.grnet.aquarium.event.model.resource.ResourceEventModel @@ -41,9 +43,7 @@ import gr.grnet.aquarium.event.model.resource.ResourceEventModel.FullMutableReso /** - * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.ImplicitlyIssuedResourceEventsSnapshot]]. - * - * @param implicitlyIssuedEventsMap + * This is the mutable cousin of [[gr.grnet.aquarium.computation.state.parts.ImplicitlyIssuedResourceEventsSnapshot]]. * * @author Christos KK Loverdos */ diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsSnapshot.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsSnapshot.scala index 8aa9684..1e7b144 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsSnapshot.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.event.model.resource.ResourceEventModel @@ -49,7 +51,7 @@ case class LatestResourceEventsSnapshot(resourceEvents: List[ResourceEventModel] /** * The gateway to playing with mutable state. * - * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsWorker]]. + * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsWorker]]. */ def toMutableWorker = { val map = scala.collection.mutable.Map[ResourceEventModel.FullResourceType, ResourceEventModel]() diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsWorker.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsWorker.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsWorker.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsWorker.scala index 95d62a1..fa3594c 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/LatestResourceEventsWorker.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/LatestResourceEventsWorker.scala @@ -33,14 +33,16 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts import gr.grnet.aquarium.util.findAndRemoveFromMap import gr.grnet.aquarium.event.model.resource.ResourceEventModel import gr.grnet.aquarium.event.model.resource.ResourceEventModel.FullMutableResourceTypeMap /** - * This is the mutable cousin of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]]. + * This is the mutable cousin of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsSnapshot]]. * * @param latestEventsMap * @@ -52,7 +54,7 @@ case class LatestResourceEventsWorker(latestEventsMap: FullMutableResourceTypeMa * The gateway to immutable state. * * @param snapshotTime The relevant snapshot time. - * @return A fresh instance of [[gr.grnet.aquarium.computation.data.LatestResourceEventsSnapshot]]. + * @return A fresh instance of [[gr.grnet.aquarium.computation.state.parts.LatestResourceEventsSnapshot]]. */ def toImmutableSnapshot(snapshotTime: Long) = LatestResourceEventsSnapshot(latestEventsMap.valuesIterator.toList) diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesMap.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesMap.scala similarity index 91% rename from src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesMap.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesMap.scala index c144456..1eab08d 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesMap.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesMap.scala @@ -33,13 +33,16 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts /** * A map from (resourceName, resourceInstanceId) to value. * * This representation is convenient for computations and updating, while the - * [[gr.grnet.aquarium.computation.data.OwnedResourcesSnapshot]] representation is convenient for JSON serialization. + * [[gr.grnet.aquarium.computation.state.parts.OwnedResourcesSnapshot]] representation is convenient for JSON + * serialization. * * @author Christos KK Loverdos */ diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesSnapshot.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesSnapshot.scala index f11a3e0..52ae97e 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/OwnedResourcesSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/OwnedResourcesSnapshot.scala @@ -33,8 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data - +package gr.grnet.aquarium.computation +package state +package parts /** * @@ -50,8 +51,8 @@ case class OwnedResourcesSnapshot(resourceInstanceSnapshots: List[ResourceInstan } def resourceInstanceSnapshotsExcept(resource: String, instanceId: String) = { - // Unfortunately, we have to use a List for data, since JSON serialization is not as flexible - // (at least out of the box). Thus, the update is O(L), where L is the length of the data List. + // Unfortunately, we have to use a List for parts, since JSON serialization is not as flexible + // (at least out of the box). Thus, the update is O(L), where L is the length of the parts List. resourceInstanceSnapshots.filterNot(_.isSameResourceInstance(resource, instanceId)) } diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/ResourceInstanceSnapshot.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ResourceInstanceSnapshot.scala similarity index 88% rename from src/main/scala/gr/grnet/aquarium/computation/data/ResourceInstanceSnapshot.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/ResourceInstanceSnapshot.scala index 70cca44..9c64d56 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/ResourceInstanceSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/ResourceInstanceSnapshot.scala @@ -33,7 +33,9 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation +package state +package parts /** * Maintains the current state of a resource instance owned by the user. @@ -41,7 +43,7 @@ package gr.grnet.aquarium.computation.data * * name: DSLResource.name * instanceId: instance-id (in the resource's descriminatorField) - * data: current-resource-value + * parts: current-resource-value * snapshotTime: last-update-timestamp * * In order to have a uniform representation of the resource state for all @@ -50,11 +52,13 @@ package gr.grnet.aquarium.computation.data * - If the resource is complex, the (name, instanceId) is (DSLResource.name, instance-id) * - If the resource is simple, the (name, instanceId) is (DSLResource.name, "1") * - * @param resource Same as `resource` of [[gr.grnet.aquarium.event.resource.ResourceEventModel]] - * @param instanceId Same as `instanceId` of [[gr.grnet.aquarium.event.resource.ResourceEventModel]] + * @param resource Same as `resource` of [[gr.grnet.aquarium.event.model.resource.ResourceEventModel]] + * @param instanceId Same as `instanceId` of [[gr.grnet.aquarium.event.model.resource.ResourceEventModel]] * @param instanceAmount This is the amount kept for the resource instance. -* The general rule is that an amount saved in a [[gr.grnet.aquarium.computation.data. ResourceInstanceSnapshot]] - * represents a total value, while a value appearing in a [[gr.grnet.aquarium.event.resource.ResourceEventModel]] +* The general rule is that an amount saved in a + * [[gr.grnet.aquarium.computation.parts. ResourceInstanceSnapshot]] + * represents a total value, while a value appearing in a + * [[gr.grnet.aquarium.event .model.resource.ResourceEventModel]] * represents a difference. How these two values are combined to form the new amount is dictated * by the underlying [[gr.grnet.aquarium.logic.accounting.dsl.DSLCostPolicy]] * diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/RoleHistory.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistory.scala similarity index 95% rename from src/main/scala/gr/grnet/aquarium/computation/data/RoleHistory.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistory.scala index fdee408..a7cd961 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/RoleHistory.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistory.scala @@ -33,7 +33,7 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation.parts import gr.grnet.aquarium.logic.accounting.dsl.Timeslot import scala.collection.immutable.{TreeMap, SortedMap} @@ -46,10 +46,11 @@ import scala.annotation.tailrec */ case class RoleHistory( - /** - * The head role is the most recent. The same rule applies for the tail. - */ - roles: List[RoleHistoryItem]) { + /** + * The head role is the most recent. The same rule applies for the tail. + */ + roles: List[RoleHistoryItem] +) { def roleNamesByTimeslot: SortedMap[Timeslot, String] = { TreeMap(roles.map(role ⇒ (role.timeslot, role.name)): _*) diff --git a/src/main/scala/gr/grnet/aquarium/computation/data/RoleHistoryItem.scala b/src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistoryItem.scala similarity index 96% rename from src/main/scala/gr/grnet/aquarium/computation/data/RoleHistoryItem.scala rename to src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistoryItem.scala index 59dca21..eed9cde 100644 --- a/src/main/scala/gr/grnet/aquarium/computation/data/RoleHistoryItem.scala +++ b/src/main/scala/gr/grnet/aquarium/computation/state/parts/RoleHistoryItem.scala @@ -33,7 +33,7 @@ * or implied, of GRNET S.A. */ -package gr.grnet.aquarium.computation.data +package gr.grnet.aquarium.computation.parts import gr.grnet.aquarium.util.date.MutableDateCalc import gr.grnet.aquarium.logic.accounting.dsl.Timeslot @@ -58,7 +58,8 @@ case class RoleHistoryItem( /** * Validity stop time for this role. The time is exclusive. */ - validTo: Long = Long.MaxValue) { + validTo: Long = Long.MaxValue +) { try { require( @@ -67,6 +68,7 @@ case class RoleHistoryItem( } catch { case e: IllegalArgumentException ⇒ + // TODO Remove this Aquarium.Instance.debug(this, "!! validFrom = %s, validTo = %s, dx=%s", validFrom, validTo, validTo-validFrom) throw e } diff --git a/src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala b/src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala index dd98ec0..71e3479 100644 --- a/src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala +++ b/src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala @@ -55,6 +55,8 @@ object JsonConversions { * The application-wide JSON formats used from the underlying lift-json library. */ implicit final val Formats = (DefaultFormats ++ JodaTimeSerializers.all) +// implicit final val Formats = (DefaultFormats.withHints(FullTypeHints(List(classOf[AnyRef]))) ++ JodaTimeSerializers.all) +// Serialization.formats(FullTypeHints(List(classOf[AnyRef]))) /** * Converts a value to JSON AST (Abstract Syntax Tree) by acting a bit intelligently, depending on the actual type diff --git a/src/main/scala/gr/grnet/aquarium/converter/StdConverters.scala b/src/main/scala/gr/grnet/aquarium/converter/StdConverters.scala index f0cbaa3..4febdcc 100644 --- a/src/main/scala/gr/grnet/aquarium/converter/StdConverters.scala +++ b/src/main/scala/gr/grnet/aquarium/converter/StdConverters.scala @@ -45,7 +45,7 @@ import net.liftweb.json.Xml /** - * Every data type conversion happening inside Aquarium is defined here. + * Every parts type conversion happening inside Aquarium is defined here. * * @author Christos KK Loverdos */ diff --git a/src/main/scala/gr/grnet/aquarium/event/model/PolicyEntry.scala b/src/main/scala/gr/grnet/aquarium/event/model/PolicyEntry.scala index 3a62163..8ea7401 100644 --- a/src/main/scala/gr/grnet/aquarium/event/model/PolicyEntry.scala +++ b/src/main/scala/gr/grnet/aquarium/event/model/PolicyEntry.scala @@ -40,7 +40,7 @@ import java.util.Date import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters} /** - * Store entry for serialized policy data. + * Store entry for serialized policy parts. * * @author Georgios Gousios */ diff --git a/src/main/scala/gr/grnet/aquarium/store/RecordID.scala b/src/main/scala/gr/grnet/aquarium/store/RecordID.scala index 44b092b..91102de 100644 --- a/src/main/scala/gr/grnet/aquarium/store/RecordID.scala +++ b/src/main/scala/gr/grnet/aquarium/store/RecordID.scala @@ -36,7 +36,7 @@ package gr.grnet.aquarium.store /** - * Represents an ID given by a store to a data object (document). + * Represents an ID given by a store to a parts object (document). * * @author Christos KK Loverdos . */ diff --git a/src/main/scala/gr/grnet/aquarium/store/UserStateStore.scala b/src/main/scala/gr/grnet/aquarium/store/UserStateStore.scala index 2ffd1e2..00832ef 100644 --- a/src/main/scala/gr/grnet/aquarium/store/UserStateStore.scala +++ b/src/main/scala/gr/grnet/aquarium/store/UserStateStore.scala @@ -35,12 +35,13 @@ package gr.grnet.aquarium.store -import gr.grnet.aquarium.computation.UserState +import gr.grnet.aquarium.computation.state.UserState +import gr.grnet.aquarium.computation.BillingMonthInfo /** * A store for user state snapshots. * - * This is used to hold snapshots of [[gr.grnet.aquarium.computation.UserState]] + * This is used to hold snapshots of [[gr.grnet.aquarium.computation.state.UserState]]. * * @author Christos KK Loverdos */ @@ -48,22 +49,17 @@ import gr.grnet.aquarium.computation.UserState trait UserStateStore { /** - * Store a user state. + * Stores a user state. */ def insertUserState(userState: UserState): UserState /** - * Find a state by user ID + * Finds a state by user ID */ def findUserStateByUserID(userID: String): Option[UserState] /** - * Find the most up-to-date user state for the particular billing period. + * Finds the most up-to-date user state for the particular billing period. */ - def findLatestUserStateForEndOfBillingMonth(userID: String, yearOfBillingMonth: Int, billingMonth: Int): Option[UserState] - - /** - * Delete a state for a user - */ - def deleteUserState(userId: String): Unit + def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState] } \ No newline at end of file diff --git a/src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala b/src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala index 5db4519..f61ade6 100644 --- a/src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala +++ b/src/main/scala/gr/grnet/aquarium/store/memory/MemStore.scala @@ -36,22 +36,22 @@ package gr.grnet.aquarium.store.memory import com.ckkloverdos.props.Props -import com.ckkloverdos.maybe.{NoVal, Just} +import com.ckkloverdos.maybe.Just import gr.grnet.aquarium.store._ import scala.collection.JavaConversions._ import collection.mutable.ConcurrentMap import java.util.concurrent.ConcurrentHashMap -import gr.grnet.aquarium.uid.ConcurrentVMLocalUIDGenerator import gr.grnet.aquarium.Configurable import gr.grnet.aquarium.event.model.PolicyEntry import gr.grnet.aquarium.event.model.im.{StdIMEvent, IMEventModel} import org.bson.types.ObjectId import gr.grnet.aquarium.event.model.resource.{StdResourceEvent, ResourceEventModel} -import gr.grnet.aquarium.computation.UserState +import gr.grnet.aquarium.computation.state.UserState import gr.grnet.aquarium.util.Tags +import gr.grnet.aquarium.computation.BillingMonthInfo /** - * An implementation of various stores that persists data in memory. + * An implementation of various stores that persists parts in memory. * * This is just for testing purposes. * @@ -111,16 +111,12 @@ class MemStore extends UserStateStore _userStates.find(_.userID == userID) } - def findLatestUserStateForEndOfBillingMonth(userID: String, - yearOfBillingMonth: Int, - billingMonth: Int): Option[UserState] = { + def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState] = { val goodOnes = _userStates.filter(_.theFullBillingMonth.isDefined).filter { userState ⇒ val f1 = userState.userID == userID val f2 = userState.isFullBillingMonthState val bm = userState.theFullBillingMonth.get - val f3 = (bm ne null) && { - bm.year == yearOfBillingMonth && bm.month == billingMonth - } + val f3 = bm == bmi f1 && f2 && f3 } @@ -135,10 +131,6 @@ class MemStore extends UserStateStore None } } - - def deleteUserState(userId: String) { - _userStates.filterNot(_.userID == userId) - } //- UserStateStore //+ ResourceEventStore diff --git a/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala b/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala index 617471f..04ecf59 100644 --- a/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala +++ b/src/main/scala/gr/grnet/aquarium/store/mongodb/MongoDBStore.scala @@ -36,7 +36,7 @@ package gr.grnet.aquarium.store.mongodb import com.mongodb.util.JSON -import gr.grnet.aquarium.computation.UserState.{JsonNames ⇒ UserStateJsonNames} +import gr.grnet.aquarium.computation.state.UserState.{JsonNames ⇒ UserStateJsonNames} import gr.grnet.aquarium.util.json.JsonSupport import collection.mutable.ListBuffer import gr.grnet.aquarium.event.model.im.IMEventModel @@ -51,8 +51,10 @@ import org.bson.types.ObjectId import com.ckkloverdos.maybe.Maybe import gr.grnet.aquarium.util._ import gr.grnet.aquarium.converter.Conversions -import gr.grnet.aquarium.computation.UserState +import gr.grnet.aquarium.computation.state.UserState import gr.grnet.aquarium.event.model.{ExternalEventModel, PolicyEntry} +import gr.grnet.aquarium.computation.BillingMonthInfo +import gr.grnet.aquarium.Aquarium /** * Mongodb implementation of the various aquarium stores. @@ -226,16 +228,16 @@ class MongoDBStore( MongoDBStore.firstResultIfExists(cursor, MongoDBStore.dbObjectToUserState) } - def findLatestUserStateForEndOfBillingMonth(userID: String, - yearOfBillingMonth: Int, - billingMonth: Int): Option[UserState] = { + def findLatestUserStateForFullMonthBilling(userID: String, bmi: BillingMonthInfo): Option[UserState] = { val query = new BasicDBObjectBuilder(). add(UserState.JsonNames.userID, userID). add(UserState.JsonNames.isFullBillingMonthState, true). - add(UserState.JsonNames.theFullBillingMonth.year, yearOfBillingMonth). - add(UserState.JsonNames.theFullBillingMonth.month, billingMonth). + add(UserState.JsonNames.theFullBillingMonth_year, bmi.year). + add(UserState.JsonNames.theFullBillingMonth_month, bmi.month). get() + logger.debug("findLatestUserStateForFullMonthBilling(%s, %s) query: %s".format(userID, bmi, query)) + // Descending order, so that the latest comes first val sorter = new BasicDBObject(UserState.JsonNames.occurredMillis, -1) @@ -243,11 +245,6 @@ class MongoDBStore( MongoDBStore.firstResultIfExists(cursor, MongoDBStore.dbObjectToUserState) } - - def deleteUserState(userId: String) = { - val query = new BasicDBObject(UserStateJsonNames.userID, userId) - userStates.findAndRemove(query) - } //- UserStateStore //+IMEventStore @@ -373,9 +370,10 @@ object MongoDBStore { final val RESOURCE_EVENTS_COLLECTION = "resevents" /** - * Collection holding the snapshots of [[gr.grnet.aquarium.computation.UserState]]. + * Collection holding the snapshots of [[gr.grnet.aquarium.computation.state.UserState]]. * - * [[gr.grnet.aquarium.computation.UserState]] is held internally within [[gr.grnet.aquarium.actor.service.user.UserActor]]s. + * [[gr.grnet.aquarium.computation.state.UserState]] is held internally within + * [[gr.grnet.aquarium.actor.service.user .UserActor]]s. */ final val USER_STATES_COLLECTION = "userstates" diff --git a/src/main/scala/gr/grnet/aquarium/util/ContextualLogger.scala b/src/main/scala/gr/grnet/aquarium/util/ContextualLogger.scala index 3d71eae..71cef07 100644 --- a/src/main/scala/gr/grnet/aquarium/util/ContextualLogger.scala +++ b/src/main/scala/gr/grnet/aquarium/util/ContextualLogger.scala @@ -52,36 +52,36 @@ DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - t DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo) DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() --- [Events by OccurredMillis] --- DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2012-01) BEGIN +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2012-01) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12) No user state found from cache, will have to (re)compute -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-12) BEGIN +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-12) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11) No user state found from cache, will have to (re)compute -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-11) BEGIN +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-11) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10) BEGIN DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10) User did not exist before 2011-11-01 00:00:00.000 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10) Returning INITIAL state [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,0,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,InitialUserStateSetup,0,None,4fa7e12ba0eee3db73fbe8d0) DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-10) END -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-11) calculationReason = MonthlyBillingCalculation(2011-11) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-11) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-11) RETURN UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-11) END +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-11) calculationReason = MonthlyBillingCalculation(2011-11) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-11) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-11) RETURN UserState(true,CKKL,1320098400000,1,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-11),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-11) END DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-11) END -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-12) calculationReason = MonthlyBillingCalculation(2011-12) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-12) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-12) RETURN UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2011-12) END +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-12) calculationReason = MonthlyBillingCalculation(2011-12) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-12) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-12) RETURN UserState(true,CKKL,1320098400000,2,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List()),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2011-12),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2011-12) END DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - …ndUserStateAtEndOfBillingMonth(2011-12) END DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - walletEntriesForResourceEvent(0) +++ [EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)] +++ DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - walletEntriesForResourceEvent(0) Cost policy OnOffCostPolicy for DSLResource(vmtime,Hr,OnOffCostPolicy,true,instanceId) DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - walletEntriesForResourceEvent(0) PreviousM None DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - walletEntriesForResourceEvent(0) Ignoring first event of its kind EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo) DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - walletEntriesForResourceEvent(0) --- [EVENT(0, [2012-01-01 00:00:00], 0.0, vmtime::VM.1, Map(), CKKL, synnefo)] --- -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2012-01) calculationReason = MonthlyBillingCalculation(2012-01) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2012-01) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2012-01) RETURN UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) -DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthlyBilling(2012-01) END +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2012-01) calculationReason = MonthlyBillingCalculation(2012-01) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2012-01) Saved [_id=4fa7e12ba0eee3db73fbe8d0] UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2012-01) RETURN UserState(true,CKKL,1320098400000,3,false,null,ImplicitlyIssuedResourceEventsSnapshot(List()),List(),List(),LatestResourceEventsSnapshot(List(StdResourceEvent(0,1325368800000,1325368800000,CKKL,synnefo,vmtime,VM.1,0.0,1.0,Map()))),0,0,IMStateSnapshot(StdIMEvent(,1320098400000,1320098400000,CKKL,,true,user,1.0,create,Map())),CreditSnapshot(0.0),AgreementHistory(List(AgreementHistoryItem(default, 2011-11-01 00:00:00.000, 292278994-08-17 07:12:55.807))),OwnedResourcesSnapshot(List()),List(),1320098400000,MonthlyBillingCalculation(2012-01),0,Some(4fa7e12ba0eee3db73fbe8d0),4fa7e12ba0eee3db73fbe8d0) +DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - doFullMonthBilling(2012-01) END DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() _id = 4fa7e12ba0eee3db73fbe8d0 DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() parentId = Some(4fa7e12ba0eee3db73fbe8d0) DEBUG 17:50:19 g.g.a.user.UserStateComputationsTest - testOrphanOFF() credits = 0.0 diff --git a/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala b/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala index 3417a6e..7d9c28c 100644 --- a/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala +++ b/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala @@ -46,8 +46,9 @@ import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableCostPolicyAlgorit import gr.grnet.aquarium.AquariumException import gr.grnet.aquarium.Aquarium.{Instance ⇒ AquariumInstance} import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, MonthlyBillingCalculation} -import gr.grnet.aquarium.computation.{TimeslotComputations, UserStateBootstrappingData, UserState, BillingMonthInfo} -import gr.grnet.aquarium.util.date.{TimeHelpers, MutableDateCalc} +import gr.grnet.aquarium.util.date.MutableDateCalc +import gr.grnet.aquarium.computation.BillingMonthInfo +import gr.grnet.aquarium.computation.state.{UserStateBootstrap, UserState} /** @@ -250,7 +251,7 @@ aquariumpolicy: // initialAgreement = DSLAgreement.DefaultAgreementName // ) - val UserStateBootstrap = UserStateBootstrappingData( + val UserStateBootstrapper = UserStateBootstrap( userID = UserCKKL.userID, userCreationMillis = UserCreationDate.getTime(), initialRole = "default", @@ -301,27 +302,30 @@ aquariumpolicy: def doFullMonthlyBilling( clog: ContextualLogger, billingMonthInfo: BillingMonthInfo, - isFullBillingMonthState: Boolean, billingTimeMillis: Long) = { - Computations.doBillingForMonth( - UserStateBootstrap, + Computations.doMonthBillingUpTo( billingMonthInfo, - isFullBillingMonthState, billingTimeMillis, + UserStateBootstrapper, DefaultResourcesMap, MonthlyBillingCalculation(NoSpecificChangeReason(), billingMonthInfo), + aquarium.userStateStore.insertUserState, Some(clog) ) } private[this] - def expectCredits(clog: ContextualLogger, - creditsConsumed: Double, - userState: UserState, - accuracy: Double = 0.001): Unit = { + def expectCredits( + clog: ContextualLogger, + creditsConsumed: Double, + userState: UserState, + accuracy: Double = 0.001 + ): Unit = { + val computed = userState.totalCredits Assert.assertEquals(-creditsConsumed, computed, accuracy) + clog.info("Consumed %.3f credits [accuracy = %f]", creditsConsumed, accuracy) } @@ -353,7 +357,7 @@ aquariumpolicy: showResourceEvents(clog) - val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis()) + val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis) showUserState(clog, userState) @@ -382,7 +386,7 @@ aquariumpolicy: showResourceEvents(clog) - val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis()) + val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis) showUserState(clog, userState) @@ -412,7 +416,7 @@ aquariumpolicy: showResourceEvents(clog) - val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis()) + val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis) showUserState(clog, userState) @@ -463,7 +467,7 @@ aquariumpolicy: clog.debugMap("DefaultResourcesMap", DefaultResourcesMap.map, 1) - val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, false, TimeHelpers.nowMillis()) + val userState = doFullMonthlyBilling(clog, BillingMonthInfoJan, BillingMonthInfoJan.monthStopMillis) showUserState(clog, userState)