package gr.grnet.aquarium.charging
+import scala.collection.mutable
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.computation.BillingMonthInfo
-import gr.grnet.aquarium.charging.state.UserStateBootstrap
+import gr.grnet.aquarium.charging.state.{WorkingResourcesChargingState, UserStateBootstrap, WorkingUserState, UserStateModel, StdUserState}
import gr.grnet.aquarium.policy.ResourceType
-import gr.grnet.aquarium.util.{Lifecycle, Loggable, ContextualLogger}
+import gr.grnet.aquarium.util.{Lifecycle, Loggable}
+import gr.grnet.aquarium.util.LogHelpers.Debug
+import gr.grnet.aquarium.util.LogHelpers.Warn
import gr.grnet.aquarium.util.date.{MutableDateCalc, TimeHelpers}
import gr.grnet.aquarium.{AquariumInternalError, AquariumAwareSkeleton}
-import gr.grnet.aquarium.charging.state.{WorkingUserState, UserStateModel, StdUserState}
import gr.grnet.aquarium.charging.reason.{MonthlyBillChargingReason, InitialUserStateSetup, ChargingReason}
/**
lazy val resourceEventStore = aquarium.resourceEventStore
//+ Lifecycle
- def start() = ()
+ def start() {}
- def stop() = ()
+ def stop() {}
//- Lifecycle
userStateBootstrap: UserStateBootstrap,
defaultResourceTypesMap: Map[String, ResourceType],
chargingReason: ChargingReason,
- userStateRecorder: UserStateModel ⇒ UserStateModel,
- clogOpt: Option[ContextualLogger]
+ userStateRecorder: UserStateModel ⇒ UserStateModel
): WorkingUserState = {
- val clog = ContextualLogger.fromOther(
- clogOpt,
- logger,
- "findOrCalculateWorkingUserStateAtEndOfBillingMonth(%s)", billingMonthInfo.toShortDebugString)
- clog.begin()
-
- lazy val clogSome = Some(clog)
-
def computeFullMonthBillingAndSaveState(): WorkingUserState = {
val workingUserState = replayFullMonthBilling(
userStateBootstrap,
billingMonthInfo,
defaultResourceTypesMap,
chargingReason,
- userStateRecorder,
- clogSome
+ userStateRecorder
)
val newChargingReason = MonthlyBillChargingReason(chargingReason, billingMonthInfo)
workingUserState.chargingReason = newChargingReason
- val monthlyUserState0 = workingUserState.toUserState(Some(billingMonthInfo), None)
+ val monthlyUserState0 = workingUserState.toUserState(
+ true,
+ billingMonthInfo.year,
+ billingMonthInfo.month,
+ ""
+ )
// We always save the state when it is a full month billing
val monthlyUserState1 = userStateRecorder.apply(monthlyUserState0)
- clog.debug("Stored full %s %s", billingMonthInfo.toDebugString, monthlyUserState1.toJsonString)
+ Debug(logger, "Stored full %s %s", billingMonthInfo.toDebugString, monthlyUserState1.toJsonString)
workingUserState
}
if(billingMonthStopMillis < userCreationMillis) {
// If the user did not exist for this billing month, piece of cake
- clog.debug("User did not exist before %s", userCreationDateCalc)
+ Debug(logger, "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
InitialUserStateSetup(Some(chargingReason)) // we record the originating calculation reason
)
- logger.debug("Created (from bootstrap) initial user state %s".format(initialUserState0))
+ Debug(logger, "Created (from bootstrap) initial user state %s", initialUserState0)
// We always save the initial state
val initialUserState1 = userStateRecorder.apply(initialUserState0)
- clog.debug("Stored initial state = %s", initialUserState1.toJsonString)
- clog.end()
+ Debug(logger, "Stored initial state = %s", initialUserState1.toJsonString)
return initialUserState1.toWorkingUserState(defaultResourceTypesMap)
}
latestUserStateOpt match {
case None ⇒
// Not found, must compute
- clog.debug("No user state found from cache, will have to (re)compute")
- val result = computeFullMonthBillingAndSaveState
- clog.end()
- result
+ Debug(logger, "No user state found from cache, will have to (re)compute")
+ computeFullMonthBillingAndSaveState
case Some(latestUserState) ⇒
// Found a "latest" user state but need to see if it is indeed the true and one latest.
case 0 ⇒
// NOTE: Keep the caller's calculation reason
val userStateModel = latestUserState.newWithChargingReason(chargingReason)
- clog.end()
userStateModel.toWorkingUserState(defaultResourceTypesMap)
// We had more, so must recompute
case n if n > 0 ⇒
- clog.debug(
+ Debug(logger,
"Found %s out of sync events (%s more), will have to (re)compute user state", actualOOSEventsCounter, n)
- val workingUserState = computeFullMonthBillingAndSaveState
- clog.end()
- workingUserState
+ computeFullMonthBillingAndSaveState
// We had less????
case n if n < 0 ⇒
val errMsg = "Found %s out of sync events (%s less). DB must be inconsistent".format(actualOOSEventsCounter, n)
- clog.warn(errMsg)
+ Warn(logger, errMsg)
throw new AquariumInternalError(errMsg)
}
}
}
/**
- * Processes one resource event and computes relevant charges.
+ * Processes one resource event and computes relevant, incremental charges.
*
* @param resourceEvent
* @param workingUserState
* @param chargingReason
* @param billingMonthInfo
- * @param clogOpt
*/
def processResourceEvent(
resourceEvent: ResourceEventModel,
workingUserState: WorkingUserState,
chargingReason: ChargingReason,
billingMonthInfo: BillingMonthInfo,
- clogOpt: Option[ContextualLogger]
- ): Unit = {
+ updateLatestMillis: Boolean
+ ): Boolean = {
val resourceTypeName = resourceEvent.resource
val resourceTypeOpt = workingUserState.findResourceType(resourceTypeName)
if(resourceTypeOpt.isEmpty) {
- return
+ // Unknown (yet) resource, ignoring event.
+ return false
}
val resourceType = resourceTypeOpt.get
- val resourceAndInstanceInfo = resourceEvent.safeResourceInstanceInfo
val chargingBehavior = aquarium.chargingBehaviorOf(resourceType)
+ val workingResourcesState = workingUserState.workingStateOfResources.get(resourceTypeName) match {
+ case Some(existingState) ⇒
+ existingState
- val (walletEntriesCount, newTotalCredits) = chargingBehavior.chargeResourceEvent(
+ case None ⇒
+ // First time for this ChargingBehavior.
+ val newState = new WorkingResourcesChargingState(
+ details = mutable.Map(chargingBehavior.initialChargingDetails.toSeq:_*),
+ stateOfResourceInstance = mutable.Map()
+ )
+
+ workingUserState.workingStateOfResources(resourceTypeName) = newState
+ newState
+ }
+
+ val m0 = TimeHelpers.nowMillis()
+ val (walletEntriesCount, newTotalCredits) = chargingBehavior.processResourceEvent(
aquarium,
resourceEvent,
resourceType,
billingMonthInfo,
- workingUserState.workingAgreementHistory.toAgreementHistory,
- workingUserState.getChargingDataForResourceEvent(resourceAndInstanceInfo),
+ workingResourcesState,
+ workingUserState.workingAgreementHistory,
workingUserState.totalCredits,
- workingUserState.walletEntries += _,
- clogOpt
+ workingUserState.walletEntries += _
)
+ val m1 = TimeHelpers.nowMillis()
+
+ if(updateLatestMillis) {
+ workingUserState.latestUpdateMillis = m1
+ }
+ workingUserState.updateLatestResourceEventOccurredMillis(resourceEvent.occurredMillis)
workingUserState.totalCredits = newTotalCredits
+
+ true
}
def processResourceEvents(
resourceEvents: Traversable[ResourceEventModel],
workingUserState: WorkingUserState,
chargingReason: ChargingReason,
- billingMonthInfo: BillingMonthInfo,
- clogOpt: Option[ContextualLogger] = None
+ billingMonthInfo: BillingMonthInfo
): Unit = {
+ var _counter = 0
for(currentResourceEvent ← resourceEvents) {
processResourceEvent(
currentResourceEvent,
workingUserState,
chargingReason,
billingMonthInfo,
- clogOpt
+ false
)
+
+ _counter += 1
+ }
+
+ if(_counter > 0) {
+ workingUserState.latestUpdateMillis = TimeHelpers.nowMillis()
}
}
billingMonthInfo: BillingMonthInfo,
defaultResourceTypesMap: Map[String, ResourceType],
chargingReason: ChargingReason,
- userStateRecorder: UserStateModel ⇒ UserStateModel,
- clogOpt: Option[ContextualLogger]
+ userStateRecorder: UserStateModel ⇒ UserStateModel
): WorkingUserState = {
replayMonthChargingUpTo(
userStateBootstrap,
defaultResourceTypesMap,
chargingReason,
- userStateRecorder,
- clogOpt
+ userStateRecorder
)
}
* @param resourceTypesMap
* @param chargingReason
* @param userStateRecorder
- * @param clogOpt
* @return
*/
def replayMonthChargingUpTo(
userStateBootstrap: UserStateBootstrap,
resourceTypesMap: Map[String, ResourceType],
chargingReason: ChargingReason,
- userStateRecorder: UserStateModel ⇒ UserStateModel,
- clogOpt: Option[ContextualLogger]
+ userStateRecorder: UserStateModel ⇒ UserStateModel
): WorkingUserState = {
val isFullMonthBilling = billingEndTimeMillis == billingMonthInfo.monthStopMillis
val userID = userStateBootstrap.userID
- val clog = ContextualLogger.fromOther(
- clogOpt,
- logger,
- "replayMonthChargingUpTo(%s)", TimeHelpers.toYYYYMMDDHHMMSSSSS(billingEndTimeMillis))
- clog.begin()
-
- clog.debug("%s", chargingReason)
-
- val clogSome = Some(clog)
+ Debug(logger, "%s", chargingReason)
// In order to replay the full month, we start with the state at the beginning of the month.
val previousBillingMonthInfo = billingMonthInfo.previousMonth
userStateBootstrap,
resourceTypesMap,
chargingReason,
- userStateRecorder,
- clogSome
+ userStateRecorder
)
// FIXME the below comments
// 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
- clog.debug("previousBillingMonthUserState(%s) = %s".format(
+ Debug(logger, "workingUserState=%s", workingUserState)
+ Debug(logger, "previousBillingMonthUserState(%s) = %s",
previousBillingMonthInfo.toShortDebugString,
- workingUserState.toJsonString)
+ workingUserState
)
var _rcEventsCounter = 0
billingEndTimeMillis // to requested time
) { currentResourceEvent ⇒
- clog.debug("Processing %s".format(currentResourceEvent))
+ Debug(logger, "Processing %s", currentResourceEvent)
processResourceEvent(
currentResourceEvent,
workingUserState,
chargingReason,
billingMonthInfo,
- clogSome
+ false
)
_rcEventsCounter += 1
}
- clog.debug("Found %s resource events for month %s".format(_rcEventsCounter, billingMonthInfo.toShortDebugString))
+ if(_rcEventsCounter > 0) {
+ workingUserState.latestUpdateMillis = TimeHelpers.nowMillis()
+ }
+
+ Debug(logger, "Found %s resource events for month %s",
+ _rcEventsCounter,
+ billingMonthInfo.toShortDebugString
+ )
- if(isFullMonthBilling) {
+ /*if(isFullMonthBilling) {
// 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 (generatorsOfImplicitEnds, theirImplicitEnds) = workingUserState.findAndRemoveGeneratorsOfImplicitEndEvents(
)
if(generatorsOfImplicitEnds.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) {
- clog.debug("")
- clog.debug("Process implicitly issued events")
- clog.debugSeq("generatorsOfImplicitEnds", generatorsOfImplicitEnds, 0)
- clog.debugSeq("theirImplicitEnds", theirImplicitEnds, 0)
+ Debug(logger, "")
+ Debug(logger, "Process implicitly issued events")
+ DebugSeq(logger, "generatorsOfImplicitEnds", generatorsOfImplicitEnds, 0)
+ DebugSeq(logger, "theirImplicitEnds", theirImplicitEnds, 0)
}
// Now, the previous and implicitly started must be our base for the following computation, so we create an
theirImplicitEnds,
specialWorkingUserState,
chargingReason,
- billingMonthInfo,
- clogSome
+ billingMonthInfo
)
workingUserState.walletEntries ++= specialWorkingUserState.walletEntries
workingUserState.totalCredits = specialWorkingUserState.totalCredits
- }
+ }*/
- clog.end()
workingUserState
}
}