Skip to content

Commit dc85657

Browse files
committed
Merge pull request #126 from testmycode/fix-125
Fix #125 (potential tight loop in sending events)
2 parents dffc004 + 471145f commit dc85657

File tree

5 files changed

+72
-236
lines changed

5 files changed

+72
-236
lines changed

tmc-plugin/src/fi/helsinki/cs/tmc/spyware/EventRateLimiter.java

Lines changed: 0 additions & 106 deletions
This file was deleted.

tmc-plugin/src/fi/helsinki/cs/tmc/spyware/EventSendBuffer.java

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fi.helsinki.cs.tmc.spyware;
22

3+
import static com.google.common.base.Preconditions.checkArgument;
4+
35
import com.google.common.collect.Iterables;
46
import fi.helsinki.cs.tmc.data.Course;
57
import fi.helsinki.cs.tmc.model.CourseDb;
@@ -33,6 +35,7 @@ public class EventSendBuffer implements EventReceiver {
3335
public static final int DEFAULT_MAX_EVENTS = 64 * 1024;
3436
public static final int DEFAULT_AUTOSEND_THREHSOLD = DEFAULT_MAX_EVENTS / 2;
3537
public static final int DEFAULT_AUTOSEND_COOLDOWN = 30*1000;
38+
public static final int DEFAULT_MAX_EVENTS_PER_SEND = 500;
3639

3740
private Random random = new Random();
3841
private SpywareSettings settings;
@@ -45,6 +48,7 @@ public class EventSendBuffer implements EventReceiver {
4548
private int eventsToRemoveAfterSend = 0;
4649
private int maxEvents = DEFAULT_MAX_EVENTS;
4750
private int autosendThreshold = DEFAULT_AUTOSEND_THREHSOLD;
51+
private int maxEventsPerSend = DEFAULT_MAX_EVENTS_PER_SEND; // Servers have POST size limits
4852
private Cooldown autosendCooldown;
4953

5054

@@ -70,17 +74,17 @@ public EventSendBuffer(SpywareSettings settings, ServerAccess serverAccess, Cour
7074
}
7175

7276
public void setSendingInterval(long interval) {
77+
checkArgument(interval >= 0);
7378
this.sendingTask.setInterval(interval);
7479
}
7580

7681
public void setSavingInterval(long interval) {
82+
checkArgument(interval >= 0);
7783
this.savingTask.setInterval(interval);
7884
}
7985

8086
public void setMaxEvents(int newMaxEvents) {
81-
if (newMaxEvents <= 0) {
82-
throw new IllegalArgumentException();
83-
}
87+
checkArgument(newMaxEvents > 0);
8488

8589
synchronized (sendQueue) {
8690
if (newMaxEvents < maxEvents) {
@@ -107,7 +111,17 @@ public void setAutosendThreshold(int autosendThreshold) {
107111
}
108112

109113
public void setAutosendCooldown(long durationMillis) {
110-
this.autosendCooldown.setDurationMillis(durationMillis);
114+
checkArgument(durationMillis > 0);
115+
synchronized (sendQueue) {
116+
this.autosendCooldown.setDurationMillis(durationMillis);
117+
}
118+
}
119+
120+
public void setMaxEventsPerSend(int maxEventsPerSend) {
121+
checkArgument(maxEventsPerSend > 0);
122+
synchronized (sendQueue) {
123+
this.maxEventsPerSend = maxEventsPerSend;
124+
}
111125
}
112126

113127
public void sendNow() {
@@ -174,9 +188,6 @@ public void close() {
174188

175189

176190
private SingletonTask sendingTask = new SingletonTask(new Runnable() {
177-
// Sending too many at once may go over the server's POST size limit.
178-
private static final int MAX_EVENTS_PER_SEND = 500;
179-
180191
@Override
181192
public void run() {
182193
boolean shouldSendMore;
@@ -198,7 +209,9 @@ public void run() {
198209

199210
log.log(Level.INFO, "Sending {0} events to {1}", new Object[] { eventsToSend.size(), url });
200211

201-
doSend(eventsToSend, url);
212+
if (!tryToSend(eventsToSend, url)) {
213+
shouldSendMore = false;
214+
}
202215
} while (shouldSendMore);
203216
}
204217

@@ -207,7 +220,7 @@ private ArrayList<LoggableEvent> copyEventsToSendFromQueue() {
207220
ArrayList<LoggableEvent> eventsToSend = new ArrayList<LoggableEvent>(sendQueue.size());
208221

209222
Iterator<LoggableEvent> i = sendQueue.iterator();
210-
while (i.hasNext() && eventsToSend.size() < MAX_EVENTS_PER_SEND) {
223+
while (i.hasNext() && eventsToSend.size() < maxEventsPerSend) {
211224
eventsToSend.add(i.next());
212225
}
213226

@@ -235,7 +248,7 @@ private String pickDestinationUrl() {
235248
return url;
236249
}
237250

238-
private void doSend(final ArrayList<LoggableEvent> eventsToSend, final String url) {
251+
private boolean tryToSend(final ArrayList<LoggableEvent> eventsToSend, final String url) {
239252
CancellableCallable<Object> task = serverAccess.getSendEventLogJob(url, eventsToSend);
240253
Future<Object> future = BgTask.start("Sending stats", task);
241254

@@ -245,7 +258,7 @@ private void doSend(final ArrayList<LoggableEvent> eventsToSend, final String ur
245258
future.cancel(true);
246259
} catch (ExecutionException ex) {
247260
log.log(Level.INFO, "Sending failed", ex);
248-
return;
261+
return false;
249262
}
250263

251264
log.log(Level.INFO, "Sent {0} events successfully to {1}", new Object[] { eventsToSend.size(), url });
@@ -256,6 +269,7 @@ private void doSend(final ArrayList<LoggableEvent> eventsToSend, final String ur
256269
// then we may end up sending duplicate events later.
257270
// This will hopefully be very rare.
258271
savingTask.start();
272+
return true;
259273
}
260274

261275
private void removeSentEventsFromQueue() {

tmc-plugin/test/unit/src/fi/helsinki/cs/tmc/data/serialization/CourseListParserTest.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@
66
import fi.helsinki.cs.tmc.data.CourseListUtils;
77
import fi.helsinki.cs.tmc.data.Exercise;
88
import fi.helsinki.cs.tmc.data.ExerciseListUtils;
9+
import java.util.Date;
910
import java.util.GregorianCalendar;
1011
import java.util.List;
1112
import org.junit.Before;
1213

1314
public class CourseListParserTest {
14-
15+
1516
private CourseListParser parser;
16-
17+
1718
@Before
1819
public void setUp() {
1920
parser = new CourseListParser();
2021
}
21-
22+
2223
@Test
2324
public void itShouldParseJsonCourseLists() {
2425
String exercisesJson =
@@ -33,30 +34,33 @@ public void itShouldParseJsonCourseLists() {
3334
"checksum: \"123abc\"" +
3435
"}]";
3536
String json = "{api_version: 1, courses: [{\"name\": \"TheCourse\",\"exercises\": " + exercisesJson + "}]}";
36-
37+
3738
List<Course> result = parser.parseFromJson(json);
38-
39+
3940
Course course = CourseListUtils.getCourseByName(result, "TheCourse");
4041
assertEquals("TheCourse", course.getName());
41-
42+
4243
Exercise exercise = ExerciseListUtils.getExerciseByName(course.getExercises(), "TheExercise");
43-
44+
4445
assertEquals("TheCourse", exercise.getCourseName());
45-
46+
4647
GregorianCalendar cal = new GregorianCalendar();
4748
cal.setTime(exercise.getDeadline());
4849
assertEquals(2015, cal.get(GregorianCalendar.YEAR));
4950
assertEquals(1, cal.get(GregorianCalendar.HOUR_OF_DAY));
5051
assertEquals(30, cal.get(GregorianCalendar.MINUTE));
51-
52+
5253
assertEquals("http://example.com/courses/123/exercises/1.zip", exercise.getDownloadUrl());
5354
assertEquals("http://example.com/courses/123/exercises/1/submissions", exercise.getReturnUrl());
5455
assertTrue(exercise.isAttempted());
5556
assertFalse(exercise.isCompleted());
57+
58+
exercise.setDeadline(new Date(new Date().getTime() + 60 * 60 * 1000));
5659
assertTrue(exercise.isReturnable());
60+
5761
assertEquals("123abc", exercise.getChecksum());
5862
}
59-
63+
6064
@Test
6165
public void itShouldParseAnEmptyJsonArrayAsAnEmptyCourseList() {
6266
List<Course> empty = parser.parseFromJson("{api_version: 1, courses: []}");
@@ -67,12 +71,12 @@ public void itShouldParseAnEmptyJsonArrayAsAnEmptyCourseList() {
6771
public void itShouldThrowAnNullPointerExceptionIfTheInputIsEmpty() throws Exception {
6872
parser.parseFromJson(null);
6973
}
70-
74+
7175
@Test(expected = IllegalArgumentException.class)
7276
public void itShouldThrowAnIllegalArgumentExceptionIfTheInputIsEmpty() throws Exception {
7377
parser.parseFromJson(" ");
7478
}
75-
79+
7680
@Test
7781
public void itShouldParseANullDeadlineAsNull() {
7882
String exercisesJson =
@@ -86,9 +90,9 @@ public void itShouldParseANullDeadlineAsNull() {
8690
"returnable: true" +
8791
"}]";
8892
String json = "{api_version: 1, courses: [{\"name\": \"TheCourse\",\"exercises\": " + exercisesJson + "}]}";
89-
93+
9094
List<Course> result = parser.parseFromJson(json);
91-
95+
9296
assertNull(result.get(0).getExercises().get(0).getDeadline());
9397
}
9498
}

0 commit comments

Comments
 (0)