Count user sessions from logs
Company: Google
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates a candidate's ability to parse and process large-scale event logs, perform sessionization based on time gaps, handle out-of-order records and memory constraints, and reason about time and space complexity and optimization strategies in the Coding & Algorithms domain.
Constraints
- 0 <= number of log lines <= 10^6
- Each well-formed line is "timestamp,user,action" with an ISO 8601 timestamp (e.g. 2024-01-01T10:00:00).
- 1 <= timeout_minutes <= 1440
- A new session starts only when the gap is STRICTLY greater than T minutes (exactly T = same session).
- Malformed lines (fewer than 3 fields, empty user, or unparseable timestamp) are skipped.
- Users appearing only in malformed lines do not appear in the result.
Examples
Input: (["2024-01-01T10:00:00,alice,click", "2024-01-01T10:20:00,alice,view", "2024-01-01T11:30:00,alice,click"], 30)
Expected Output: {"alice": 2}
Explanation: alice has events at 10:00, 10:20, 11:30. 10:00->10:20 is a 20-min gap (<=30, same session); 10:20->11:30 is a 70-min gap (>30, new session). So 2 sessions.
Input: (["2024-01-01T10:00:00,bob,login", "2024-01-01T10:30:00,bob,logout"], 30)
Expected Output: {"bob": 1}
Explanation: Boundary case: the gap is exactly 30 minutes. Since a new session requires gap STRICTLY greater than T, both events stay in 1 session.
Input: (["2024-01-01T09:00:00,carol,a"], 15)
Expected Output: {"carol": 1}
Explanation: Single event for carol -> exactly 1 session.
Input: ([], 30)
Expected Output: {}
Explanation: No log lines at all -> empty result.
Input: (["2024-01-01T11:30:00,dave,c", "2024-01-01T10:00:00,dave,a", "2024-01-01T10:20:00,dave,b"], 30)
Expected Output: {"dave": 2}
Explanation: Out-of-order input. After sorting: 10:00, 10:20, 11:30. Same as the alice case -> 2 sessions, proving order of arrival doesn't matter.
Input: (["2024-01-01T10:00:00,eve,x", "malformed line", "not,enough", "2024-01-01T10:05:00,,noaction", "2024-01-01T11:00:00,eve,y"], 30)
Expected Output: {"eve": 2}
Explanation: Malformed lines are skipped: 'malformed line' has <3 fields, 'not,enough' has only 2 fields, and the empty-user line is dropped. eve's two valid events at 10:00 and 11:00 are 60 min apart (>30) -> 2 sessions.
Input: (["2024-01-01T10:00:00,frank,a", "2024-01-01T10:10:00,frank,b", "2024-01-01T10:00:00,grace,a", "2024-01-01T12:00:00,grace,b"], 30)
Expected Output: {"frank": 1, "grace": 2}
Explanation: Multiple users counted independently: frank's 10-min gap stays in 1 session; grace's 120-min gap splits into 2 sessions.
Hints
- Group events by user first (a hash map of user -> list of timestamps), then sort each user's timestamps. Don't assume the input is sorted.
- Count starts at 1 for any user with at least one valid event; increment only when consecutive sorted timestamps differ by MORE than T minutes.
- Be careful with the boundary: a gap of exactly T minutes must stay in the same session, so use a strict `>` comparison on the gap in seconds.
- For data that does not fit in memory, external-sort the lines by (user, timestamp) and stream them — the per-user counting logic is identical to the in-memory version.