-
Notifications
You must be signed in to change notification settings - Fork 38
fix: support arbitrarily long list of user attributes in batch events #386
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
fe2b4f6
compress batch events for workmanager
jaeopt 0c7d5cd
clean up
jaeopt 6669d8e
add compress performance check
jaeopt d58299f
compare compress algs
jaeopt b4674b7
clean up
jaeopt cec3071
base64 for event compression
jaeopt 64f23fc
more compress tests
jaeopt 66ab1f8
more tests
jaeopt b5a5d24
fix random texts for compress
jaeopt 6d24da3
fix byte64 for backward compat
jaeopt b355942
Update event-handler/build.gradle
jaeopt 170e3d9
clean up
jaeopt ecf0fd4
clean up
jaeopt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventHandlerUtils.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.optimizely.ab.android.event_handler; | ||
|
||
import static java.util.zip.Deflater.BEST_COMPRESSION; | ||
|
||
import android.os.Build; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.RequiresApi; | ||
import androidx.annotation.VisibleForTesting; | ||
|
||
import org.apache.commons.codec.binary.Base64; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.nio.charset.Charset; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.zip.Deflater; | ||
import java.util.zip.GZIPInputStream; | ||
import java.util.zip.GZIPOutputStream; | ||
import java.util.zip.Inflater; | ||
|
||
public class EventHandlerUtils { | ||
|
||
private static final int BUFFER_SIZE = 32*1024; | ||
|
||
public static String compress(@NonNull String uncompressed) throws IOException { | ||
byte[] data = uncompressed.getBytes(); | ||
|
||
final Deflater deflater = new Deflater(); | ||
//deflater.setLevel(BEST_COMPRESSION); | ||
deflater.setInput(data); | ||
|
||
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length)) { | ||
deflater.finish(); | ||
final byte[] buffer = new byte[BUFFER_SIZE]; | ||
while (!deflater.finished()) { | ||
final int count = deflater.deflate(buffer); | ||
outputStream.write(buffer, 0, count); | ||
} | ||
|
||
byte[] bytes = outputStream.toByteArray(); | ||
// encoded to Base64 (instead of byte[] since WorkManager.Data size is unexpectedly expanded with byte[]). | ||
return encodeToBase64(bytes); | ||
} | ||
} | ||
|
||
public static String decompress(@NonNull String base64) throws Exception { | ||
byte[] data = decodeFromBase64(base64); | ||
|
||
final Inflater inflater = new Inflater(); | ||
inflater.setInput(data); | ||
|
||
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length)) { | ||
byte[] buffer = new byte[BUFFER_SIZE]; | ||
while (!inflater.finished()) { | ||
final int count = inflater.inflate(buffer); | ||
outputStream.write(buffer, 0, count); | ||
} | ||
|
||
return outputStream.toString(); | ||
} | ||
} | ||
|
||
static String encodeToBase64(byte[] bytes) { | ||
// - org.apache.commons.Base64 is used (instead of android.util.Base64) for unit testing | ||
// - encodeBase64() for backward compatibility (instead of encodeBase64String()). | ||
String base64 = ""; | ||
if (bytes != null) { | ||
byte[] encoded = Base64.encodeBase64(bytes); | ||
base64= new String(encoded); | ||
} | ||
return base64; | ||
} | ||
|
||
static byte[] decodeFromBase64(String base64) { | ||
return Base64.decodeBase64(base64.getBytes()); | ||
} | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
...-handler/src/test/java/com/optimizely/ab/android/event_handler/EventHandlerUtilsTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.optimizely.ab.android.event_handler; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import androidx.work.Data; | ||
|
||
import org.junit.Test; | ||
|
||
import java.io.IOException; | ||
|
||
public class EventHandlerUtilsTest { | ||
|
||
@Test | ||
public void compressAndUncompress() throws Exception { | ||
String str = makeRandomString(1000); | ||
|
||
String compressed = EventHandlerUtils.compress(str); | ||
assert(compressed.length() < (str.length() * 0.5)); | ||
|
||
String uncompressed = EventHandlerUtils.decompress(compressed); | ||
assertEquals(str, uncompressed); | ||
} | ||
|
||
@Test(timeout=30000) | ||
public void measureCompressionDelay() throws Exception { | ||
int maxEventSize = 100000; // 100KB (~100 attributes) | ||
int count = 3000; | ||
|
||
String body = EventHandlerUtilsTest.makeRandomString(maxEventSize); | ||
|
||
long start = System.currentTimeMillis(); | ||
for (int i = 0; i < count; i++) { | ||
EventHandlerUtils.compress(body); | ||
} | ||
long end = System.currentTimeMillis(); | ||
float delayCompress = ((float)(end - start))/count; | ||
System.out.println("Compression Delay: " + String.valueOf(delayCompress) + " millisecs"); | ||
assert(delayCompress < 10); // less than 1ms for 100KB (set 10ms upperbound) | ||
|
||
start = System.currentTimeMillis(); | ||
for (int i = 0; i < count; i++) { | ||
String compressed = EventHandlerUtils.compress(body); | ||
EventHandlerUtils.decompress(compressed); | ||
} | ||
end = System.currentTimeMillis(); | ||
float delayUncompress = ((float)(end - start))/count - delayCompress; | ||
System.out.println("Uncompression Delay: " + String.valueOf(delayUncompress) + " millisecs"); | ||
assert(delayUncompress < 10); // less than 1ms for 100KB (set 10ms upperbound) | ||
} | ||
|
||
public static String makeRandomString(int maxSize) { | ||
StringBuilder builder = new StringBuilder(); | ||
|
||
// for high compression rate, shift repeated string window. | ||
int window = 100; | ||
int shift = 3; // adjust (1...10) this for compression rate. smaller for higher rates. | ||
|
||
int start = 0; | ||
int end = start + window; | ||
int i = 0; | ||
|
||
int size = 0; | ||
while (true) { | ||
String str = String.valueOf(i); | ||
size += str.length(); | ||
if (size > maxSize) { | ||
break; | ||
} | ||
builder.append(str); | ||
|
||
i++; | ||
if (i > end) { | ||
start = start + shift; | ||
end = start + window; | ||
i = start; | ||
} | ||
} | ||
|
||
return builder.toString(); | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we remove commented out code before moving to Master?