Question

I am currently into the development of a play-powered (v. 1.2.4) application where users can perform certain tasks and gain rewards for doing so. These tasks require some energy, which will be refilled over time. The basic setting is as follows:

public class User extends Model {
    public Long energy;
    public Long maxenergy;
    public Long cooldown = Long.valueOf(300);
}

public class Task extends Controller {
    public static void perform(Long id) {
        User user = User.findById(id).first();

        // do some complex task here...

        user.energy--;
        user.save();
        Task.list();
    }
}

Now I want to refill the energy of the user after the cooldown (5 min). Assuming the user has 10/10 energy points and I want to refill a point 5 minutes after it has been used, I could easily use a job for this:

public class EnergyHealer extends Job {
    public Long id;

    public EnergyHealer(Long id) {
        this.id = id;
    }

    public void doJob() throws Exception {
        User user = User.findById(id);
        user.energy++;
        if (user.energy > user.maxenergy) {
            user.energy = user.maxenergy;
        }
        user.save()
    }
}

... and call it in my controller right after the task has been competed:

new EnergyHealer(user.id).in(user.cooldown);

My problem here is, that in this case jobs are scheduled concurrently, thus if the user performs a task 2 seconds after he performed a previous task the first energy point is refilled after 5 mins, while the subsequent point is refilled only 2 seconds later.

So, I need the jobs to be serialized, e.g., assuming I have 8 of 10 energy points, it should take exactly 10 minutes until all energy points are refilled.

On a related note: users have different levels and gain experience for completing tasks, once a certain threshold is reached. Their level is increased and all energy points are refilled, no matter how many of them have been used in the previous level, hence some jobs may become obsolete by the time they are executed.

Considering some thousand users, jobs may not be the perfect choice at all, so if someone has an idea on how to achieve the described scenario, I'm glad for any help!

Was it helpful?

Solution

I think you just have your Job scheduling wrong. Rather than kick off your job every time they perform an action, you should simply have a facade or something that will only kick off a job if one does not already exist for the user.

  1. If the job does not already exist, create it, otherwise do nothing

  2. then this should add 1 energy,

  3. check if energy is full, if it is, end

  4. if not, pause for 5 minutes

  5. go back to 2

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top