client app can:
the locking service can:
The locking service should know which client services and which shared resources it's serving
minimize latency
highly consistent and reliable
1 billion of locking/unlocking requests per day yield 23k QPS on avg and ~46k QPS at peak
We need DB to store all client requests and our service events. As it's very fast write in logging style, we prefer NoSQL. Time series DB may be good choice.
lock(service_id, resource_id, auth): lock a resource
Can pass by optional timeout how long the client will need the resource for. Holding the lock for longer than the timeout is considered as deadlock and the lock will be broken by our service
access(service_id, resource_id, auth): access the locked resource. If called without locking the resource, will get an error code
unlock(service_id, resource_id, auth): unlock the resource
we can use RDB to store client service and shared resource profiles, as these are small, do not change often, and has low traffic of write and moderate traffic of read.
Client:
id: unique id of the client service. Each instance under a service group has its own unique id
name
group_id: a service may have many instances. This is the unique id for the service group
created_at: timestamp
last_updated_at: timestamp
resource_group_id: permitted resource group ids
resource_id: permitted resource unique ids, if more granular permission is needed
Resource: similar setup as client
id: unique id of resource instance
name
group_id
created_at
last_updated_at
client_group_id
client_id
We store events in time series DB, as they require very fast write, usually append only, recent ones are more critical, and large scale.
Event:
id: unique event id
type: e.g. lock, unlock, etc.
client_id
client_group_id
resource_id
resource_group_id
created_at
status: e.g. granted, denied, timed out
duration
last_updated_at
reason: for denied, we may want to know reason
see high level diagram
Lock (accept):
Lock (deny):
Access: similar as lock requests
Unlock: similar as lock requests
We'd better know all client service identity, and all shared resource identity, so we can reject unwanted or unexpected attempts to access/lock.
We can use different servers to handle different clients and resources, especially independent resources can be handled separately from others. For each cluster of servers, we can further distribute traffic based on client service unique ids. We'll need some sticky session algorithm so that each client's usage of resources can be tracked deterministically.
If some resources are files or stored in DBs, we may cache them for faster access.
For DB, events on independent resources can be put in different shards from others.
On fairness, we can start with FIFO and set a global timeout (more relaxed). If later we identify different priority of client services, we will consider priority.
For servers, two options, leader-follower pattern or leaderless pattern:
We can start from leader-follower, and if the traffic becomes too high that it's not able to handle, then we consider leaderless
When a server fails, make sure another server in the same cluster kicks in. They should continuously carry the status of current locked resources.