Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flank needs to respect the timeout value as that's a cap for billing purposes. #865

Merged
merged 12 commits into from
Jun 29, 2020

Conversation

adamfilipow92
Copy link
Contributor

@adamfilipow92 adamfilipow92 commented Jun 23, 2020

Fixes #836

Flank billing calculation rules:

  1. test execution time is rounded up to next minute and used in calculations
Execution time Rounded value
3m 22s 4m
15m 01s 16m
5m 00s 5m
  1. if test execution time is smaller then test timeout (default 15m) round up execution time and use it in billing calculations
  2. if test execution time is bigger then test timeout use timeout value in billing calculations
  3. if test execution state is ERROR flank should skip billing calculation and assume $0.00

Examples:

  1. execution time is 13m 24s and timeout is default (15m), execution time is smaller then timeout therefor is rounded up to 14m and used in billing calculations
  2. execution time is 20m 13s and timeout is set to 18m, execution time is bigger then timeout therefor timeout value (18m) is used in billing calculations

Test Plan

How do we know the code works?

Flank needs to respect the timeout value as that's a cap for billing purposes, and round time to nearest minutes.

  1. Run flank with yml

gcloud:
  app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
  robo-script: ./src/test/kotlin/ftl/fixtures/test_app_cases/MainActivity_robo_script.json
  environment-variables:
    coverage: true
    coverageFilePath: /sdcard/
    clearPackageData: true

flank should return cost report with 2 min


CostReport
  Virtual devices
    $0.03 for 2m

  1. Add in gcloud section timeout for 60s

gcloud:
  app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
  robo-script: ./src/test/kotlin/ftl/fixtures/test_app_cases/MainActivity_robo_script.json
  environment-variables:
    coverage: true
    coverageFilePath: /sdcard/
    clearPackageData: true
  timeout: 60s

flank should return cost report with 1 min


CostReport
  Virtual devices
    $0.02 for 1m

  1. To check timeout is respected only when is lower than execution time set timeout to 5m and run

gcloud:
  app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
  robo-script: ./src/test/kotlin/ftl/fixtures/test_app_cases/MainActivity_robo_script.json
  environment-variables:
    coverage: true
    coverageFilePath: /sdcard/
    clearPackageData: true
  timeout: 5m

flank should return cost report with 2 min


CostReport
  Virtual devices
    $0.03 for 2m

Checklist

  • Documented
  • Unit tested
  • release_notes.md updated

@adamfilipow92 adamfilipow92 self-assigned this Jun 23, 2020
@bootstraponline
Copy link
Contributor

Cost report should contains seconds not rounded up minutes.

Hey, this is not a correct description of the problem. FTL does round to the nearest minute and that's how they bill.

The problem is that Flank needs to respect the timeout value as that's a cap for billing purposes.

Flank is over-reporting billable minutes for tests that hit their timeout limit. FTL caps the billed time at the timeout value, but Flank seems to report total time (e.g. a Robo test has a 1 minute timeout, but actually runs 75sec before the cancellation happens. FTL bills for 1m, but Flank says 2m.

@adamfilipow92 adamfilipow92 changed the title Print cost report with seconds Flank needs to respect the timeout value as that's a cap for billing purposes. Jun 24, 2020
@adamfilipow92
Copy link
Contributor Author

adamfilipow92 commented Jun 24, 2020

Cost report should contains seconds not rounded up minutes.

Hey, this is not a correct description of the problem. FTL does round to the nearest minute and that's how they bill.

The problem is that Flank needs to respect the timeout value as that's a cap for billing purposes.

Flank is over-reporting billable minutes for tests that hit their timeout limit. FTL caps the billed time at the timeout value, but Flank seems to report total time (e.g. a Robo test has a 1 minute timeout, but actually runs 75sec before the cancellation happens. FTL bills for 1m, but Flank says 2m.

Thanks for fast feedback! I check rounding time strategy and flank rounding time always up. Now if we have 1m 7s flank round it to 2 mins. I think this is problem too because flank should round 1m 7s to 1m right?

@bootstraponline
Copy link
Contributor

Thanks for fast feedback! I check rounding time strategy and flank rounding time always up. Now if we have 1m 7s flank round it to 2 mins. I think this is problem too because flank should round 1m 7s to 1m right?

Firebase will bill us for 2m if a shard runs for 1m 7s. The only exception is if there's a timeout for 1m, then we'll only ever be billed up to the timeout.

@pawelpasterz
Copy link
Contributor

Thanks for fast feedback! I check rounding time strategy and flank rounding time always up. Now if we have 1m 7s flank round it to 2 mins. I think this is problem too because flank should round 1m 7s to 1m right?

Firebase will bill us for 2m if a shard runs for 1m 7s. The only exception is if there's a timeout for 1m, then we'll only ever be billed up to the timeout.

agreed, flank needs to take timeout value under consideration.
This implementation will be improved :)

@pawelpasterz pawelpasterz self-assigned this Jun 24, 2020
@adamfilipow92 adamfilipow92 force-pushed the #836-Fix-billable-minutes branch from 1d7944d to 522ffbf Compare June 24, 2020 16:22
@pawelpasterz
Copy link
Contributor

Description updated

@bootstraponline
Copy link
Contributor

Feedback from Paul Davis via Slack:

That looks correct, as long as you also report zero billing for all infrastructure errors. Unfortunately, infra errors can be marked on either the last attempt within an execution, or on the execution itself.
In FTL billing logic, that looks like

if (!execution.attempts().isEmpty()) {
    Attempt lastAttempt = execution.attempts().get(execution.attempts().size() - 1);
    if (lastAttempt.getAttemptStatus() != AttemptStatus.ERROR && execution.state() != TestState.ERROR) {
        // do rounding/timeout calculations as described in the pull request
    }
}

@pawelpasterz
Copy link
Contributor

Feedback from Paul Davis via Slack:

That looks correct, as long as you also report zero billing for all infrastructure errors. Unfortunately, infra errors can be marked on either the last attempt within an execution, or on the execution itself.
In FTL billing logic, that looks like

if (!execution.attempts().isEmpty()) {
    Attempt lastAttempt = execution.attempts().get(execution.attempts().size() - 1);
    if (lastAttempt.getAttemptStatus() != AttemptStatus.ERROR && execution.state() != TestState.ERROR) {
        // do rounding/timeout calculations as described in the pull request
    }
}

@bootstraponline
That's valuable information, thanks! (I was not able to find such info in FTL docs)

@pawelpasterz pawelpasterz force-pushed the #836-Fix-billable-minutes branch from bf58fa6 to e80131f Compare June 25, 2020 03:39
@bootstraponline
Copy link
Contributor

I was not able to find such info in FTL docs

we should probably document clearly in the Kotlin file what the business rules for billing are. 🙂

@@ -27,7 +29,7 @@ class SavedMatrix(matrix: TestMatrix) {

var billableVirtualMinutes: Long = 0
private set
var billablePhysicalMinutes: Long = 0
var billablePhysicalSecondsMinutes: Long = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we think about other name, because I am confused if we use seconds or minutes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, definitely. We have yet another round of refactor ahead of us

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, its my fail. Its should be billablePhysicalMinutes

.setResultStorage(createResultsStorage())
.setState(MatrixState.FINISHED)
.setTestMatrixId("123")
.setTestExecutions(
listOf(
createStepExecution(executionId, "")
)
)
),
testTimeout = defaultTestTimeout
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make it as a default parameter value

Copy link
Contributor

@pawelpasterz pawelpasterz Jun 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are thinking about default value for SavedMatrix constructor I don't think it's good idea. That would lead us to 2 places where default value (common args and SavedMatrix constructor) is hold, easy to forget about one when changing/updating second

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we get testTimeout from api response so we don't need pass them by constructor.

@pawelpasterz
Copy link
Contributor

pawelpasterz commented Jun 26, 2020

According to Paul Davis, we can check just TestExecution for state and if it's ERROR skip billing

Just check the TestExecution state, and report $0.00 if the state is ERROR.

Description updated

Another comment:

Flank uses a Spark free-billing-tier project, so the billing step is skipped, which is why I think you're seeing that (deviceUsageDuration) null value.

In theory we could use deviceUsageDuration to exact billing calculation and assume $0.00 in case when it's null -- this needs to be verified

cc @bootstraponline @adamfilipow92

@bootstraponline
Copy link
Contributor

In theory we could use deviceUsageDuration to exact billing calculation and assume $0.00 in case when it's null -- this needs to be verified

I'd like to support both Spark plan and the paid plan. It's useful to know what Flank runs will cost.

We probably need to setup a paid FTL account to verify deviceUsageDuration works as expected.

@pawelpasterz
Copy link
Contributor

pawelpasterz commented Jun 26, 2020

In theory we could use deviceUsageDuration to exact billing calculation and assume $0.00 in case when it's null -- this needs to be verified

I'd like to support both Spark plan and the paid plan. It's useful to know what Flank runs will cost.

Therefor deviceUsageDuration would be best for this.

We probably need to setup a paid FTL account to verify deviceUsageDuration works as expected.

I might have access to the project with paid plan -- will check it

@pawelpasterz
Copy link
Contributor

@bootstraponline
I've tested fetching value from deviceUsageDuration on project with Blaze without any result -- null is all I got. I think for now we should go with 'error check' approach and come back to this once API would be more...reliable?

With 'error check' we would have correct numbers as well

cc @adamfilipow92

@adamfilipow92
Copy link
Contributor Author

So this is plan:

  1. Skip billing if testExecution.state == ERROR.
  2. Calculate cost even if we on spark plan.

@adamfilipow92 adamfilipow92 marked this pull request as ready for review June 26, 2020 15:41
@bootstraponline
Copy link
Contributor

I've tested fetching value from deviceUsageDuration on project with Blaze without any result -- null is all I got. I think for now we should go with 'error check' approach and come back to this once API would be more...reliable?

With 'error check' we would have correct numbers as well

That sounds good. The FTL API is often completely broken so it's good that we always double check. 🙂

jan-goral
jan-goral previously approved these changes Jun 29, 2020
piotradamczyk5
piotradamczyk5 previously approved these changes Jun 29, 2020
@adamfilipow92 adamfilipow92 merged commit 3978611 into master Jun 29, 2020
@adamfilipow92 adamfilipow92 deleted the #836-Fix-billable-minutes branch June 29, 2020 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix billable minutes
5 participants