Pruning stuff from user state
authorChristos KK Loverdos <loverdos@gmail.com>
Mon, 6 Feb 2012 16:16:47 +0000 (18:16 +0200)
committerChristos KK Loverdos <loverdos@gmail.com>
Mon, 6 Feb 2012 16:16:47 +0000 (18:16 +0200)
src/main/scala/gr/grnet/aquarium/logic/accounting/dsl/DSLCostPolicy.scala
src/main/scala/gr/grnet/aquarium/logic/events/ResourceEvent.scala
src/main/scala/gr/grnet/aquarium/user/UserDataSnapshot.scala
src/main/scala/gr/grnet/aquarium/user/UserState.scala
src/main/scala/gr/grnet/aquarium/user/UserStateComputations.scala
src/test/scala/gr/grnet/aquarium/user/UserActorTest.scala
src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala

index 2907857..f6328b0 100644 (file)
@@ -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)
   }
 }
 
index de7e9b6..7e4794b 100644 (file)
@@ -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
index 8600bc1..c9a7706 100644 (file)
@@ -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.
index e4c987a..637b164 100644 (file)
@@ -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
index 9d46e76..4a5bebf 100644 (file)
@@ -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
index a2707ab..ba35ab0 100644 (file)
@@ -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)
     )
 
index c1f8009..51259c3 100644 (file)
@@ -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])