Skip to content

Commit e64ae0b

Browse files
Timer tests
1 parent 3634d74 commit e64ae0b

File tree

2 files changed

+146
-1
lines changed

2 files changed

+146
-1
lines changed

kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/Timer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public Timed start() {
5353
* @param amount The amount to record in nanoseconds
5454
*/
5555
public void record(long amount) {
56-
checkArgument(amount >= 0, "Cannot record %s %s: must be >= 0", amount);
56+
checkArgument(amount >= 0, "Cannot record %s: must be >= 0", amount);
5757
this.totalTime.add(amount);
5858
this.count.increment();
5959
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (2024) The Delta Lake Project Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.delta.kernel.metrics
17+
18+
import java.util.concurrent.Callable
19+
import java.util.function.Supplier
20+
21+
import io.delta.kernel.internal.metrics.Timer
22+
import org.scalatest.funsuite.AnyFunSuite
23+
24+
class MetricsUtilsSuite extends AnyFunSuite {
25+
26+
def millisToNanos(millis: Long): Long = {
27+
millis*1000000
28+
}
29+
30+
/**
31+
* @param incrementFx Function given (duration, timer) increments the timer by about duration ms
32+
*/
33+
def testTimer(incrementFx: (Long, Timer) => Unit): Unit = {
34+
val timer = new Timer()
35+
assert(timer.count == 0)
36+
assert(timer.totalDuration == 0)
37+
38+
val incrementAmt1 = 0
39+
val paddedEndTimeOp1 = incrementAmt1 + 5 // We pad each operation by 5ms
40+
incrementFx(incrementAmt1, timer)
41+
assert(timer.count == 1)
42+
assert(timer.totalDuration >= millisToNanos(incrementAmt1) &&
43+
timer.totalDuration < millisToNanos(paddedEndTimeOp1))
44+
45+
val incrementAmt2 = 20
46+
val paddedEndTimeOp2 = paddedEndTimeOp1 + incrementAmt2 + 5 // 30
47+
incrementFx(incrementAmt2, timer)
48+
assert(timer.count == 2)
49+
assert(timer.totalDuration >= millisToNanos(incrementAmt1 + incrementAmt2) &&
50+
timer.totalDuration < millisToNanos(paddedEndTimeOp2))
51+
52+
val incrementAmt3 = 50
53+
val paddedEndTimeOp3 = paddedEndTimeOp2 + incrementAmt3 + 5 // 85
54+
incrementFx(incrementAmt3, timer)
55+
assert(timer.count == 3)
56+
assert(timer.totalDuration >= millisToNanos(incrementAmt1 + incrementAmt2 + incrementAmt3) &&
57+
timer.totalDuration < millisToNanos(paddedEndTimeOp3))
58+
}
59+
60+
test("Timer class") {
61+
// Using Timer.record()
62+
testTimer((amount, timer) => timer.record(millisToNanos(amount)))
63+
64+
// Using Timer.start()
65+
testTimer((amount, timer) => {
66+
val timed = timer.start()
67+
Thread.sleep(amount)
68+
timed.stop()
69+
})
70+
71+
// Using Timer.time(supplier)
72+
def supplier(amount: Long): Supplier[Long] = {
73+
() => {
74+
Thread.sleep(amount)
75+
amount
76+
}
77+
}
78+
testTimer((amount, timer) => {
79+
timer.time(supplier(amount))
80+
})
81+
82+
// Using Timer.timeCallable
83+
def callable(amount: Long): Callable[Long] = {
84+
() => {
85+
Thread.sleep(amount)
86+
amount
87+
}
88+
}
89+
testTimer((amount, timer) => {
90+
timer.timeCallable(callable(amount))
91+
})
92+
93+
// Using Timer.time(runnable)
94+
def runnable(amount: Long): Runnable = {
95+
() => Thread.sleep(amount)
96+
}
97+
testTimer((amount, timer) => {
98+
timer.time(runnable(amount))
99+
})
100+
}
101+
102+
test("Timer class with exceptions") {
103+
// We catch the exception outside of the functional interfaces
104+
def catchException(fx: () => Any): Unit = {
105+
try {
106+
fx.apply()
107+
} catch {
108+
case _: Exception =>
109+
}
110+
}
111+
112+
// Using Timer.time(supplier)
113+
def supplier(amount: Long): Supplier[Long] = {
114+
() => {
115+
Thread.sleep(amount)
116+
throw new RuntimeException()
117+
}
118+
}
119+
testTimer((amount, timer) => {
120+
catchException(() => timer.time(supplier(amount)))
121+
})
122+
123+
// Using Timer.timeCallable
124+
def callable(amount: Long): Callable[Long] = {
125+
() => {
126+
Thread.sleep(amount)
127+
throw new RuntimeException()
128+
}
129+
}
130+
testTimer((amount, timer) => {
131+
catchException(() => timer.timeCallable(callable(amount)))
132+
})
133+
134+
// Using Timer.time(runnable)
135+
def runnable(amount: Long): Runnable = {
136+
() => {
137+
Thread.sleep(amount)
138+
throw new RuntimeException()
139+
}
140+
}
141+
testTimer((amount, timer) => {
142+
catchException(() => timer.time(runnable(amount)))
143+
})
144+
}
145+
}

0 commit comments

Comments
 (0)