Sync parallel operation in Azure - azure

I need some achitectural suggestions about an Azure application. So, there is a queue with items, let's say it is [A, B, A, B, D].
Each distinct item in the queue will get a random category assigned and it is possible to have the same item multiple time in the queue. The category assignment is done by some worker roles which do the following: if the item has already a category assigned, it will add the item to the category, otherwise it will create a new category and the add the item. So it goes like:
D: has category? no. Create category 123. Assign [D, 123]
B: has category? no. Create category 435. Assign [B, 435]
A: has category? no. Create category 154. Assign [A, 154]
B: has category? yes. Assign [B, 435] (category already created)
... etc ...
My dillema is: how do I syncronize workers so that the same item doesn't get two categories? If two workers pick item B them it would be possible to have two categories for "B".

The only way to ensure that you don't get duplicates is to have a lock on the assigning of categories that can be accessed from both instances. The most popular way of doing this in Azure is with a lease on a blob in storage. If your items are of type Foo and you're passing the Id of the Foo through the queue, the pseudo code would look something like this:
int fooId = GetIdFromQueue();
Foo myFoo = LoadFooFromStorage(fooId);
if (myFoo.Category == null)
{
CreateLockBlobIfNoExistForFoo(fooId);
while (not GetLockOnBlobForFoo(fooId))
{
WaitForSomeTime();
}
// Need to reload the underlying item as another thread may have
// been assigning the category while we were waiting on the lock
Foo myFoo = LoadFooFromStorage(fooId);
if (myFoo.Category == null)
{
myFoo.Category = GetRandomCategory();
SaveFoo(myFoo);
}
ReleaseLease(fooId);
}
You'll need to look up some specifics on blob leases, but hopefully that's enough to get you started.

Maintain your Item/Category list in an azure table that is accessible to your worker roles, but this is still likely to end up in duplicates without some kind of throttling. For throttling, for example, set your GetMessage() in a timer loop with a reasonable wait (1-3 seconds) - and before each call to GetMessage(), call PeekMessages(5) to view but not dequeue the next 5 messages. Loop through them and assign categories any unassigned items and store them in the Azure table before calling GetMessage().

Related

Is there a way in AnyLogic to assign resources to a population of agents in use rather than individual agents?

I have a simple example of dish washers at a restaurant to illustrate the issue I am having.
Question
How can I ensure that the correct number of dish washers are seized & released when it's depended on the number of agents being used?
Problem
Using a function to assign the resources, the number of dish washers are not always correct due to different times in which sinks are used and not used.
Example
Main:
Generates dishes and randomly assigns them to one of three sinks in the exit block.
Sinks is a population of agents.
dish_washers is a ResourcePool with a capacity of 10.
Sink:
Dishes enter a queue and are entered one at a time using a hold block.
Once the dish is cleaned, the hold is unblocked to grab the next dish.
Details:
I have a shared ResourcePool of dish_washers at a restaurant.
There are 3 sinks at the restaurant.
Dishes are generated and randomly assigned to each sink.
If only 1 sink is being used, then two dish washers are needed.
However, if 2 or more sinks are being used then the number of dish washers becomes:
numberOfDishWashers = 2 + numberOfSinksInUse;
In order to change the numberOfDishWashers as more sinks are being used, I created a function that defines the numberOfDishWashers to be seized from the dish_washer ResourcePool.
int numberOfSinksUsed = 0;
int numberOfWorkersToSeize = 0;
int numberOfWorkersAlreadySeized = 0;
int numberOfWorkersToAssign = 0;
ResourcePool[][] dish_washers;
for(Sink curSink : main.sinks){
if(curSink.queue.size() > 0){
numberOfSinksUsed += 1;
}
}
numberOfWorkersAlreadySeized = main.dish_washers.busy();
numberOfWorkersToSeize = 2 + numberOfSinksUsed;
numberOfWorkersToAssign = numberOfWorkersToSeize - numberOfWorkersAlreadySeized;
dish_washers = new ResourcePool[1][numberOfWorkersToAssign];
for(int i = 0; i < numberOfWorkersToAssign; i++){
dish_washers[0][i] = main.dish_washers;
}
return dish_washers;
Error Description:
However, depending on which sink completes first & releases, the number of dish washer assigned will be incorrect. A traceln at the end of the sink process illustrates this where the numberOfDishWashers seized on the exit block doesn't match "2 + numberOfSinksInUse".
There is an instance where 3 sinks are in used but only 4 workers were seized.
Exit, Sink: C Workers Currently Seized: 4
Sinks in Use: 2
Exit, Sink: C Workers Currently Seized: 4
Sinks in Use: 3
Exit, Sink: C Workers Currently Seized: 5
Sinks in Use: 2
Exit, Sink: C Workers Currently Seized: 4
Sinks in Use: 2
Another way to look at the issue, is this Excel table outlining the current logic.
The number of busy workers doesn't match the number of busy workers there should be based on the number of active sinks.
Methods I have Tried
Custom function to release only the necessary workers to keep the correct total.
Generates an error because the resource gets assigned to the 'agent' or dish.
When the dish gets destroyed it has unreleased resources attached to it.
Passing the "sink" agent through an "enter", "seize", and "exit" block to assign the
resource to the agent "sink" instead of the dish that is generated.
Error regarding the "dish" agent being in the flowchart of the "sink" agent while the
"sink" agent is seizing the workers.
How can I ensure the correct number of dish washers are always grabbed?
So your fundamental problem here is that inside the sink you will seize a dishwasher, then the dish goes into the delay (With the number of dishwashers seized) and once out of the delay it will release what ever dishwashers it seized... But during the time it is in the delay the situation might have changed and you actually wanted to seize a different number of dishwashers for that specific sink...
Your options are to either
Remove dishes from the delay, release the correct amount of dishwashers, return back into the delay and delay for the remainder of the time...
Implement your own logic.
I would go for option 2 as option 1 means that you develop a workaround for the block created by AnyLogic and you will end up not using the blocks the way they were designed, this is unfortunately the issue with blockification
So I would have a collection inside of a sink that shows the number of dishwashers currently assigned to this sink. Then whenever a new dish enters a sink we recalculate the number of dishwashers to assign (perhaps at every sink? ) and then make the correct assignment.
Here is an example with some sample code - I did not test it but you will have something similar

How can I make a demand command loop?

I have a command named demand and need to make a limit to amounts per user. There are roles in the server named "team_role" and then there's also "2 demands", "1 demand", and "0 demands". After the user demands, I need it to role them down a demand until eventually, they hit 0. Once they hit 0, it should send them a message saying they can't demand. Here's my code for one of the teams (the Dallas Cowboys).
#bot.command(aliases=["<:DallasCowboys:788796627161710592>"])
#commands.has_any_role("Dallas Cowboys")
async def t(ctx):
guild = bot.get_guild(766292887914151949)
role_name = discord.utils.get(guild.roles, name='Free Agent')
role = discord.utils.get(guild.roles, name='Dallas Cowboys')
embed = discord.Embed()
embed.add_field(name="<a:CheckMark:768095274949935146> Successful Demand", value=f"{ctx.author.mention} has demanded from the <:DallasCowboys:788796627161710592>")
await ctx.send(embed=embed)
await ctx.author.add_roles(role_name)
await ctx.author.remove_roles(role)
Since you said I can just provide the logic:
Create a means of persistently storing the data, whether that be
json file or a database.
a) If you choose the json file, the file will consist of key value
pairs of user ids to number of demands remaining. For example, the
file might look something like this:
{ "UserDemandsRemaining" : { 12345 : 2, 45678: 1, 09876: 0 } }
b) If you choose to use to use a SQL database, create a table called
UserDemandsRemaining with two fields: UserId and
DemandsRemaining. The primary key would be UserId.
Each time a user makes a demand, you will check how many remaining
demands the user has.
If there is no matching UserId stored:
a) If you chose to use a json file, and there is no matching key in the json value associated with the UserDemandsRemaining key, then create a key-value pair with the key as the UserId that made the demand, and the value as 3.
b) If you chose to use a SQL db, and there is no matching UserId matching the UserId that made the demand in the UserDemandsRemaining table of the database, then insert a row into the table containing the UserId that made the demand and a DemandsRemaining value of 3.
Since the user isn't stored, we know that they have not yet made any demands. Execute the demand. Then decrement the `DemandsRemaining` by 1.
Else if there is a matching UserId:
if DemandsRemaining > 0:
#Execute the command. Decrement `DemandsRemaining` by 1.
else:
#Notify the user that they have no more remaining demands.
In order to reset the number of demands each person has remaining,
just set the value pair associated with the users in the json file
or the DemandsRemaining in the table to 3 for everyone.

Is there an efficient way for getting the rarest role of a member discord.js

I have implemented a feature that shows people their rarest role (the role that the least people have). I did it by looping through each of their roles and checking how many people have the role. As it turns out, if you have over like 50 roles, it delays the bots response visibly, so I was wondering if there is a more efficient way of doing the same thing. My code is here:
const rolesOfMember = member._roles
for(const role in rolesOfMember) {
var membersHavingCurrentRole = message.guild.roles.cache.get(rolesOfMember[role]).members.size
if(membersHavingCurrentRole < membersHavingRarestRole) {
membersHavingRarestRole = membersHavingCurrentRole
rarestRoleID = rolesOfMember[role]
} else if(membersHavingCurrentRole == membersHavingRarestRole) {
var rarestRole = message.guild.roles.cache.get(rarestRoleID)
var currentRole = message.guild.roles.cache.get(rolesOfMember[role])
if(rarestRole.comparePositionTo(currentRole) < 0) {
membersHavingRarestRole = membersHavingCurrentRole
rarestRoleID = rolesOfMember[role]
}
}
}
Well, one way to simplify your code would be to not have to call message.guild.roles.cache.get() 3 or so times for every role that the member has, especially if they're going to have 50+ roles. In fact, we could eliminate the need for your member._roles object entirely, and therefore eliminate the need for the for/in loop. Here's an example (tested and works):
var rolesOfMember = message.member.roles.cache;
var rarestRole = rolesOfMember.sort((roleA, roleB) => roleA.members.size - roleB.members.size).first();
var rarestRoleID = rarestRole.id;
In this example, I first get the Collection of all of the roles that the member has. Then, I use the Collection.sort() method to sort the roles based on how many members have each role in the guild (from least to greatest, so in other words from rarest to most common). Since the first role in this sorted Collection will be the rarest role that the user has, I simply call Collection.first() on the sorted Collection in order to retrieve the first and rarest Role. From there, you can use rarestRole however you'd like, I simply retrieve the role's ID and store it in a variable in my example.
You can also alter the sorting function however you'd like in order to consider the case in which two roles have the same amount of people who have them.
Relevant resources:
https://discord.js.org/#/docs/main/stable/class/Role?scrollTo=members
https://discord.js.org/#/docs/collection/master/class/Collection?scrollTo=sort

How can I keep track of all the objects I created of a class, in a list in the driver program?

I need to make a list that tracks the objects that have been called of a certain class.
These objects need to be accessible and display information when called from the list.
pizza2 = DeluxePizza("small",1,1,1,1,1)
pizza2.__str__()
pizza3 = DeluxePizza("medium",1,2,3,0,2)
pizza3.__str__()
pizza4 = DeluxePizza("large",1,0,1,0,5)
pizza4.__str__()
Whenever a new object is called, they need to be stored in the list like this-
pizzas = [pizza2, pizza3, pizza4]
I think you want to do something along the lines of the following:
pizza_orders = []
pizzas = [("small",1,1,1,1,1),("medium",1,2,3,0,2),("large",1,0,1,0,5)]
for pza in pizzas:
pizza_orders.append(DeluxePizza("small",pza[0],pza[1],pza[2],pza[3],pza[4]))
for po in pizza_orders:
print(po)
pizza_orders is a list of DeluxePizza Class instances
pizzas is a list of the specific instantiations of the Deluxe Pizza class you want to order
When you execute the po in pizza_orders list you will get the list of orders for the three pizzas

In one-to-many relationship, is there a delete rule to delete the owner when its last member is deleted?

I have a one-to-many relationship in Core Data. For example, A->B where A is a department, B is employees. A department can have many employees and one employee can only have one department.
I set A->(casacade)->B, so if I delete A, all Bs will be deleted.
On the contrary, I can also delete B. In a situation when I delete the last B, is there any delete rule so that A will get deleted automatically? Or I have to delete A programmatically? Anything I should be careful about?
In our sample app for our book https://www.objc.io/books/core-data/ we show exactly this use case.
Basically what you want to do is override prepareForDeletion (it gets called when your object is deleted). There you want to check if the array or set of objects that are no deleted is empty or not:
Here a country's prepareForDeletion checks it's (parent) continent has any non-deleted countries left:
public override func prepareForDeletion() {
guard let c = continent else { return }
if c.countries.filter({ !$0.deleted }).isEmpty {
managedObjectContext?.deleteObject(c)
}
}

Resources