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

Use monotonic clocks for ThroughputControl. #423

Open
wants to merge 1 commit into
base: tail
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions jpos/src/main/java/org/jpos/util/ThroughputControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package org.jpos.util;

import java.time.Instant;
import java.time.Duration;

/**
* ThroughputControl can be used to limit the throughput
Expand All @@ -29,8 +29,8 @@ public class ThroughputControl {
private int[] period;
private int[] max;
private int[] cnt;
private long[] start;
private long[] sleep;
private Duration[] start;
private Duration[] sleep;

/**
* @param maxTransactions ditto
Expand All @@ -50,13 +50,13 @@ public ThroughputControl (int[] maxTransactions, int[] periodInMillis) {
period = new int[l];
max = new int[l];
cnt = new int[l];
start = new long[l];
sleep = new long[l];
start = new Duration[l];
sleep = new Duration[l];
for (int i=0; i<l; i++) {
this.max[i] = maxTransactions[i];
this.period[i] = periodInMillis[i];
this.sleep[i] = Math.min(Math.max (periodInMillis[i]/10, 500L),50L);
this.start[i] = Instant.now().toEpochMilli();
this.sleep[i] = Duration.ofMillis(Math.min(Math.max (periodInMillis[i]/10, 500L),50L));
this.start[i] = Duration.ofNanos(System.nanoTime());
}
}

Expand All @@ -68,7 +68,7 @@ public ThroughputControl (int[] maxTransactions, int[] periodInMillis) {
*/
public long control() {
boolean delayed = false;
long init = Instant.now().toEpochMilli();
Duration init = Duration.ofNanos(System.nanoTime());
for (int i=0; i<cnt.length; i++) {
synchronized (this) {
cnt[i]++;
Expand All @@ -77,21 +77,21 @@ public long control() {
if (cnt[i] > max[i]) {
delayed = true;
try {
Thread.sleep (sleep[i]);
Thread.sleep (sleep[i].toMillis());
} catch (InterruptedException e) { }
}
synchronized (this) {
long now = Instant.now().toEpochMilli();
if (now - start[i] > period[i]) {
long elapsed = now - start[i];
Duration now = Duration.ofNanos(System.nanoTime());
if (now.minus(start[i]).toMillis() > period[i]) {
long elapsed = now.minus(start[i]).toMillis();
int allowed = (int) (elapsed * max[i] / period[i]);
start[i] = now;
cnt[i] = Math.max (cnt[i] - allowed, 0);
}
}
} while (cnt[i] > max[i]);
}
return delayed ? Instant.now().toEpochMilli() - init : 0L;
return delayed ? Duration.ofNanos(System.nanoTime()).minus(init).toMillis() : 0L;
}
}

25 changes: 12 additions & 13 deletions jpos/src/test/java/org/jpos/util/ThroughputControlTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,43 +23,42 @@
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.time.Instant;


public class ThroughputControlTestCase {
@Test
public void testSingleThread () throws Exception {
ThroughputControl tc = new ThroughputControl (2, 1000);
Instant start = Instant.now();
Duration start = Duration.ofNanos(System.nanoTime());
assertTrue (tc.control() == 0L, "Control should return 0L");
assertTrue (
Duration.between(start, Instant.now()).toMillis() < 1000L,
Duration.ofNanos(System.nanoTime()).minus(start).toMillis() < 1000L,
"Elapsed time should be less than one second"
);
tc.control();
assertTrue (
Duration.between(start, Instant.now()).toMillis() < 1000L,
Duration.ofNanos(System.nanoTime()).minus(start).toMillis() < 1000L,
"Elapsed time should still be less than one second"
);
tc.control();
assertTrue (
Duration.between(start, Instant.now()).toMillis() > 1000L,
Duration.ofNanos(System.nanoTime()).minus(start).toMillis() > 1000L,
"Elapsed time should be greater than one second"
);
tc.control();
assertTrue (
Duration.between(start, Instant.now()).toMillis() < 2000L,
Duration.ofNanos(System.nanoTime()).minus(start).toMillis() < 2000L,
"second transaction should be less than two seconds"
);
}
@Test
public void testFifty () throws Exception {
ThroughputControl tc = new ThroughputControl (10, 1000);
Instant start = Instant.now();
Duration start = Duration.ofNanos(System.nanoTime());
for (int i=0; i<50; i++)
tc.control();

long elapsed = Duration.between(start, Instant.now()).toMillis();
long elapsed = Duration.ofNanos(System.nanoTime()).minus(start).toMillis();
assertTrue (
elapsed >= 4000L,
"50 transactions should take at least 4 seconds but took " + elapsed
Expand All @@ -75,19 +74,19 @@ public void testDualPeriod () throws Exception {
new int[] { 100, 150 },
new int[] { 1000, 5000 }
);
Instant start = Instant.now();
Duration start = Duration.ofNanos(System.nanoTime());
for (int i=0; i<100; i++)
tc.control();

long elapsed = Duration.between(start, Instant.now()).toMillis();
long elapsed = Duration.ofNanos(System.nanoTime()).minus(start).toMillis();
assertTrue (
elapsed <= 1000L,
"100 initial transactions should take more than about one second but took " + elapsed
);
for (int i=0; i<100; i++)
tc.control();

elapsed = Duration.between(start, Instant.now()).toMillis();
elapsed = Duration.ofNanos(System.nanoTime()).minus(start).toMillis();
assertTrue (
elapsed >= 5000L,
"100 additional transactions should take more than five seconds but took " + elapsed
Expand All @@ -96,7 +95,7 @@ public void testDualPeriod () throws Exception {
@Test
public void testMultiThread() throws Exception {
final ThroughputControl tc = new ThroughputControl (2, 1000);
Instant start = Instant.now();
Duration start = Duration.ofNanos(System.nanoTime());
Thread[] t = new Thread[10];
for (int i=0; i<10; i++) {
t[i] = new Thread() {
Expand All @@ -109,7 +108,7 @@ public void run() {
for (int i=0; i<10; i++) {
t[i].join();
}
long elapsed = Duration.between(start, Instant.now()).toMillis();
long elapsed = Duration.ofNanos(System.nanoTime()).minus(start).toMillis();
assertTrue (
elapsed > 4000L && elapsed < 5000L,
"10 transactions should take about four seconds but took " + elapsed
Expand Down