How to trigger a new state in a timed state machine (solidity) - solidity

I am creating a perpetual trivia dapp (for learning purposes) that has 3 stages. Each stage should last approximately 30 secs. Example:
enum Stages {
AcceptingEntryFees,
RevealQuestion,
Complete
}
modifier transitionToReveal(uint _playerCount) {
_;
if (stage == Stages.AcceptingEntryFees && now >= creationTime + 30 seconds && _playerCount > 0) {
nextStage();
}
}
modifier transitionToComplete() {
_;
if (stage == Stages.RevealQuestion && now >= creationTime + 60 seconds) {
nextStage();
}
}
modifier transitionToAcceptingFees() {
_;
if (stage == Stages.Complete && now >= creationTime + 90 seconds) {
nextStage();
}
}
function nextStage() internal {
stage = Stages(uint(stage) + 1);
}
Im struggling with a solution on how to make the stage increment once the time requirement has been met. I don't need exactly 30 seconds by any means.
Take the first transition (accepting fees).
function payEntryFee() external payable transitionToReveal(getPlayerCount()) atStage(Stages.AcceptingEntryFees) {
....
}
I currently have it set up where people can pay to play up until the 30 seconds is up. However for the stage to increment a tx has to take place. So for this setup the first person to join after the 30 seconds is up will incur the gas price and trigger the next stage. This is not ideal because what if another player doesn't show for a while.
From my research there is no way to trigger a method internally by time and trying to trigger it from the front end would require gas and then who pays it?
Can anyone think of an elegant solution to this? I would like the stage to increment every ~ 30 seconds without interruption to the game.

You would either need an external entity, like a game master web application which changes the state using its own timer and sending transactions but that would cost you gas for every single transaction.
Or you could keep track of the state on the front end (kind of like syncing) and then whenever the player interacts with the ethereum dapp to make a function call, you could do a fetchState() to get the new state and then route the player to the correct game state.
For example, after the web app frontend gets confirmation that the user has paid, it personally keeps track of the state and presents the user with the UI options related to predicted state of the dapp, then when the user sends something like "submitTriviaAnswer" the dapp would update its state and verify that the user can submit a trivia answer.
function SubmitTriviaAnswer(int responseID) public {
fetchState()
...
}

Related

How can I observe an other apps launch?

For a mental health app project, I need to intercept the startup of specific apps (like Instagram) and check if they used instagram the n-th time, possibly opening a questionair etc.
Searching for a solutions online, I came across the "android.app.usage" API. I could not get my around how to use this.
Do I need a for every running background service which does active polling with the usage api?
Or is their a way to say "run this code or start this app/service when appXY launches"?
Looking forward to any kind of input :)
Greetings Pascal
You can't listen out for an "app is being opened" intent unfortunately, Android doesn't support it. Your approach is likely the best workaround, to state it explicitly:
Have a foreground service running (so it is less likely to be killed by the OS) at all times.
Check regularly the currently running app, and see if it's the one you're trying to look for.
If this is different to last time you checked, do whatever you need to. Perhaps this will include keeping track of last time the app was opened, how many times it's been opened etc.
As a warning however, the OS won't really like this, and there's likely to be an impact on battery life. Whatever you do, make sure this check isn't happening when the screen is off, happening as infrequently as possible, and doesn't include any unnecessary computation, otherwise the user will quickly notice the negative impact.
Here's an extract from an article that looks like it'll be able to fetch the latest app even on later versions:
var foregroundAppPackageName : String? = null
val currentTime = System.currentTimeMillis()
// The `queryEvents` method takes in the `beginTime` and `endTime` to retrieve the usage events.
// In our case, beginTime = currentTime - 10 minutes ( 1000 * 60 * 10 milliseconds )
// and endTime = currentTime
val usageEvents = usageStatsManager.queryEvents( currentTime - (1000*60*10) , currentTime )
val usageEvent = UsageEvents.Event()
while ( usageEvents.hasNextEvent() ) {
usageEvents.getNextEvent( usageEvent )
Log.e( "APP" , "${usageEvent.packageName} ${usageEvent.timeStamp}" )
}

Using Ticker for polling an API in Go

There is an API that I need to periodically poll in order to check the status message.
ticker := time.NewTicker(time.Second * tickerWaitTimeSeconds)
defer ticker.Stop()
for range ticker.C {
res, err := r.client.Get(someURL)
if err != nil {
results <- err
return
}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
results <- err
return
}
status := response.Data.Attributes.CompleteStatus
if status == "COMPLETED" {
results <- nil
return
}
if status == "ERROR" {
results <- ErrFailedJob
return
}
It has worked in quite a stable way so far, but there is one possible catch if I understand how tickers work correctly.
There is this constant tickerWaitTimeSeconds that is currently set to 2 seconds. The value was chosen in such a way that the request has enough time to succeed (it's not even close to 1 second, not to mention 2) and we don't spam the API.
I suspect, however, that if for some reason the request takes longer than tickerWaitTimeSeconds there might be more GET requests to the API than necessary.
Is my suspicion valid in this case? Probably, there is something wrong with my understanding of what really happens and the Get call blocks the ticker, but I doubt that's the case.
Based on the ticker documentation:
The ticker will adjust the time interval or drop ticks to make up for slow receivers.
So the ticker should drop ticks if Get takes longer than tick interval. The Get call is not run in a separate goroutine, so you cannot submit more than one request at any given time. It may be that if one Get lasts 3 seconds, the next Get happens 1 second after the first one. If you want to call the API at least 2 seconds after the completion of the previous call, reset the ticker at each iteration.
Why do you need a ticker? Wouldn't it be easier to use time.Sleep()?
Something like this:
import (
"fmt"
"time"
)
func ticktock( d time.Duration ) {
for true {
doSomethingUseful()
time.Sleep(d)
}
}
See it in action at https://go.dev/play/p/xtg6bqNdwuk
You might want to track the elapsed time required to doSomethingUseful() and deduct that from the sleep interval, so you'd never sleep more than the specified duration, but you might sleep less.

FLOWABLE: How to change 5 minute default interval of Async job

I assume DefaultAsyncJobExecutor is the class which gets picked up by default as an implementation of AsyncExecutor interface (not sure if this assumption is right or not)
So basically I want to modify the default time-out duration of an asynchronous job, the default time-out duration is 5 minutes, which is the value of two variables:
timerLockTimeInMillis, asyncJobLockTimeInMillis in AbstractAsyncExecutor.java**
I tried to change both values with respective setter methods and tried to directly modify the value in the constructor of my custom implementation like this:
public class AsyncExecutorConfigImpl extends DefaultAsyncJobExecutor
{
// #Value( "${async.timeout.duration}" )
private int customAsyncJobLockTimeInMillis = 10 * 60 * 1000;
AsyncExecutorConfigImpl()
{
super();
setTimerLockTimeInMillis( this.customAsyncJobLockTimeInMillis );
setAsyncJobLockTimeInMillis( this.customAsyncJobLockTimeInMillis );
super.timerLockTimeInMillis = this.customAsyncJobLockTimeInMillis;
super.asyncJobLockTimeInMillis = this.customAsyncJobLockTimeInMillis;
}
}
But the values remain same because time-out still happens after 5 minutes.
Initialisation is done via an API, like start-new-process-instance, in this APIfollowing code is there to start the process instance
->Start a workflow process instance asynchronously something like this
(processInstanceName, processInstanceId)
ProcessInstance lProcessInstance = mRuntimeService.createProcessInstanceBuilder()
.processDefinitionId( lProcessDefinition.get().getId() )
.variables( processInstanceRequest.getVariables() )
.name( lProcessInstanceName )
.predefineProcessInstanceId( lProcessInstanceId )
.startAsync();
->Once this is done rest of the workflow involves service tasks and while one instance is executing, I guess the time-out occurs and instance gets restarted
-> Since, I have a listener configured I was able to see this in logs that start-event activity gets started after every 5 minutes
so for example: event-1 is the first event then this event is getting re-started after 5 minutes(duration is displayed in console logs)
Not sure, what I'm missing at this point, let me know if any other details required
if the jar file is not under your control you cannot change the default value of count because in the jar classes are compiled. You can only change the value inside of an object so you can super keyword:
class CustomImplementation extends DefaultExecutedClass{
private int custom_count=1234;
CustomImplementation(){
super();
super.count = this.custom_count;
}
}
otherwise if you really need to change the original file you have to extract it from the jar
When you are using the Flowable Spring Boot starters. Then the SpringAsyncExecutor is used, this uses the TaskExecutor from Spring. It's is provided as a bean. In order to change it's values you can use properties.
e.g.
flowable.process.async.executor.timer-lock-time-in-millis=600000
flowable.process.async.executor.async-job-lock-time-in-millis=600000
Note: Be careful when changing this. If your processes start is taking more than 5 minutes then this means that you have a transaction open for that duration of the time.

Optaplanner : View intermediate score

Is there a way keep track of the score from time to time while the solver is running?
I currently instantiate my solver as follow
SolverFactory solverFactory = SolverFactory.createFromXmlResource("solver/SolverConfig.xml");
Solver solver = solverFactory.buildSolver();
solver.addEventListener(new SolverEventListener() {
#Override
public void bestSolutionChanged(BestSolutionChangedEvent event) {
logger.info("New best score : " + event.getNewBestScore().toShortString());
}
});
solver.solve(planningSolution);
This way I am able to see the logs every time the best score changes.
However, I would like to view the score after every 100 steps or after every 10 seconds. Is that possible?
If you turn on DEBUG (or TRACE) logging, you'll see it.
If you want to listen to it in java, that's not supported in the public API, but there's PhaseLifecycleListener in the internal implementation that has no backward compatibility guarantees...

RabbitMQ API returning incorrect queue statistics

I'm working with RabbitMQ instances hosted at CloudAMQP. I'm calling the management API to get detailed queue statistics. About 1 in 10 calls to the API return invalid numbers.
The endpoint is /api/queues/[vhost]/[queue]?msg_rates_age=600&msg_rates_incr=30. I'm looking for average message rates at 30 second increments over a 10 minute span of time. Usually that returns valid data for the stats I'm interested in, e.g.
{
"messages": 16,
"consumers": 30,
"message_stats": {
"ack_details": {
"avg_rate": 441
},
"publish_details": {
"avg_rate": 441
}
}
}
But sometimes I get incorrect results for one or both "avg_rate" values, often 714676 or higher. If I then wait 15 seconds and call the same API again the numbers go back down to normal. There's no way the average over 10 minutes jumps by a multiple of 200 and then comes back down seconds later.
I haven't been able to reproduce the issue with a local install, only in production where the queue is always very busy. The data displayed on the admin web page always looks correct. Is there some other way to get the same stats accurately like the UI?