Skip to content

Commit d1d985c

Browse files
diegomtz5claude
andcommitted
Preserve event timestamp across harvests for session attributes
- Remove timestamp reset from empty() method in both event systems - Add resetTimestamp() method for explicit session clear - Update tests for new behavior - Ensures session attributes sent on every harvest after first 60s Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 90c2997 commit d1d985c

File tree

10 files changed

+107
-8
lines changed

10 files changed

+107
-8
lines changed

Agent/Analytics/NRMAAnalytics.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,10 +1127,12 @@ - (void) clearLastSessionsAnalytics {
11271127
if([NRMAFlags shouldEnableNewEventSystem]){
11281128
[_sessionAttributeManager removeAllSessionAttributes];
11291129
[_eventManager empty];
1130+
[_eventManager resetTimestamp];
11301131
} else {
11311132
try {
11321133
_analyticsController->clearAttributesDuplicationStore();
11331134
_analyticsController->clearEventsDuplicationStore();
1135+
_analyticsController->resetEventTimestamp();
11341136
} catch (std::exception& e) {
11351137
NRLOG_AGENT_VERBOSE(@"Failed to clear last sessions' analytcs, %s",e.what());
11361138
} catch (...) {

Agent/Analytics/NRMAEventManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
2626
- (BOOL)didReachMaxQueueTime:(NSTimeInterval)currentTimeMilliseconds;
2727
- (BOOL)addEvent:(id<NRMAAnalyticEventProtocol>)event;
2828
- (void)empty;
29+
- (void)resetTimestamp;
2930
- (nullable NSString *)getEventJSONStringWithError:(NSError *__autoreleasing *)error clearEvents:(BOOL)clearEvents;
3031

3132
+ (nullable NSString *)getLastSessionEventsFromFilename:(NSString *)filename;

Agent/Analytics/NRMAEventManager.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,14 @@ - (void)empty {
122122
@synchronized (events) {
123123
[events removeAllObjects];
124124
[_persistentStore clearAll];
125-
oldestEventTimestamp = 0;
126125
totalAttemptedInserts = 0;
127126
}
128127
}
129128

129+
- (void)resetTimestamp {
130+
oldestEventTimestamp = 0;
131+
}
132+
130133
- (nullable NSString *)getEventJSONStringWithError:(NSError *__autoreleasing *)error clearEvents:(BOOL)clearEvents {
131134
NSString *eventJsonString = nil;
132135
@synchronized (events) {

Agent/Utilities/NRLogger.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ - (NSMutableDictionary*) commonBlockDict {
335335
if (NRSessionId) [commonAttributes setObject:NRSessionId forKey:NRLogMessageSessionIdKey];
336336
[commonAttributes setObject:NRLogMessageMobileValue forKey:NRLogMessageInstrumentationProviderKey];
337337
if (name) [commonAttributes setObject:name forKey:NRLogMessageInstrumentationNameKey];
338-
[commonAttributes setObject:[NRMAAgentConfiguration connectionInformation].deviceInformation.agentVersion forKey:NRLogMessageInstrumentationVersionKey];
338+
[commonAttributes setObject:[NRMAAgentConfiguration connectionInformation].deviceInformation.platformVersion ?: [NRMAAgentConfiguration connectionInformation].deviceInformation.agentVersion forKey:NRLogMessageInstrumentationVersionKey];
339339
if (nativePlatform) [commonAttributes setObject:nativePlatform forKey:NRLogMessageInstrumentationCollectorKey];
340340
if (nrAppId) [commonAttributes setObject:nrAppId forKey:NRLogMessageAppIdKey];
341341

Tests/Unit-Tests/NewRelicAgentTests/Analytics-Tests/TestIntegratedEventManager.m

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,32 +187,65 @@ - (void)testEmptyEvents {
187187
XCTAssertEqual(emptyDecode.count, 0);
188188
}
189189

190-
- (void)testEmptyEventsResetOldestEventTime {
190+
- (void)testEmptyEventsDoesNotResetOldestEventTime {
191191
// Given
192192
NRMACustomEvent *customEventOne = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 1"
193193
timestamp:1000
194194
sessionElapsedTimeInSeconds:20
195195
withAttributeValidator:agreeableAttributeValidator];
196-
196+
197197
NRMACustomEvent *customEventTwo = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 2"
198198
timestamp:1000
199199
sessionElapsedTimeInSeconds:20
200200
withAttributeValidator:agreeableAttributeValidator];
201-
201+
202202
NRMACustomEvent *customEventThree = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 3"
203203
timestamp:1000
204204
sessionElapsedTimeInSeconds:20
205205
withAttributeValidator:agreeableAttributeValidator];
206206
[sut setMaxEventBufferTimeInSeconds:1];
207-
207+
208208
[sut addEvent:customEventOne];
209209
[sut addEvent:customEventTwo];
210210
[sut addEvent:customEventThree];
211-
211+
212212
XCTAssertTrue([sut didReachMaxQueueTime:2000]);
213213

214+
// When - empty() is called (during normal harvest)
214215
[sut empty];
215216

217+
// Then - timestamp should persist (matching Android behavior)
218+
XCTAssertTrue([sut didReachMaxQueueTime:2000]);
219+
}
220+
221+
- (void)testResetTimestampResetsOldestEventTime {
222+
// Given
223+
NRMACustomEvent *customEventOne = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 1"
224+
timestamp:1000
225+
sessionElapsedTimeInSeconds:20
226+
withAttributeValidator:agreeableAttributeValidator];
227+
228+
NRMACustomEvent *customEventTwo = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 2"
229+
timestamp:1000
230+
sessionElapsedTimeInSeconds:20
231+
withAttributeValidator:agreeableAttributeValidator];
232+
233+
NRMACustomEvent *customEventThree = [[NRMACustomEvent alloc] initWithEventType:@"Custom Event 3"
234+
timestamp:1000
235+
sessionElapsedTimeInSeconds:20
236+
withAttributeValidator:agreeableAttributeValidator];
237+
[sut setMaxEventBufferTimeInSeconds:1];
238+
239+
[sut addEvent:customEventOne];
240+
[sut addEvent:customEventTwo];
241+
[sut addEvent:customEventThree];
242+
243+
XCTAssertTrue([sut didReachMaxQueueTime:2000]);
244+
245+
// When - resetTimestamp() is explicitly called (during session clear)
246+
[sut resetTimestamp];
247+
248+
// Then - timestamp should be reset
216249
XCTAssertFalse([sut didReachMaxQueueTime:2000]);
217250
}
218251

libMobileAgent/src/Analytics/include/Analytics/AnalyticsController.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ namespace NewRelic {
218218

219219
void clearAttributesDuplicationStore();
220220

221+
void resetEventTimestamp();
222+
221223
static std::shared_ptr <NRJSON::JsonArray> fetchDuplicatedEvents(
222224
PersistentStore<std::string, AnalyticEvent> &eventStore,
223225
bool shouldClearStore);

libMobileAgent/src/Analytics/include/Analytics/EventManager.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ namespace NewRelic {
102102
void setMaxBufferSize(unsigned int size); //sets max buffer size
103103
bool didReachMaxQueueTime(unsigned long long currentTimestamp_ms); //checks if oldest event timestamp exceededs max queue time
104104
void empty(); //removes all events in _events;
105+
void resetTimestamp(); //resets _oldest_event_timestamp_ms to 0 (for session clear)
105106
};
106107
}
107108
#endif

libMobileAgent/src/Analytics/src/AnalyticsController.cxx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,10 @@ namespace NewRelic {
10021002
_attributeDuplicationStore.clear();
10031003
}
10041004

1005+
void AnalyticsController::resetEventTimestamp() {
1006+
_eventManager.resetTimestamp();
1007+
}
1008+
10051009
const char *AnalyticsController::getPersistentAttributeStoreName() {
10061010
return ATTRIBUTE_STORE_DB_FILENAME;
10071011
}

libMobileAgent/src/Analytics/src/EventManager.cxx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ void EventManager::empty() {
4141
lock1.lock();
4242
_events.clear();
4343
_eventDuplicationStore.clear();
44-
_oldest_event_timestamp_ms = 0;
4544
//we're empty so let's reset the total number of attempted inserts.
4645
_total_attempted_inserts = 0;
4746
}
4847

48+
void EventManager::resetTimestamp() {
49+
_oldest_event_timestamp_ms = 0;
50+
}
51+
4952
std::string EventManager::createKey(std::shared_ptr<AnalyticEvent> event) {
5053
std::stringstream ss;
5154
ss << *event;

libMobileAgent/test/AnalyticsTest/EventManager_test.cxx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,5 +286,55 @@ TEST_F(EventManagerTest, testBadSerializedEvent) {
286286

287287
ASSERT_THROW(manager.newEvent(strs2), std::runtime_error);
288288
}
289+
290+
TEST_F(EventManagerTest, testEmptyDoesNotResetTimestamp) {
291+
EventManager manager{store};
292+
293+
// Set max buffer time to 1 second
294+
manager.setMaxBufferTime(1);
295+
296+
// Add events at timestamp 1000ms
297+
auto event1 = manager.newCustomMobileEvent("custom1", 1000, 1, validator);
298+
auto event2 = manager.newCustomMobileEvent("custom2", 1000, 1, validator);
299+
auto event3 = manager.newCustomMobileEvent("custom3", 1000, 1, validator);
300+
301+
manager.addEvent(event1);
302+
manager.addEvent(event2);
303+
manager.addEvent(event3);
304+
305+
// Check at 2000ms - should have reached max queue time (1 second elapsed)
306+
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));
307+
308+
// Call empty() (simulating normal harvest)
309+
manager.empty();
310+
311+
// Timestamp should persist (matching Android behavior)
312+
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));
313+
}
314+
315+
TEST_F(EventManagerTest, testResetTimestampResetsTimestamp) {
316+
EventManager manager{store};
317+
318+
// Set max buffer time to 1 second
319+
manager.setMaxBufferTime(1);
320+
321+
// Add events at timestamp 1000ms
322+
auto event1 = manager.newCustomMobileEvent("custom1", 1000, 1, validator);
323+
auto event2 = manager.newCustomMobileEvent("custom2", 1000, 1, validator);
324+
auto event3 = manager.newCustomMobileEvent("custom3", 1000, 1, validator);
325+
326+
manager.addEvent(event1);
327+
manager.addEvent(event2);
328+
manager.addEvent(event3);
329+
330+
// Check at 2000ms - should have reached max queue time (1 second elapsed)
331+
ASSERT_TRUE(manager.didReachMaxQueueTime(2000));
332+
333+
// Call resetTimestamp() (simulating session clear)
334+
manager.resetTimestamp();
335+
336+
// Timestamp should be reset
337+
ASSERT_FALSE(manager.didReachMaxQueueTime(2000));
338+
}
289339
} // namespace NewRelic
290340

0 commit comments

Comments
 (0)