Skip to content

Commit

Permalink
Execution tokens are now cpu credit
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessioDelConte committed Feb 14, 2024
1 parent 88ba8ec commit d60e414
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 49 deletions.
4 changes: 2 additions & 2 deletions drmaatic/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def clean(self):
class GroupAdmin(admin.ModelAdmin):
# Define columns to show
list_display = ('name', 'has_full_access', 'throttling_rate_burst',
'token_renewal_time', 'execution_token_max_amount', 'execution_token_regen_amount',
'_execution_token_regen_time')
'token_renewal_time', 'cpu_credit_max_amount', 'cpu_credit_regen_amount',
'_cpu_credit_regen_time')
form = GroupForm


Expand Down
26 changes: 13 additions & 13 deletions drmaatic/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db import models, OperationalError
from django.utils.translation import gettext_lazy
from pytimeparse.timeparse import timeparse

Expand Down Expand Up @@ -44,33 +44,33 @@ class Group(models.Model):
has_full_access = models.BooleanField(default=False, blank=False, null=False)
throttling_rate_burst = models.CharField(max_length=30, default="10/s", null=False, blank=False)
token_renewal_time = models.CharField(default="1 day", null=False, blank=False, max_length=40)
execution_token_max_amount = models.IntegerField(default=100, blank=False, null=False)
_execution_token_regen_time = models.CharField(default="30 seconds", null=False, blank=False, max_length=40)
execution_token_regen_amount = models.IntegerField(default=1, blank=False, null=False)
cpu_credit_max_amount = models.IntegerField(default=100, blank=False, null=False)
_cpu_credit_regen_time = models.CharField(default="30 seconds", null=False, blank=False, max_length=40)
cpu_credit_regen_amount = models.IntegerField(default=1, blank=False, null=False)

@property
def execution_token_regen_time(self):
return timeparse(self._execution_token_regen_time)
def cpu_credit_regen_time(self):
return timeparse(self._cpu_credit_regen_time)

@classproperty
def anonymous(self):
if table_exists('drmaatic_group', 'default'):
return self.objects.get_or_create(name='anonymous', defaults={'throttling_rate_burst': '20/s',
'token_renewal_time': '3 days',
'execution_token_max_amount': 100})[0]
'cpu_credit_max_amount': 100})[0]
else:
return Group(name='anonymous', throttling_rate_burst='20/s', token_renewal_time='3 days',
execution_token_max_amount=100)
cpu_credit_max_amount=100)

@classproperty
def registered(self):
if table_exists('drmaatic_group', 'default'):
try:
return self.objects.get_or_create(name='registered', defaults={'throttling_rate_burst': '30/s',
'token_renewal_time': '5 days',
'execution_token_max_amount': 200})[0]
else:
return Group(name='anonymous', throttling_rate_burst='20/s', token_renewal_time='3 days',
execution_token_max_amount=100)
'cpu_credit_max_amount': 200})[0]
except OperationalError:
return Group(name='registered', throttling_rate_burst='30/s', token_renewal_time='5 days',
cpu_credit_max_amount=200)

def __str__(self):
return self.name
Expand Down
24 changes: 12 additions & 12 deletions drmaatic/static/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ info:
2. **Job Management**: DRMAAtic enables users to create, manage, and monitor jobs seamlessly. Users can define jobs
with varying parameters and types, submit them to the DRM (Digital Rights Management), and access job-related information.
3. **Execution Tokens**: The system tracks and manages execution tokens, which are essential for job execution.
Users can check the number of available execution tokens, ensuring efficient resource utilization.
3. **CPU credit**: The system tracks and manages CPU credit, which are essential for job execution.
Users can check the number of available CPU credit, ensuring efficient resource utilization.
4. **Task Catalog**: DRMAAtic offers a catalog of available tasks, providing users with detailed information about
each task's parameters, requirements, and descriptions.
Expand All @@ -38,7 +38,7 @@ info:
- **Retrieve Internal JWT**: Allows users to obtain JWT tokens for authenticating with DRMAAtic, using access tokens
from external providers.
- **Number of Execution Tokens**: Provides the count of available execution tokens for the user.
- **Amount of CPU credit**: Provides the count of available CPU credit for the user.
- **Create a New Job**: Enables users to create new jobs, defining job parameters based on specific tasks.
Expand Down Expand Up @@ -125,34 +125,34 @@ paths:
example: 'Authentication header is not valid'
tags:
- 'Authentication'
/execution-tokens/:
/cpu-credit/:
get:
operationId: get_execution_tokens_number
summary: 'Number of execution tokens'
operationId: get_cpu_credit_amount
summary: 'Amount of CPU credit'
description: |
Get the number of execution tokens that the user has at the moment.
Get the amount of CPU credit that the user has at the moment.
If the user is not logged, the user is identified with the IP address.
Pass the JWT token in the Authorization header to identify the user.
parameters:
- name: required
in: query
required: false
description: 'If the user needs a specific number of execution tokens, it returns the time in seconds that the user has to wait to get the required number of tokens'
description: 'If the user needs a specific amount of CPU credit, it returns the time in seconds that the user has to wait to get the required number of tokens'
schema:
type: integer
minimum: 0
responses:
'200':
description: |
Number of execution tokens, and wait time if the user needs a specific number of tokens. <br />
Amount of CPU credit, and wait time if the user needs a specific number of tokens. <br />
**If the specified number of tokens exceeds the maximum number, "infinite" is returned as wait_time**
content:
application/json:
schema:
type: object
properties:
available_execution_tokens:
available_cpu_credit:
type: integer
example: 10
wait_time:
Expand All @@ -161,7 +161,7 @@ paths:
- string
example: 200
tags:
- 'Execution tokens'
- 'CPU credit'
/job/:
post:
operationId: createJob
Expand Down Expand Up @@ -806,7 +806,7 @@ components:
type: string
maxLength: 100
required_tokens:
description: 'Number of execution tokens that the job will need to be executed'
description: 'Amount of CPU credit that the job will need to be executed'
type: integer
maximum: 2147483647
minimum: 0
Expand Down
34 changes: 17 additions & 17 deletions drmaatic/throttles.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,33 +94,33 @@ def user(self, user):
self._user = user

@property
def user_tokens_key(self):
def user_credit_key(self):
return f"{self.TOKENS_CACHE_PREFIX}{self.user_id}"

@property
def user_current_tokens(self):
return cache.get(self.user_tokens_key, self.max_tokens)
def user_current_credit(self):
return cache.get(self.user_credit_key, self.max_tokens)

@property
def max_tokens(self):
if not self.anonymous_request:
return self.user.group.execution_token_max_amount
return self.user.group.cpu_credit_max_amount
else:
return Group.anonymous.execution_token_max_amount
return Group.anonymous.cpu_credit_max_amount

@property
def token_regen_interval(self):
if not self.anonymous_request:
return self.user.group.execution_token_regen_time
return self.user.group.cpu_credit_regen_time
else:
return Group.anonymous.execution_token_regen_time
return Group.anonymous.cpu_credit_regen_time

@property
def token_regen_amount(self):
if not self.anonymous_request:
return self.user.group.execution_token_regen_amount
return self.user.group.cpu_credit_regen_amount
else:
return Group.anonymous.execution_token_regen_amount
return Group.anonymous.cpu_credit_regen_amount

@property
def last_regen_time_key(self):
Expand All @@ -147,14 +147,14 @@ def regenerate_tokens(self):

regenerated_tokens = int(time_since_last_regen / self.token_regen_interval) * self.token_regen_amount

new_tokens = min(max_tokens, self.user_current_tokens + regenerated_tokens)
cache.set(self.user_tokens_key, new_tokens, None)
new_tokens = min(max_tokens, self.user_current_credit + regenerated_tokens)
cache.set(self.user_credit_key, new_tokens, None)
else:
self.wait_time = timedelta(seconds=self.token_regen_interval - time_since_last_regen)

def deduct_tokens(self, tokens):
new_tokens = max(0, self.user_current_tokens - tokens)
cache.set(self.user_tokens_key, new_tokens, None)
new_tokens = max(0, self.user_current_credit - tokens)
cache.set(self.user_credit_key, new_tokens, None)

def extract_user_from_request(self, request):
self.anonymous_request = False
Expand All @@ -176,7 +176,7 @@ def calculate_time_to_wait(self, tokens_requested) -> float:
if tokens_requested > self.max_tokens:
return float('inf')

tokens_to_wait = tokens_requested - self.user_current_tokens
tokens_to_wait = tokens_requested - self.user_current_credit
# If there are enough tokens, then no need to wait
if tokens_to_wait <= 0:
return 0
Expand All @@ -190,11 +190,11 @@ def allow_request(self, request, view):

self.regenerate_tokens()

request.available_execution_tokens = self.user_current_tokens
request.available_cpu_credit = self.user_current_credit

# If the request is for the view for retrieving the execution token, and the required parameter is present, then calculate the time to wait for
# the tokens to be available, if ever
if self.view_name == 'retrieve_execution_token' and 'required' in request.query_params:
if self.view_name == 'retrieve_cpu_credit' and 'required' in request.query_params:
try:
token_requested = int(request.query_params['required'])
except ValueError:
Expand All @@ -212,7 +212,7 @@ def allow_request(self, request, view):
except (Task.DoesNotExist, KeyError):
return True

if self.user_current_tokens >= required_tokens:
if self.user_current_credit >= required_tokens:
self.deduct_tokens(required_tokens)
return self.throttle_success()

Expand Down
4 changes: 2 additions & 2 deletions drmaatic/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
JobStatusView = drmaatic.job.views.JobViewSet.as_view({'get': 'status'})
JobAssignOwnershipView = drmaatic.job.views.JobViewSet.as_view({'put': 'assign_ownership'})

ExecutionTokenView = drmaatic.views.retrieve_execution_token
CPUCreditView = drmaatic.views.retrieve_cpu_credit

# Define URL patterns
urlpatterns = [
Expand All @@ -32,7 +32,7 @@
re_path(r'^job/(?P<uuid>[^/.]+)/status/$', JobStatusView),
re_path(r'^job/(?P<uuid>[^/.]+)/get_ownership/$', JobAssignOwnershipView),

re_path(r'^execution-tokens/$', ExecutionTokenView, name='execution tokens'),
re_path(r'^cpu-credit/$', CPUCreditView, name='CPU credit'),

path(r'', TemplateView.as_view(
template_name='swagger-ui.html',
Expand Down
6 changes: 3 additions & 3 deletions drmaatic/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ def retrieve_internal_token(request, **kwargs):
@authentication_classes([BearerAuthentication])
@throttle_classes([TokenBucketThrottle])
@renderer_classes([JSONRenderer])
def retrieve_execution_token(request):
def retrieve_cpu_credit(request):
# If in the request there is a required number of tokens, then calculate in how many seconds the tokens will be available, if ever
if 'required' in request.query_params:
wait_time = request.time_to_wait
wait_time_value = 'infinite' if wait_time == float('inf') else int(wait_time)
return Response(data={'available_execution_tokens': request.available_execution_tokens,
return Response(data={'available_cpu_credit': request.available_cpu_credit,
'wait_time': wait_time_value},
status=status.HTTP_200_OK)

# Based on the renderers, the response will be either JSON or plain text
return Response(data={'available_execution_tokens': request.available_execution_tokens}, status=status.HTTP_200_OK)
return Response(data={'available_cpu_credit': request.available_cpu_credit}, status=status.HTTP_200_OK)

0 comments on commit d60e414

Please sign in to comment.