Accounting¶
Tracks resource usage
Rationale¶
The goal of UCloud’s accounting system is to:
Allow or deny access to a provider’s service catalog
Track consumption of resources at the workspace level
Generate visualizations and reports which track historical consumption data
Allocations: Granting access to a service catalog¶
UCloud achieves the first point by having the ability to grant resource allocations. A resource allocation
is also known as a Alloc
. These are grouped into AllocationGroup
s based on their parent, usage is
tracked at a group level up until the point any specific allocation retires. They grant a workspace the
ability to use ProductV2
s from a specific ProductCategory
. Unless otherwise stated, a workspace must
always hold an allocation to use a product. If a workspace does not hold an allocation, then the accounting
system will deny access to them. An allocation sets several limits on how the workspace can use the
products. This includes:
An allocation is only valid for the
ProductV2
s belonging to a single category. For example, if a workspace has an allocation foru1-standard
then it does not grant access tou1-gpu
.An allocation has a start date and an end date. Outside of this period, the allocation is invalid.
Each allocation have an associated quota. If a workspace is using more than the quota allows, then the provider should deny access to the
ProductV2
.
📝NOTE: It is the responsibility of the provider and not UCloud’s accounting system to deny access to a resource when the quota is exceeded. UCloud assists in this process by telling providers when a workspace exceeds their quota. But the responsibility lies with the providers, as they usually have more information. UCloud will only check for the existence of a valid allocation before forwarding the request.
Resource allocations are hierarchical in UCloud. In practice, this means that all allocations can have either 0 or 1 parent allocation. Allocations which do not have a parent are root allocations. Only UCloud administrators/provider administrators can create root allocations. Administrators of a workspace can “sub-allocate” their own allocations. This will create a new allocation which has one of their existing allocations as the parent. UCloud allows for over-allocation when creating sub-allocations. UCloud avoids over-spending by making sure that the usage in a sub-tree doesn’t exceed the quota specified in the root of the sub-tree. For example, consider the following sub-allocation created by a workspace administrator:
They can even create another which is even larger.
The sub-allocations themselves can continue to create new sub-allocations. These hierarchies can be as complex as they need to be.
In the above example neither “Research 1” or “Research 2” can have a usage above 10GB due to their parent. Similarly, if the combined usage goes above 10GB then UCloud will lock both of the allocations.
Summary¶
Important concepts:
AllocationGroup
andAlloc
: Stores a resource allocation which grants a workspace access to a ProductCategoryWalletV2
: Combines multiple allocations, belonging to the same workspace for a specific category. The accounting system spreads out usages evenly across all allocations in a Wallet.Allocations form a hierarchy. Over-allocation is allowed but the combined usage in a single allocation tree must not exceed the quota in the root.
Important calls:
accounting.v2.browseWallets
: Browse your allocations and view current usage.accounting.v2.reportUsage
: Endpoint for providers which allow them to report usage of their products.
Table of Contents¶
1. Remote Procedure Calls
Name | Description |
---|---|
browseWallets |
Browses the catalog of accessible Wallets |
retrieveDescendants |
No description |
browseProviderAllocations |
Browses allocations relevant for a specific provider |
browseWalletsInternal |
Retrieves a list of up-to-date wallets |
checkProviderUsable |
No description |
findRelevantProviders |
No description |
reportUsage |
No description |
rootAllocate |
No description |
updateAllocation |
No description |
2. Data Models
Remote Procedure Calls¶
browseWallets
¶
Browses the catalog of accessible Wallets
Request | Response | Error |
---|---|---|
AccountingV2.BrowseWallets.Request |
PageV2<WalletV2> |
CommonErrorMessage |
retrieveDescendants
¶
Request | Response | Error |
---|---|---|
AccountingV2.RetrieveDescendants.Request |
AccountingV2.RetrieveDescendants.Response |
CommonErrorMessage |
browseProviderAllocations
¶
Browses allocations relevant for a specific provider
Request | Response | Error |
---|---|---|
AccountingV2.BrowseProviderAllocations.Request |
PageV2<AccountingV2.BrowseProviderAllocations.ResponseItem> |
CommonErrorMessage |
This endpoint is only usable by providers. The endpoint will return a stable results.
browseWalletsInternal
¶
Retrieves a list of up-to-date wallets
Request | Response | Error |
---|---|---|
AccountingV2.BrowseWalletsInternal.Request |
AccountingV2.BrowseWalletsInternal.Response |
CommonErrorMessage |
This endpoint will return a list of Wallet
s which are related to the active
workspace. This is mainly for backend use. For frontend, use the browse call instead for a
paginated response
checkProviderUsable
¶
Request | Response | Error |
---|---|---|
BulkRequest<AccountingV2.CheckProviderUsable.RequestItem> |
BulkResponse<AccountingV2.CheckProviderUsable.ResponseItem> |
CommonErrorMessage |
findRelevantProviders
¶
Request | Response | Error |
---|---|---|
BulkRequest<AccountingV2.FindRelevantProviders.RequestItem> |
BulkResponse<AccountingV2.FindRelevantProviders.Response> |
CommonErrorMessage |
reportUsage
¶
Request | Response | Error |
---|---|---|
BulkRequest<UsageReportItem> |
BulkResponse<Boolean> |
CommonErrorMessage |
rootAllocate
¶
Request | Response | Error |
---|---|---|
BulkRequest<AccountingV2.RootAllocate.RequestItem> |
BulkResponse<FindByStringId> |
CommonErrorMessage |
updateAllocation
¶
Request | Response | Error |
---|---|---|
BulkRequest<AccountingV2.UpdateAllocation.RequestItem> |
Unit |
CommonErrorMessage |
Data Models¶
AllocationGroup
¶
data class AllocationGroup(
val id: Int,
val allocations: List<AllocationGroup.Alloc>,
val usage: Long,
)
AllocationGroup.Alloc
¶
data class Alloc(
val id: Long,
val startDate: Long,
val endDate: Long,
val quota: Long,
val grantedIn: Long?,
val retiredUsage: Long?,
)
AllocationGroupWithChild
¶
data class AllocationGroupWithChild(
val child: ParentOrChildWallet,
val group: AllocationGroup,
)
AllocationGroupWithParent
¶
data class AllocationGroupWithParent(
val parent: ParentOrChildWallet?,
val group: AllocationGroup,
)
ChargeDescription
¶
data class ChargeDescription(
val scope: String?,
val description: String?,
)
ParentOrChildWallet
¶
data class ParentOrChildWallet(
val projectId: String?,
val projectTitle: String,
val pi: String,
)
UsageReportItem
¶
data class UsageReportItem(
val isDeltaCharge: Boolean,
val owner: WalletOwner,
val categoryIdV2: ProductCategoryIdV2,
val usage: Long,
val description: ChargeDescription,
)
Properties
isDeltaCharge
: Boolean
Boolean
owner
: WalletOwner
WalletOwner
categoryIdV2
: ProductCategoryIdV2
ProductCategoryIdV2
usage
: Long
Long
description
: ChargeDescription
ChargeDescription
WalletOwner
¶
sealed class WalletOwner {
class Project : WalletOwner()
class User : WalletOwner()
}
WalletOwner.Project
¶
data class Project(
val projectId: String,
val type: String /* "project" */,
)
WalletOwner.User
¶
data class User(
val username: String,
val type: String /* "user" */,
)
WalletV2
¶
data class WalletV2(
val owner: WalletOwner,
val paysFor: ProductCategory,
val allocationGroups: List<AllocationGroupWithParent>,
val children: List<AllocationGroupWithChild>?,
val totalUsage: Long,
val localUsage: Long,
val maxUsable: Long,
val quota: Long,
val totalAllocated: Long,
val lastSignificantUpdateAt: Long,
)
Properties
owner
: WalletOwner
WalletOwner
paysFor
: ProductCategory
ProductCategory
allocationGroups
: List<AllocationGroupWithParent>
List<AllocationGroupWithParent>
children
: List<AllocationGroupWithChild>?
List<AllocationGroupWithChild>?
totalUsage
: Long
Long
localUsage
: Long
Long
maxUsable
: Long
Long
quota
: Long
Long
totalAllocated
: Long
Long
lastSignificantUpdateAt
: Long
Long
AccountingV2.BrowseProviderAllocations.Request
¶
The base type for requesting paginated content.
data class Request(
val itemsPerPage: Int?,
val next: String?,
val consistency: PaginationRequestV2Consistency?,
val itemsToSkip: Long?,
val filterOwnerId: String?,
val filterOwnerIsProject: Boolean?,
val filterCategory: String?,
)
Paginated content can be requested with one of the following consistency
guarantees, this greatly changes the
semantics of the call:
Consistency | Description |
---|---|
PREFER |
Consistency is preferred but not required. An inconsistent snapshot might be returned. |
REQUIRE |
Consistency is required. A request will fail if consistency is no longer guaranteed. |
The consistency
refers to if collecting all the results via the pagination API are consistent. We consider the
results to be consistent if it contains a complete view at some point in time. In practice this means that the results
must contain all the items, in the correct order and without duplicates.
If you use the PREFER
consistency then you may receive in-complete results that might appear out-of-order and can
contain duplicate items. UCloud will still attempt to serve a snapshot which appears mostly consistent. This is helpful
for user-interfaces which do not strictly depend on consistency but would still prefer something which is mostly
consistent.
The results might become inconsistent if the client either takes too long, or a service instance goes down while
fetching the results. UCloud attempts to keep each next
token alive for at least one minute before invalidating it.
This does not mean that a client must collect all results within a minute but rather that they must fetch the next page
within a minute of the last page. If this is not feasible and consistency is not required then PREFER
should be used.
📝 NOTE: Services are allowed to ignore extra criteria of the request if the next
token is supplied. This is
needed in order to provide a consistent view of the results. Clients should provide the same criterion as they
paginate through the results.
Properties
itemsPerPage
: Int?
Requested number of items per page. Supported values: 10, 25, 50, 100, 250.
Int?
next
: String?
A token requesting the next page of items
String?
consistency
: PaginationRequestV2Consistency?
Controls the consistency guarantees provided by the backend
PaginationRequestV2Consistency?
itemsToSkip
: Long?
Items to skip ahead
Long?
filterOwnerId
: String?
String?
filterOwnerIsProject
: Boolean?
Boolean?
filterCategory
: String?
String?
AccountingV2.BrowseWallets.Request
¶
The base type for requesting paginated content.
data class Request(
val itemsPerPage: Int?,
val next: String?,
val consistency: PaginationRequestV2Consistency?,
val itemsToSkip: Long?,
val filterType: ProductType?,
val includeChildren: Boolean?,
val childrenQuery: String?,
)
Paginated content can be requested with one of the following consistency
guarantees, this greatly changes the
semantics of the call:
Consistency | Description |
---|---|
PREFER |
Consistency is preferred but not required. An inconsistent snapshot might be returned. |
REQUIRE |
Consistency is required. A request will fail if consistency is no longer guaranteed. |
The consistency
refers to if collecting all the results via the pagination API are consistent. We consider the
results to be consistent if it contains a complete view at some point in time. In practice this means that the results
must contain all the items, in the correct order and without duplicates.
If you use the PREFER
consistency then you may receive in-complete results that might appear out-of-order and can
contain duplicate items. UCloud will still attempt to serve a snapshot which appears mostly consistent. This is helpful
for user-interfaces which do not strictly depend on consistency but would still prefer something which is mostly
consistent.
The results might become inconsistent if the client either takes too long, or a service instance goes down while
fetching the results. UCloud attempts to keep each next
token alive for at least one minute before invalidating it.
This does not mean that a client must collect all results within a minute but rather that they must fetch the next page
within a minute of the last page. If this is not feasible and consistency is not required then PREFER
should be used.
📝 NOTE: Services are allowed to ignore extra criteria of the request if the next
token is supplied. This is
needed in order to provide a consistent view of the results. Clients should provide the same criterion as they
paginate through the results.
Properties
itemsPerPage
: Int?
Requested number of items per page. Supported values: 10, 25, 50, 100, 250.
Int?
next
: String?
A token requesting the next page of items
String?
consistency
: PaginationRequestV2Consistency?
Controls the consistency guarantees provided by the backend
PaginationRequestV2Consistency?
itemsToSkip
: Long?
Items to skip ahead
Long?
filterType
: ProductType?
ProductType?
includeChildren
: Boolean?
Boolean?
childrenQuery
: String?
String?
AccountingV2.BrowseWalletsInternal.Request
¶
data class Request(
val owner: WalletOwner,
)
Properties
owner
: WalletOwner
WalletOwner
AccountingV2.CheckProviderUsable.RequestItem
¶
data class RequestItem(
val owner: WalletOwner,
val category: ProductCategoryIdV2,
)
AccountingV2.FindRelevantProviders.RequestItem
¶
data class RequestItem(
val username: String,
val project: String?,
val useProject: Boolean,
val filterProductType: ProductType?,
)
AccountingV2.RetrieveDescendants.Request
¶
data class Request(
val project: String,
)
Properties
project
: String
String
AccountingV2.RootAllocate.RequestItem
¶
data class RequestItem(
val category: ProductCategoryIdV2,
val quota: Long,
val start: Long,
val end: Long,
)
AccountingV2.UpdateAllocation.RequestItem
¶
data class RequestItem(
val allocationId: Long,
val newQuota: Long?,
val newStart: Long?,
val newEnd: Long?,
val reason: String,
)
AccountingV2.BrowseProviderAllocations.ResponseItem
¶
data class ResponseItem(
val id: String,
val owner: WalletOwner,
val categoryId: ProductCategory,
val notBefore: Long,
val notAfter: Long,
val quota: Long,
)
Properties
id
: String
String
owner
: WalletOwner
WalletOwner
categoryId
: ProductCategory
ProductCategory
notBefore
: Long
The earliest timestamp which allows for the balance to be consumed
Long
notAfter
: Long
The earliest timestamp at which the reported balance is no longer fully usable
Long
quota
: Long
Long
AccountingV2.BrowseWalletsInternal.Response
¶
data class Response(
val wallets: List<WalletV2>,
)
AccountingV2.CheckProviderUsable.ResponseItem
¶
data class ResponseItem(
val maxUsable: Long,
)
Properties
maxUsable
: Long
Long
AccountingV2.FindRelevantProviders.Response
¶
data class Response(
val providers: List<String>,
)
AccountingV2.RetrieveDescendants.Response
¶
data class Response(
val descendants: List<String>,
)