From: Christos KK Loverdos Date: Mon, 6 Feb 2012 16:16:47 +0000 (+0200) Subject: Pruning stuff from user state X-Git-Url: https://code.grnet.gr/git/aquarium/commitdiff_plain/28bb45ec3fbedac2aa44f1691676dea8ee323534?ds=sidebyside Pruning stuff from user state --- diff --git a/src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala b/src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala index 2907857..f6328b0 100644 --- a/src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala +++ b/src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala @@ -104,8 +104,10 @@ abstract class DSLCostPolicy(val name: String) extends DSLItem { * * Typically all events are billable by default and indeed this is the default implementation * provided here. + * + * The only exception to the rule is ON events for [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]]. */ - def isBillableEventBasedOnValue(newEventValue: Double): Boolean = true + def isBillableEventBasedOnValue(eventValue: Double): Boolean = true } object DSLCostPolicyNames { @@ -245,8 +247,9 @@ case object OnOffCostPolicy extends DSLCostPolicy(DSLCostPolicyNames.onoff) { } } - override def isBillableEventBasedOnValue(newEventValue: Double) = { - OnOffCostPolicyValues.isOFF(newEventValue) + override def isBillableEventBasedOnValue(eventValue: Double) = { + // ON events do not contribute, only OFF ones. + OnOffCostPolicyValues.isOFF(eventValue) } } diff --git a/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala b/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala index de7e9b6..7e4794b 100644 --- a/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala +++ b/src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala @@ -107,8 +107,8 @@ case class ResourceEvent( "EVENT(%s, [%s], %s, %s, %s, %s, %s)".format( id, occurredFormatted, - instanceInfo, bvalue, + instanceInfo, details, userId, clientId @@ -118,8 +118,8 @@ case class ResourceEvent( id, occurredFormatted, new DateCalculator(receivedMillis), - instanceInfo, bvalue, + instanceInfo, details, userId, clientId diff --git a/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala b/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala index 8600bc1..c9a7706 100644 --- a/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala +++ b/src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala @@ -54,15 +54,6 @@ case class CreditSnapshot(data: Double, snapshotTime: Long) extends UserDataSnap case class RolesSnapshot(data: List[String], snapshotTime: Long) extends UserDataSnapshot[List[String]] -// TODO: Check if needed -case class PaymentOrdersSnapshot(data: List[AnyRef], snapshotTime: Long) extends UserDataSnapshot[List[AnyRef]] - -// TODO: Check if needed -case class OwnedGroupsSnapshot(data: List[String], snapshotTime: Long) extends UserDataSnapshot[List[String]] - -// TODO: Check if needed -case class GroupMembershipsSnapshot(data: List[String], snapshotTime: Long) extends UserDataSnapshot[List[String]] - /** * Represents an agreement valid for a specific amount of time. By convention, * if an agreement is currently valid, then the validTo field is equal to -1. diff --git a/src/main/scala/gr/grnet/aquarium/user/UserState.scala b/src/main/scala/gr/grnet/aquarium/user/UserState.scala index e4c987a..637b164 100644 --- a/src/main/scala/gr/grnet/aquarium/user/UserState.scala +++ b/src/main/scala/gr/grnet/aquarium/user/UserState.scala @@ -61,6 +61,7 @@ case class UserState( /** * When the user was created in the system (not Aquarium). We use this as a basis for billing periods. Set to * zero if unknown. + * */ startDateMillis: Long, @@ -74,14 +75,18 @@ case class UserState( /** * True iff this user state refers to a full billing period, that is a full billing month. */ - isFullBillingPeriod: Boolean, + isFullBillingMonthState: Boolean, /** * The full billing period for which this user state refers to. * This is set when the user state refers to a full billing period (= month) * and is used to cache the user state for subsequent queries. */ - fullBillingPeriod: BillingPeriod, + theFullBillingMonth: BillingMonth, + + /** + * + */ /** * Counts the number of resource events used to produce this user state for @@ -93,9 +98,6 @@ case class UserState( credits: CreditSnapshot, agreements: AgreementSnapshot, roles: RolesSnapshot, - paymentOrders: PaymentOrdersSnapshot, - ownedGroups: OwnedGroupsSnapshot, - groupMemberships: GroupMembershipsSnapshot, ownedResources: OwnedResourcesSnapshot ) extends JsonSupport { @@ -103,7 +105,6 @@ case class UserState( List( active.snapshotTime, credits.snapshotTime, agreements.snapshotTime, roles.snapshotTime, - paymentOrders.snapshotTime, ownedGroups.snapshotTime, groupMemberships.snapshotTime, ownedResources.snapshotTime) } @@ -172,4 +173,4 @@ object UserState { } } -case class BillingPeriod(startMillis: Long, stopMillis: Long) \ No newline at end of file +case class BillingMonth(yearOfBillingMonth: Int, billingMonth: Int) \ No newline at end of file diff --git a/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala b/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala index 9d46e76..4a5bebf 100644 --- a/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala +++ b/src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala @@ -73,7 +73,7 @@ trait UserStateCache { /** * Find the most up-to-date user state for the particular billing period. */ - def findLatestUserStateForBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int): Maybe[UserState] + def findLatestUserStateForEndOfBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int): Maybe[UserState] } /** @@ -118,9 +118,6 @@ class UserStateComputations extends Loggable { CreditSnapshot(0, now), AgreementSnapshot(Agreement(agreementName, now, -1) :: Nil, now), RolesSnapshot(List(), now), - PaymentOrdersSnapshot(Nil, now), - OwnedGroupsSnapshot(Nil, now), - GroupMembershipsSnapshot(Nil, now), OwnedResourcesSnapshot(List(), now) ) } @@ -138,9 +135,6 @@ class UserStateComputations extends Loggable { CreditSnapshot(0, now), AgreementSnapshot(Agreement(agreementName, now, - 1) :: Nil, now), RolesSnapshot(List(), now), - PaymentOrdersSnapshot(Nil, now), - OwnedGroupsSnapshot(Nil, now), - GroupMembershipsSnapshot(Nil, now), OwnedResourcesSnapshot(List(), now) ) } @@ -150,7 +144,7 @@ class UserStateComputations extends Loggable { * * Always compute, taking into account any "out of sync" resource events */ - def computeUserStateAtStartOfBillingPeriod(billingYear: Int, + def computeUserStateAtEndOfBillingPeriod(billingYear: Int, billingMonth: Int, knownUserState: UserState, accounting: Accounting): Maybe[EndOfBillingState] = { @@ -174,6 +168,20 @@ class UserStateComputations extends Loggable { // } } + + def findBillingStateAtEndOfBillingPeriod(yearOfBillingMonth: Int, + billingMonth: Int, + userId: String, + userStateCache: UserStateCache, + accounting: Accounting): Maybe[EndOfBillingState] = { + userStateCache.findLatestUserStateForEndOfBillingMonth(userId, yearOfBillingMonth, billingMonth) match { + case Just(userState) ⇒ + case NoVal ⇒ + case failed @ Failed(e, m) ⇒ + } + + Just(EndOfBillingState(createFirstUserState(userId), ImplicitOffEvents(Nil), OutOfSyncWalletEntries(Nil))) + } /** * Find the previous resource event, if needed by the event's cost policy, @@ -204,7 +212,8 @@ class UserStateComputations extends Loggable { NoVal } - def updatePreviousRCEventWith(previousRCEventsMap: mutable.Map[ResourceEvent.FullResourceType, ResourceEvent], + type FullResourceType = ResourceEvent.FullResourceType + def updatePreviousRCEventWith(previousRCEventsMap: mutable.Map[FullResourceType, ResourceEvent], newRCEvent: ResourceEvent): Unit = { previousRCEventsMap(newRCEvent.fullResourceInfo) = newRCEvent } @@ -240,7 +249,7 @@ class UserStateComputations extends Loggable { // Check if this value is already cached and valid, otherwise compute the value // TODO : cache it in case of new computation - val cachedStartUserStateM = userStateCache.findLatestUserStateForBillingMonth( + val cachedStartUserStateM = userStateCache.findLatestUserStateForEndOfBillingMonth( userId, yearOfPrevBillingMonth, prevBillingMonth) @@ -268,7 +277,7 @@ class UserStateComputations extends Loggable { } else { // Oops, there are "out of sync" resource event. Must compute (potentially recursively) logger.debug("Recompute start user state...") - val computedUserStateAtStartOfBillingPeriod = computeUserStateAtStartOfBillingPeriod( + val computedUserStateAtStartOfBillingPeriod = computeUserStateAtEndOfBillingPeriod( yearOfPrevBillingMonth, prevBillingMonth, cachedStartUserState, @@ -283,7 +292,7 @@ class UserStateComputations extends Loggable { case NoVal ⇒ // We do not even have a cached value, so compute one! logger.debug("Do not have a cachedStartUserState, computing one...") - val computedUserStateAtStartOfBillingPeriod = computeUserStateAtStartOfBillingPeriod( + val computedUserStateAtStartOfBillingPeriod = computeUserStateAtEndOfBillingPeriod( yearOfPrevBillingMonth, prevBillingMonth, currentUserState, @@ -306,22 +315,35 @@ class UserStateComputations extends Loggable { logger.debug("allBillingPeriodRelevantRCEvents [%s] = %s".format(allBillingPeriodRelevantRCEvents.size, allBillingPeriodRelevantRCEvents)) type FullResourceType = ResourceEvent.FullResourceType + // For each type and instance of resource, we keep the previously met resource event. val previousRCEventsMap = mutable.Map[FullResourceType, ResourceEvent]() + // Since we may already have some implicit events from the beginning of the billing period, we put + // them to the map. + // TODO: val impliedRCEventsMap = mutable.Map[FullResourceType, ResourceEvent]() // those which do not exists but are // implied in order to do billing calculations (e.g. the "off" vmtime resource event) // Our temporary state holder. var _workingUserState = newStartUserState val nowMillis = TimeHelpers.nowMillis + var _counter = 0 for(currentResourceEvent <- allBillingPeriodRelevantRCEvents) { + _counter = _counter + 1 val resource = currentResourceEvent.resource val instanceId = currentResourceEvent.instanceId - logger.debug("Processing %s".format(currentResourceEvent.toDebugString(defaultResourcesMap, true))) + logger.debug("%02d. Processing %s".format(_counter, currentResourceEvent.toDebugString(defaultResourcesMap, true))) // ResourCe events Debug // = = = - def RCD(fmt: String, args: Any*) = logger.debug(" => " + fmt.format(args:_*)) + def RCD(fmt: String, args: Any*) = logger.debug(" ⇒ " + fmt.format(args:_*)) + + RCD("previousRCEventsMap: ") + for { + (k, v) <- previousRCEventsMap + } { + RCD(" %s ⇒ %s".format(k, v.toDebugString(defaultResourcesMap, true))) + } // We need to do these kinds of calculations: // 1. Credit state calculations @@ -350,36 +372,47 @@ class UserStateComputations extends Loggable { costPolicyOpt match { case Some(costPolicy) ⇒ RCD("Found costPolicy = %s".format(costPolicy)) - /////////////////////////////////////// - // A. Update user state with new resource instance amount - // TODO: Check if we are at beginning of billing period, so as to use - // costPolicy.computeResourceInstanceAmountForNewBillingPeriod - val DefaultResourceInstanceAmount = costPolicy.getResourceInstanceInitialAmount - RCD("DefaultResourceInstanceAmount = %s".format(DefaultResourceInstanceAmount)) - - val previousAmount = currentUserState.getResourceInstanceAmount(resource, instanceId, DefaultResourceInstanceAmount) - RCD("previousAmount = %s".format(previousAmount)) - val newAmount = costPolicy.computeNewResourceInstanceAmount(previousAmount, currentResourceEvent.value) - RCD("newAmount = %s".format(newAmount)) - - _workingUserState = _workingUserState.copyForResourcesSnapshotUpdate(resource, instanceId, newAmount, nowMillis) - // A. Update user state with new resource instance amount - /////////////////////////////////////// - - - /////////////////////////////////////// - // B. Update user state with new credit - val previousRCEventM = findPreviousRCEventOf(currentResourceEvent, costPolicy, previousRCEventsMap) - _workingUserState.findResourceInstanceSnapshot(resource, instanceId) - // B. Update user state with new credit - /////////////////////////////////////// - - - /////////////////////////////////////// - // C. Update ??? state with wallet entries - - // C. Update ??? state with wallet entries - /////////////////////////////////////// + + // If this is an event for which no action is required, then OK, proceed with the next one + // Basically, we do nothing for ON events but we treat everything polymorphically here + costPolicy.isBillableEventBasedOnValue(currentResourceEvent.value) match { + case true ⇒ + /////////////////////////////////////// + // A. Update user state with new resource instance amount + // TODO: Check if we are at beginning of billing period, so as to use + // costPolicy.computeResourceInstanceAmountForNewBillingPeriod + val DefaultResourceInstanceAmount = costPolicy.getResourceInstanceInitialAmount + RCD("DefaultResourceInstanceAmount = %s".format(DefaultResourceInstanceAmount)) + + val previousAmount = currentUserState.getResourceInstanceAmount(resource, instanceId, DefaultResourceInstanceAmount) + RCD("previousAmount = %s".format(previousAmount)) + val newAmount = costPolicy.computeNewResourceInstanceAmount(previousAmount, currentResourceEvent.value) + RCD("newAmount = %s".format(newAmount)) + + _workingUserState = _workingUserState.copyForResourcesSnapshotUpdate(resource, instanceId, newAmount, nowMillis) + // A. Update user state with new resource instance amount + /////////////////////////////////////// + + + /////////////////////////////////////// + // B. Update user state with new credit + val previousRCEventM = findPreviousRCEventOf(currentResourceEvent, costPolicy, previousRCEventsMap) + _workingUserState.findResourceInstanceSnapshot(resource, instanceId) + // B. Update user state with new credit + /////////////////////////////////////// + + + /////////////////////////////////////// + // C. Update ??? state with wallet entries + + // C. Update ??? state with wallet entries + /////////////////////////////////////// + + case false ⇒ // costPolicy.isBillableEventBasedOnValue(currentResourceEvent.value) + RCD("Ignoring not billabe (%s) %s".format( + currentResourceEvent.beautifyValue(defaultResourcesMap), + currentResourceEvent.toDebugString(defaultResourcesMap, true))) + } case None ⇒ () // ERROR diff --git a/src/test/scala/gr/grnet/aquarium/user/UserActorTest.scala b/src/test/scala/gr/grnet/aquarium/user/UserActorTest.scala index a2707ab..ba35ab0 100644 --- a/src/test/scala/gr/grnet/aquarium/user/UserActorTest.scala +++ b/src/test/scala/gr/grnet/aquarium/user/UserActorTest.scala @@ -23,9 +23,6 @@ class UserActorTest { CreditSnapshot(0, now), AgreementSnapshot(Agreement("default", now, now) :: Nil, now), RolesSnapshot(Nil, now), - PaymentOrdersSnapshot(Nil, now), - OwnedGroupsSnapshot(Nil, now), - GroupMembershipsSnapshot(Nil, now), OwnedResourcesSnapshot(ResourceInstanceSnapshot("foo", "1", 0.1F, 1) :: Nil, now) ) diff --git a/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala b/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala index c1f8009..51259c3 100644 --- a/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala +++ b/src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala @@ -62,7 +62,7 @@ class UserStateComputationsTest { val userStateCache = new UserStateCache { def findUserStateAtEndOfPeriod(userId: String, year: Int, month: Int) = NoVal - def findLatestUserStateForBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int) = NoVal + def findLatestUserStateForEndOfBillingMonth(userId: String, yearOfBillingMonth: Int, billingMonth: Int) = NoVal } val mc = Configurator.MasterConfigurator.withStoreProviderClass(classOf[MemStore])