diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index fa1639912..908cacdfd 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -3,7 +3,8 @@ name: Internal deploy
 on:
   push:
     branches:
-      - development
+      - 'development'
+      - '*_baseline'
 
 jobs:
   build-app:
diff --git a/CHANGES.txt b/CHANGES.txt
index 276f35747..4a034b66a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,7 @@
+5.0.0 (Nov 1, 2024)
+- Added support for targeting rules based on large segments.
+- BREAKING: Dropped support for Split Proxy below version 5.9.0. The SDK now requires Split Proxy 5.9.0 or above.
+
 4.2.2 (Sep 25, 2024)
 - Fixed issue in updating archived feature flags when using encryption.
 
diff --git a/build.gradle b/build.gradle
index d7cf463c8..0ebef0b78 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@ apply plugin: 'kotlin-android'
 apply from: 'spec.gradle'
 
 ext {
-    splitVersion = '4.2.2'
+    splitVersion = '5.0.0'
 }
 
 android {
@@ -323,3 +323,10 @@ task printReleaseDependenciesToFile {
 }
 
 preBuild.dependsOn printReleaseDependenciesToFile
+
+tasks.withType(Test) {
+    systemProperties['junit.jupiter.execution.parallel.enabled'] = true
+    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
+    forkEvery = 100
+    maxHeapSize = "1024m"
+}
diff --git a/schemas/io.split.android.client.storage.db.SplitRoomDatabase/6.json b/schemas/io.split.android.client.storage.db.SplitRoomDatabase/6.json
new file mode 100644
index 000000000..dde7c5c56
--- /dev/null
+++ b/schemas/io.split.android.client.storage.db.SplitRoomDatabase/6.json
@@ -0,0 +1,376 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 6,
+    "identityHash": "9e30c6e6f20f45fcf324c68c2d98920f",
+    "entities": [
+      {
+        "tableName": "my_segments",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_key` TEXT NOT NULL, `segment_list` TEXT NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`user_key`))",
+        "fields": [
+          {
+            "fieldPath": "userKey",
+            "columnName": "user_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "segmentList",
+            "columnName": "segment_list",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "updatedAt",
+            "columnName": "updated_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "user_key"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "splits",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `body` TEXT NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "body",
+            "columnName": "body",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "updatedAt",
+            "columnName": "updated_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "name"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "events",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `body` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `status` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "body",
+            "columnName": "body",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "created_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "impressions",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `test_name` TEXT NOT NULL, `body` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `status` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "testName",
+            "columnName": "test_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "body",
+            "columnName": "body",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "created_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "general_info",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `stringValue` TEXT, `longValue` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`name`))",
+        "fields": [
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "stringValue",
+            "columnName": "stringValue",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "longValue",
+            "columnName": "longValue",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "updatedAt",
+            "columnName": "updated_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "name"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "impressions_count",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `body` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `status` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "body",
+            "columnName": "body",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "created_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "attributes",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_key` TEXT NOT NULL, `attributes` TEXT, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`user_key`))",
+        "fields": [
+          {
+            "fieldPath": "userKey",
+            "columnName": "user_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "attributes",
+            "columnName": "attributes",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "updatedAt",
+            "columnName": "updated_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "user_key"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "unique_keys",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `user_key` TEXT NOT NULL, `feature_list` TEXT, `created_at` INTEGER NOT NULL, `status` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userKey",
+            "columnName": "user_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "featureList",
+            "columnName": "feature_list",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "created_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "impressions_observer_cache",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`hash` INTEGER NOT NULL, `time` INTEGER NOT NULL, `created_at` INTEGER NOT NULL, PRIMARY KEY(`hash`))",
+        "fields": [
+          {
+            "fieldPath": "hash",
+            "columnName": "hash",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "time",
+            "columnName": "time",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "createdAt",
+            "columnName": "created_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "hash"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "my_large_segments",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_key` TEXT NOT NULL, `segment_list` TEXT NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`user_key`))",
+        "fields": [
+          {
+            "fieldPath": "userKey",
+            "columnName": "user_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "segmentList",
+            "columnName": "segment_list",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "updatedAt",
+            "columnName": "updated_at",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "user_key"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9e30c6e6f20f45fcf324c68c2d98920f')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/spec.gradle b/spec.gradle
index 00232a7ca..b2e667495 100644
--- a/spec.gradle
+++ b/spec.gradle
@@ -1,5 +1,5 @@
 ext {
 
     // flags spec contains a String with the feature flags specification
-    flagsSpec = '1.1'
+    flagsSpec = '1.2'
 }
diff --git a/src/androidTest/assets/attributes_test_split_change.json b/src/androidTest/assets/attributes_test_split_change.json
index 8345a08ae..980b0a0fb 100644
--- a/src/androidTest/assets/attributes_test_split_change.json
+++ b/src/androidTest/assets/attributes_test_split_change.json
@@ -177,6 +177,6 @@
          "label":"rule 3"
       }
    ],
-   "since":1602796401438,
+   "since":1602796638344,
    "till":1602796638344
 }
\ No newline at end of file
diff --git a/src/androidTest/assets/bucket_split_test.json b/src/androidTest/assets/bucket_split_test.json
index b420fdab2..1a9d3e212 100644
--- a/src/androidTest/assets/bucket_split_test.json
+++ b/src/androidTest/assets/bucket_split_test.json
@@ -434,6 +434,6 @@
       "killed": false
     }
   ],
-  "since":-1,
+  "since":1506703262916,
   "till":1506703262916
 }
diff --git a/src/androidTest/assets/push_msg-largesegment_update.txt b/src/androidTest/assets/push_msg-largesegment_update.txt
new file mode 100644
index 000000000..168a77782
--- /dev/null
+++ b/src/androidTest/assets/push_msg-largesegment_update.txt
@@ -0,0 +1,3 @@
+id:cf74eb42-f687-48e4-ad18-af2125110aac
+event:message
+data:{"id":"x2dE2TEiJL:0:0","clientId":"NDEzMTY5Mzg0MA==:OTc5Nzc4NDYz","timestamp":1584647533288,"encoding":"json","channel":"MzM5´Njc0ODcyNg==_MTExMzgwNjgx_MTcwNTI2MTM0Mg==_mySegments","data":"[NOTIFICATION_DATA]"}
diff --git a/src/androidTest/assets/simple_split.json b/src/androidTest/assets/simple_split.json
index abdbe4a1b..c3e17e20b 100644
--- a/src/androidTest/assets/simple_split.json
+++ b/src/androidTest/assets/simple_split.json
@@ -102,6 +102,6 @@
       ]
     }
   ],
-  "since": 1602796401438,
+  "since": 1602796638344,
   "till": 1602796638344
 }
diff --git a/src/androidTest/assets/split_changes_1.json b/src/androidTest/assets/split_changes_1.json
index 5c886ea97..7eee38b79 100644
--- a/src/androidTest/assets/split_changes_1.json
+++ b/src/androidTest/assets/split_changes_1.json
@@ -2528,6 +2528,6 @@
       "changeNumber":1494593336752
     }
   ],
-  "since":-1,
+  "since":1506703262916,
   "till":1506703262916
 }
\ No newline at end of file
diff --git a/src/androidTest/assets/split_changes_flag_set-2.json b/src/androidTest/assets/split_changes_flag_set-2.json
index a96e3e209..0e7576af6 100644
--- a/src/androidTest/assets/split_changes_flag_set-2.json
+++ b/src/androidTest/assets/split_changes_flag_set-2.json
@@ -1 +1 @@
-{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_1","set_2"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]},{"trafficTypeName":"client","name":"workm_set_3","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":-1,"till":1602796638344}
+{"splits":[{"trafficTypeName":"client","name":"workm","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_1","set_2"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]},{"trafficTypeName":"client","name":"workm_set_3","trafficAllocation":100,"trafficAllocationSeed":147392224,"seed":524417105,"status":"ACTIVE","killed":false,"defaultTreatment":"on","changeNumber":1602796638344,"algo":2,"configurations":{},"sets":["set_3"],"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"new_segment"},"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"free","size":100},{"treatment":"conta","size":0}],"label":"in segment new_segment"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"client","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0},{"treatment":"free","size":0},{"treatment":"conta","size":0}],"label":"default rule"}]}],"since":1602796638344,"till":1602796638344}
diff --git a/src/androidTest/assets/split_changes_large_segments-0.json b/src/androidTest/assets/split_changes_large_segments-0.json
new file mode 100644
index 000000000..975f211d0
--- /dev/null
+++ b/src/androidTest/assets/split_changes_large_segments-0.json
@@ -0,0 +1,49 @@
+{
+  "splits": [
+    {
+      "trafficTypeName": "user",
+      "name": "ls_split",
+      "trafficAllocation": 100,
+      "trafficAllocationSeed": -285565213,
+      "seed": -1992295819,
+      "status": "ACTIVE",
+      "killed": false,
+      "defaultTreatment": "off",
+      "changeNumber": 1506703262916,
+      "algo": 2,
+      "conditions": [
+        {
+          "conditionType": "WHITELIST",
+          "matcherGroup": {
+            "combiner": "AND",
+            "matchers": [
+              {
+                "keySelector": null,
+                "matcherType": "IN_LARGE_SEGMENT",
+                "negate": false,
+                "userDefinedLargeSegmentMatcherData": {
+                  "largeSegmentName": "large-segment1"
+                },
+                "whitelistMatcherData": null,
+                "unaryNumericMatcherData": null,
+                "betweenMatcherData": null,
+                "booleanMatcherData": null,
+                "dependencyMatcherData": null,
+                "stringMatcherData": null
+              }
+            ]
+          },
+          "partitions": [
+            {
+              "treatment": "on",
+              "size": 100
+            }
+          ],
+          "label": "whitelisted segment"
+        }
+      ]
+    }
+  ],
+  "since": 1506703262916,
+  "till": 1506703262916
+}
diff --git a/src/androidTest/assets/splitchanges_int_test.json b/src/androidTest/assets/splitchanges_int_test.json
index c89d4305f..cd02900d2 100644
--- a/src/androidTest/assets/splitchanges_int_test.json
+++ b/src/androidTest/assets/splitchanges_int_test.json
@@ -52,6 +52,6 @@
       ]
     }
   ],
-  "since":-1,
+  "since":1567456937865,
   "till":1567456937865
 }
\ No newline at end of file
diff --git a/src/androidTest/java/fake/SynchronizerSpyImpl.java b/src/androidTest/java/fake/SynchronizerSpyImpl.java
index 88a22b8c3..6971258eb 100644
--- a/src/androidTest/java/fake/SynchronizerSpyImpl.java
+++ b/src/androidTest/java/fake/SynchronizerSpyImpl.java
@@ -55,12 +55,6 @@ public void synchronizeMySegments() {
         mSynchronizer.synchronizeMySegments();
     }
 
-    @Override
-    public void forceMySegmentsSync() {
-        mSynchronizer.forceMySegmentsSync();
-        mForceMySegmentSyncCalledCount.addAndGet(1);
-    }
-
     @Override
     public void startPeriodicFetching() {
         mSynchronizer.startPeriodicFetching();
diff --git a/src/androidTest/java/helper/IntegrationHelper.java b/src/androidTest/java/helper/IntegrationHelper.java
index 8837da304..09a17e8b0 100644
--- a/src/androidTest/java/helper/IntegrationHelper.java
+++ b/src/androidTest/java/helper/IntegrationHelper.java
@@ -3,6 +3,7 @@
 import android.content.Context;
 import android.util.Base64;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.util.Pair;
 
@@ -20,9 +21,12 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
 
 import fake.HttpClientMock;
 import fake.HttpResponseMock;
@@ -153,12 +157,27 @@ public static SplitFactory buildFactory(String apiToken, Key key, SplitClientCon
         return factory;
     }
 
-    public static String dummyMySegments() {
-        return "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}";
+    @Deprecated
+    public static String emptyMySegments() {
+        return emptyAllSegments();
     }
 
-    public static String emptyMySegments() {
-        return "{\"mySegments\":[]}";
+    public static String emptyAllSegments() {
+        return "{\"ms\":{\"k\":[],\"cn\":null},\"ls\":{\"k\":[],\"cn\":1702507130121}}";
+    }
+
+    public static String dummyAllSegments() {
+        return "{\"ms\":{\"k\":[{\"n\":\"segment1\"},{\"n\":\"segment2\"}],\"cn\":null},\"ls\":{\"k\":[{\"n\":\"large-segment1\"},{\"n\":\"large-segment2\"},{\"n\":\"large-segment3\"}],\"cn\":1702507130121}}";
+    }
+
+    public static String randomizedAllSegments() {
+        int randIntOne = (int) (Math.random() * 100);
+        int randIntTwo = (int) (Math.random() * 100);
+        return "{\"ms\":{\"k\":[{\"n\":\"segment1\"},{\"n\":\"segment2\"}],\"cn\":null},\"ls\":{\"k\":[{\"n\":\"large-segment" + randIntOne + "\"},{\"n\":\"large-segment" + randIntTwo + "\"}],\"cn\":1702507130121}}";
+    }
+
+    public static String dummySingleSegment(String segment) {
+        return "{\"ms\":{\"k\":[{\"n\":\"" + segment + "\"}],\"cn\":null},\"ls\":{\"k\":[],\"cn\":1702507130121}}";
     }
 
     public static String dummyApiKey() {
@@ -182,7 +201,7 @@ public static SplitClientConfig basicConfig() {
         return SplitClientConfig.builder()
                 .ready(30000)
                 .streamingEnabled(true)
-                .logLevel(SplitLogLevel.DEBUG)
+                .logLevel(SplitLogLevel.VERBOSE)
                 .trafficType("account")
                 .build();
     }
@@ -244,6 +263,15 @@ public static String streamingEnabledToken(int delay) {
 
     }
 
+    @Deprecated
+    public static String streamingEnabledTokenLargeSegments() {
+        return "{" +
+                "    \"pushEnabled\": true," +
+                "    \"connDelay\": " + 0 + "," +
+                "    \"token\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.ewogICJ4LWFibHktY2FwYWJpbGl0eSI6ICJ7XCJNek01TmpjME9EY3lOZz09X01URXhNemd3TmpneF9NVGN3TlRJMk1UTTBNZz09X215U2VnbWVudHNcIjpbXCJzdWJzY3JpYmVcIl0sXCJNek01TmpjME9EY3lOZz09X01URXhNemd3TmpneF9NVGN3TlRJMk1UTTBNZz09X215bGFyZ2VzZWdtZW50c1wiOltcInN1YnNjcmliZVwiXSxcIk16TTVOamMwT0RjeU5nPT1fTVRFeE16Z3dOamd4X3NwbGl0c1wiOltcInN1YnNjcmliZVwiXSxcImNvbnRyb2xfcHJpXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl0sXCJjb250cm9sX3NlY1wiOltcInN1YnNjcmliZVwiLFwiY2hhbm5lbC1tZXRhZGF0YTpwdWJsaXNoZXJzXCJdfSIsCiAgIngtYWJseS1jbGllbnRJZCI6ICJjbGllbnRJZCIsCiAgImV4cCI6IDIyMDg5ODg4MDAsCiAgImlhdCI6IDE1ODc0MDQzODgKfQ==.LcKAXnkr-CiYVxZ7l38w9i98Y-BMAv9JlGP2i92nVQY\"" +
+                "}";
+    }
+
     public static String streamingDisabledToken() {
         return "{\"pushEnabled\": false }";
     }
@@ -276,13 +304,13 @@ public static String splitChangeV2CompressionType0() {
     public static String splitChangeV2(String changeNumber, String previousChangeNumber, String compressionType, String compressedPayload) {
         return "id: vQQ61wzBRO:0:0\n" +
                 "event: message\n" +
-                "data: {\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":"+System.currentTimeMillis()+",\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":"+changeNumber+",\\\"pcn\\\":"+previousChangeNumber+",\\\"c\\\":"+compressionType+",\\\"d\\\":\\\""+compressedPayload+"\\\"}\"}\n";
+                "data: {\"id\":\"m2T85LA4fQ:0:0\",\"clientId\":\"pri:NzIyNjY1MzI4\",\"timestamp\":" + System.currentTimeMillis() + ",\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":" + changeNumber + ",\\\"pcn\\\":" + previousChangeNumber + ",\\\"c\\\":" + compressionType + ",\\\"d\\\":\\\"" + compressedPayload + "\\\"}\"}\n";
     }
 
     public static String splitKill(String changeNumber, String splitName) {
         return "id:cf74eb42-f687-48e4-ad18-af2125110aac\n" +
                 "event:message\n" +
-                "data:{\"id\":\"-OT-rGuSwz:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:NDIxNjU0NTUyNw==\",\"timestamp\":"+System.currentTimeMillis()+",\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_KILL\\\",\\\"changeNumber\\\":" + changeNumber + ",\\\"defaultTreatment\\\":\\\"off\\\",\\\"splitName\\\":\\\"" + splitName + "\\\"}\"}\n";
+                "data:{\"id\":\"-OT-rGuSwz:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:NDIxNjU0NTUyNw==\",\"timestamp\":" + System.currentTimeMillis() + ",\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MTgyNTg1MTgwNg==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_KILL\\\",\\\"changeNumber\\\":" + changeNumber + ",\\\"defaultTreatment\\\":\\\"off\\\",\\\"splitName\\\":\\\"" + splitName + "\\\"}\"}\n";
     }
 
     public static String loadSplitChanges(Context context, String fileName) {
@@ -303,14 +331,18 @@ public static HttpResponseMockDispatcher buildDispatcher(Map<String, ResponseClo
         return buildDispatcher(responses, null);
     }
 
+    public static HttpResponseMockDispatcher buildDispatcher(Map<String, ResponseClosure> responses, @Nullable BlockingQueue<String> streamingQueue) {
+        return buildDispatcher(responses, streamingQueue, null);
+    }
+
     /**
      * Builds a dispatcher with the given responses.
      *
-     * @param responses          The responses to be returned by the dispatcher. The keys are url paths.
+     * @param responses      The responses to be returned by the dispatcher. The keys are url paths.
      * @param streamingQueue The streaming responses to be returned by the dispatcher.
      * @return The dispatcher to be used in {@link HttpClientMock}
      */
-    public static HttpResponseMockDispatcher buildDispatcher(Map<String, ResponseClosure> responses, @Nullable BlockingQueue<String> streamingQueue) {
+    public static HttpResponseMockDispatcher buildDispatcher(Map<String, ResponseClosure> responses, @Nullable BlockingQueue<String> streamingQueue, CountDownLatch sseLatch) {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
@@ -330,6 +362,9 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
             @Override
             public HttpStreamResponseMock getStreamResponse(URI uri) {
                 try {
+                    if (sseLatch != null) {
+                        sseLatch.countDown();
+                    }
                     return new HttpStreamResponseMock(200, streamingQueue);
                 } catch (IOException e) {
                     e.printStackTrace();
@@ -344,6 +379,17 @@ public static String sha256(byte[] encoded) throws NoSuchAlgorithmException {
         return Base64.encodeToString(digest.digest(encoded), Base64.NO_WRAP);
     }
 
+    @NonNull
+    public static <T> Set<T> asSet(T... elements) {
+        if (elements.length == 0) {
+            return Collections.emptySet();
+        }
+        Set<T> result = new HashSet<>();
+        Collections.addAll(result, elements);
+
+        return result;
+    }
+
     /**
      * A simple interface to allow us to define the response for a given path
      */
@@ -386,4 +432,8 @@ static Map<String, String> parse(String query) throws UnsupportedEncodingExcepti
     public interface StreamingResponseClosure {
         HttpStreamResponseMock onResponse(URI uri);
     }
+
+    public static class ServicePath {
+        public static final String MEMBERSHIPS = "memberships";
+    }
 }
diff --git a/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java b/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java
index 2fb949d71..6709488ae 100644
--- a/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java
+++ b/src/androidTest/java/io/split/android/client/service/impressions/observer/DedupeIntegrationTest.java
@@ -1,8 +1,6 @@
 package io.split.android.client.service.impressions.observer;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static helper.IntegrationHelper.ResponseClosure.getSinceFromUri;
@@ -297,8 +295,8 @@ private HttpResponseMockDispatcher getDispatcher() {
             }
         });
 
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
-        responses.put("mySegments/key2", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/key2", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         return IntegrationHelper.buildDispatcher(responses);
     }
diff --git a/src/androidTest/java/tests/database/MyLargeSegmentDaoTest.java b/src/androidTest/java/tests/database/MyLargeSegmentDaoTest.java
new file mode 100644
index 000000000..e4e41cd16
--- /dev/null
+++ b/src/androidTest/java/tests/database/MyLargeSegmentDaoTest.java
@@ -0,0 +1,79 @@
+package tests.database;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
+
+public class MyLargeSegmentDaoTest extends GenericDaoTest {
+
+    @Test
+    public void insertRetrieve() {
+        long timestamp = System.currentTimeMillis();
+        mRoomDb.myLargeSegmentDao().update(generateData("key1", 1, 100, timestamp));
+        mRoomDb.myLargeSegmentDao().update(generateData("key2",101, 200, timestamp));
+        mRoomDb.myLargeSegmentDao().update(generateData("key3",201, 300, timestamp));
+
+        MyLargeSegmentEntity msKey1 = mRoomDb.myLargeSegmentDao().getByUserKey("key1");
+        MyLargeSegmentEntity msKey2 = mRoomDb.myLargeSegmentDao().getByUserKey("key2");
+        MyLargeSegmentEntity msKey3 = mRoomDb.myLargeSegmentDao().getByUserKey("key3");
+
+        Assert.assertEquals("key1", msKey1.getUserKey());
+        Assert.assertEquals("key2", msKey2.getUserKey());
+        Assert.assertEquals("key3", msKey3.getUserKey());
+
+        Assert.assertEquals(timestamp + 1, msKey1.getUpdatedAt());
+        Assert.assertEquals(timestamp + 101, msKey2.getUpdatedAt());
+        Assert.assertEquals(timestamp + 201, msKey3.getUpdatedAt());
+    }
+
+    @Test
+    public void insertUpdateRetrieve() {
+        long timestamp = System.currentTimeMillis();
+        mRoomDb.myLargeSegmentDao().update(generateData("key1", 1, 10, timestamp));
+        mRoomDb.myLargeSegmentDao().update(generateData("key1", 500, 505, timestamp));
+
+        MyLargeSegmentEntity msKey1 = mRoomDb.myLargeSegmentDao().getByUserKey("key1");
+        Set<String> segments = new HashSet<String>(Arrays.asList(msKey1.getSegmentList().split(",")));
+
+        Assert.assertEquals("key1", msKey1.getUserKey());
+        Assert.assertEquals(timestamp + 500, msKey1.getUpdatedAt());
+        Assert.assertFalse(segments.contains("segment1"));
+        Assert.assertTrue(segments.contains("segment500"));
+        Assert.assertTrue(segments.contains("segment505"));
+    }
+
+    @Test
+    public void segmentsIntegrity() {
+        long timestamp = System.currentTimeMillis();
+        mRoomDb.myLargeSegmentDao().update(generateData("key1", 1, 10, timestamp));
+
+        MyLargeSegmentEntity mySegmentEntity = mRoomDb.myLargeSegmentDao().getByUserKey("key1");
+        Set<String> segments = new HashSet<String>(Arrays.asList(mySegmentEntity.getSegmentList().split(",")));
+
+        Assert.assertEquals("key1", mySegmentEntity.getUserKey());
+        Assert.assertEquals(timestamp + 1, mySegmentEntity.getUpdatedAt());
+        Assert.assertEquals(10, segments.size());
+        Assert.assertTrue(segments.contains("segment1"));
+        Assert.assertTrue(segments.contains("segment5"));
+        Assert.assertTrue(segments.contains("segment10"));
+    }
+
+    private MyLargeSegmentEntity generateData(String key, int from, int to, long timestamp) {
+        MyLargeSegmentEntity segmentEntity = new MyLargeSegmentEntity();
+        List<String> mySegmentList = new ArrayList<>();
+        for(int i = from; i<=to; i++) {
+            mySegmentList.add("segment" + i);
+        }
+        segmentEntity.setUserKey(key);
+        segmentEntity.setSegmentList(String.join(",", mySegmentList));
+        segmentEntity.setUpdatedAt(timestamp + from);
+        return segmentEntity;
+    }
+}
diff --git a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java
index 8b06e403b..12ecce898 100644
--- a/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java
+++ b/src/androidTest/java/tests/integration/FlagsSpecInRequestTest.java
@@ -122,7 +122,7 @@ public void authContainsFlagsSpec() throws InterruptedException {
         TestingConfig testingConfig = new TestingConfig();
         initSplitFactory(new TestableSplitConfigBuilder(), mHttpClient, testingConfig);
 
-        assertEquals("s=1.1&users=CUSTOMER_ID", mAuthUrl.get().getQuery());
+        assertEquals("s=1.2&users=CUSTOMER_ID", mAuthUrl.get().getQuery());
     }
 
     @Test
@@ -156,7 +156,7 @@ private HttpResponseMockDispatcher getDispatcher() {
             }
         });
 
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         responses.put("v2/auth", (uri, httpMethod, body) -> {
             mAuthUrl.set(uri);
diff --git a/src/androidTest/java/tests/integration/InitialChangeNumberTest.java b/src/androidTest/java/tests/integration/InitialChangeNumberTest.java
index b724aab38..8ae2f6c62 100644
--- a/src/androidTest/java/tests/integration/InitialChangeNumberTest.java
+++ b/src/androidTest/java/tests/integration/InitialChangeNumberTest.java
@@ -62,8 +62,8 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (request.getPath().contains("/splitChanges")) {
 
                     long changeNumber = -1;
@@ -74,7 +74,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
                         mIsFirstChangeNumber = false;
                     }
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber + 1000) + "}");
+                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber) + "}");
                 } else if (request.getPath().contains("/events/bulk")) {
                     String trackRequestBody = request.getBody().readUtf8();
 
@@ -97,7 +97,7 @@ public void firstRequestChangeNumber() throws Exception {
         splitRoomDatabase.clearAllTables();
         splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.DATBASE_MIGRATION_STATUS, GeneralInfoEntity.DATBASE_MIGRATION_STATUS_DONE));
         splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.CHANGE_NUMBER_INFO, INITIAL_CHANGE_NUMBER));
-        splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000));
+        splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis()));
 
         SplitClient client;
 
@@ -114,7 +114,7 @@ public void firstRequestChangeNumber() throws Exception {
                 .segmentsRefreshRate(30)
                 .impressionsRefreshRate(99999)
                 .streamingEnabled(false)
-                .logLevel(SplitLogLevel.DEBUG)
+                .logLevel(SplitLogLevel.VERBOSE)
                 .build();
 
 
@@ -139,9 +139,4 @@ public void firstRequestChangeNumber() throws Exception {
         Assert.assertTrue(readyFromCacheTask.isOnPostExecutionCalled);
         Assert.assertEquals(INITIAL_CHANGE_NUMBER, mFirstChangeNumberReceived); // Checks that change number is the bigger number from cached splitss
     }
-
-    private void log(String m) {
-        System.out.println("FACTORY_TEST: " + m);
-    }
-
 }
diff --git a/src/androidTest/java/tests/integration/IntegrationTest.java b/src/androidTest/java/tests/integration/IntegrationTest.java
index 3d4b00759..66f023e13 100644
--- a/src/androidTest/java/tests/integration/IntegrationTest.java
+++ b/src/androidTest/java/tests/integration/IntegrationTest.java
@@ -83,8 +83,8 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (request.getPath().contains("/splitChanges")) {
                     int r = mCurSplitReqId;
                     mCurSplitReqId++;
diff --git a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java
index 2c2e6ce26..34b5dcc25 100644
--- a/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java
+++ b/src/androidTest/java/tests/integration/MySegmentUpdatedTest.java
@@ -26,8 +26,8 @@
 import io.split.android.client.SplitClient;
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
-import io.split.android.client.SplitFactoryBuilder;
 import io.split.android.client.api.Key;
+import io.split.android.client.dtos.Condition;
 import io.split.android.client.dtos.ConditionType;
 import io.split.android.client.dtos.KeyImpression;
 import io.split.android.client.dtos.Matcher;
@@ -39,7 +39,6 @@
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.dtos.TestImpressions;
 import io.split.android.client.dtos.UserDefinedSegmentMatcherData;
-import io.split.android.client.dtos.Condition;
 import io.split.android.client.events.SplitEvent;
 import io.split.android.client.service.impressions.ImpressionsMode;
 import io.split.android.client.storage.db.SplitRoomDatabase;
@@ -89,19 +88,19 @@ private void setupServer() throws IOException {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
 
                     String data;
                     int index = mCurReqId;
                     switch (index) {
                         case 1:
-                            data = "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}]}";
+                            data = IntegrationHelper.dummySingleSegment("segment1");
                             break;
                         case 2:
-                            data = "{\"mySegments\":[{ \"id\":\"id2\", \"name\":\"segment2\"}]}";
+                            data = IntegrationHelper.dummySingleSegment("segment2");
                             break;
                         default:
-                            data = "{\"mySegments\":[]}";
+                            data = IntegrationHelper.emptyAllSegments();
                     }
 
                     if(index > 0 && index <= mLatchs.size()) {
diff --git a/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java b/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java
index 852cfb55e..25221044d 100644
--- a/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java
+++ b/src/androidTest/java/tests/integration/MySegmentsServerErrorTest.java
@@ -24,7 +24,6 @@
 import io.split.android.client.SplitClient;
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
-import io.split.android.client.SplitFactoryBuilder;
 import io.split.android.client.api.Key;
 import io.split.android.client.dtos.Condition;
 import io.split.android.client.dtos.ConditionType;
@@ -86,14 +85,14 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
 
                     int code = 200;
                     String data = null;
                     int index = mCurReqId;
                     switch (index) {
                         case 0:
-                            data = "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}]}";
+                            data = IntegrationHelper.dummySingleSegment("segment1");
                             break;
                         case 1:
                         case 2:
@@ -101,7 +100,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
                             data = "";
                             break;
                         case 3:
-                            data = "{\"mySegments\":[{ \"id\":\"id2\", \"name\":\"segment2\"}]}";
+                            data = IntegrationHelper.dummySingleSegment("segment2");
                     }
 
                     if(index > 0 && index <= mLatchs.size()) {
diff --git a/src/androidTest/java/tests/integration/ProxyFactoryTest.java b/src/androidTest/java/tests/integration/ProxyFactoryTest.java
index 2864d8460..28f80e19d 100644
--- a/src/androidTest/java/tests/integration/ProxyFactoryTest.java
+++ b/src/androidTest/java/tests/integration/ProxyFactoryTest.java
@@ -92,9 +92,9 @@ public void settingProxyReplacesAllUrls() throws IOException, InterruptedExcepti
             @Override
             public MockResponse dispatch(RecordedRequest request) {
                 String requestLine = request.getRequestLine();
-                if (requestLine.contains("/mySegments")) {
+                if (requestLine.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mySegmentsHit.set(true);
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (requestLine.contains("splitChanges")) {
                     splitChangesHit.set(true);
                     return new MockResponse().setResponseCode(200).setBody(mSplitChanges);
@@ -198,9 +198,9 @@ public MockResponse dispatch(RecordedRequest request) {
                 if (request.getHeader("Proxy-Authorization") != null) {
                     proxyAuthorizationHeaderLatch.countDown();
                 }
-                if (requestLine.contains("/mySegments")) {
+                if (requestLine.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mySegmentsHit.set(true);
-                    return new MockResponse().setResponseCode((request.getHeader("Proxy-Authorization") != null) ? 200 : 407).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                    return new MockResponse().setResponseCode((request.getHeader("Proxy-Authorization") != null) ? 200 : 407).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (requestLine.contains("splitChanges")) {
                     splitChangesHit.set(true);
                     return new MockResponse().setResponseCode((request.getHeader("Proxy-Authorization") != null) ? 200 : 407).setBody(mSplitChanges);
diff --git a/src/androidTest/java/tests/integration/SingleSyncTest.java b/src/androidTest/java/tests/integration/SingleSyncTest.java
index 205e8769a..4dd623b3b 100644
--- a/src/androidTest/java/tests/integration/SingleSyncTest.java
+++ b/src/androidTest/java/tests/integration/SingleSyncTest.java
@@ -279,7 +279,7 @@ public HttpStreamResponseMock getStreamResponse(URI uri) {
 
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
                 System.out.println("Path is " + uri.getPath());
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitCount++;
                     return new HttpResponseMock(200, IntegrationHelper.emptyMySegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
diff --git a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java
index 17837d003..2edadda5b 100644
--- a/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java
+++ b/src/androidTest/java/tests/integration/SplitChangesCdnBypassTest.java
@@ -57,7 +57,7 @@ public void setup() throws IOException {
         SplitRoomDatabase splitRoomDatabase = DatabaseHelper.getTestDatabase(mContext);
         splitRoomDatabase.clearAllTables();
         splitRoomDatabase.generalInfoDao().update(
-                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000 - 30));
+                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() - 30));
         SplitClientConfig config = new TestableSplitConfigBuilder().ready(30000)
                 .streamingEnabled(true)
                 .enableDebug()
@@ -116,9 +116,8 @@ public HttpStreamResponseMock getStreamResponse(URI uri) {
             @Override
 
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return new HttpResponseMock(200, "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, " +
-                            "{ \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new HttpResponseMock(200, IntegrationHelper.dummyAllSegments());
 
                 } else if (uri.getPath().contains("/splitChanges")) {
                     System.out.println("URL HIT: " + uri.getPath());
diff --git a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java
index 369b9be9a..562939151 100644
--- a/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java
+++ b/src/androidTest/java/tests/integration/SplitChangesServerErrorTest.java
@@ -79,9 +79,8 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
 
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return new HttpResponseMock(200, "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, " +
-                            "{ \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new HttpResponseMock(200, IntegrationHelper.dummyAllSegments());
 
                 } else if (uri.getPath().contains("/splitChanges")) {
                     int currReq = mCurSplitReqId;
diff --git a/src/androidTest/java/tests/integration/SplitChangesTest.java b/src/androidTest/java/tests/integration/SplitChangesTest.java
index 0d4c91cb5..754854d4c 100644
--- a/src/androidTest/java/tests/integration/SplitChangesTest.java
+++ b/src/androidTest/java/tests/integration/SplitChangesTest.java
@@ -81,11 +81,10 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     return new MockResponse()
                             .setResponseCode(200)
-                            .setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, " +
-                                    "{ \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                            .setBody(IntegrationHelper.dummyAllSegments());
 
                 } else if (request.getPath().contains("/splitChanges")) {
                     int currReq = mCurSplitReqId;
@@ -100,7 +99,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
                         mLatchs.get(currReq - 1).countDown();
                     }
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\": 9567456937869, \"till\": 9567456937869 }");
+                            .setBody("{\"splits\":[], \"since\": 1567456938865, \"till\": 1567456938865 }");
 
 
                 } else if (request.getPath().contains("/testImpressions/bulk")) {
@@ -170,12 +169,12 @@ public void test() throws Exception {
             }
             treatments.add(client.getTreatment("test_feature"));
         }
+        Thread.sleep(1000);
+        client.destroy();
         boolean impAwait = mImpLatch.await(10, TimeUnit.SECONDS);
         if (!impAwait) {
             Assert.fail("Impressions not received");
         }
-        client.destroy();
-        Thread.sleep(1000);
 
         ArrayList<Impression> impLis = new ArrayList<>();
         impLis.add(impListener.getImpression(impListener.buildKey(
diff --git a/src/androidTest/java/tests/integration/SplitFetchSpecificSplitTest.java b/src/androidTest/java/tests/integration/SplitFetchSpecificSplitTest.java
index 076eda287..22e102082 100644
--- a/src/androidTest/java/tests/integration/SplitFetchSpecificSplitTest.java
+++ b/src/androidTest/java/tests/integration/SplitFetchSpecificSplitTest.java
@@ -75,8 +75,8 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (request.getPath().contains("/splitChanges")) {
                     Logger.d("Req: " + mCurSplitReqId + " -> qs =" + mReceivedQueryString);
                     if (mCurSplitReqId == 1) {
@@ -109,7 +109,7 @@ public void testAll() throws Exception {
         splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.DATBASE_MIGRATION_STATUS, GeneralInfoEntity.DATBASE_MIGRATION_STATUS_DONE));
         splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.CHANGE_NUMBER_INFO, 2));
         splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_FILTER_QUERY_STRING, expectedQs));
-        splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000));
+        splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis()));
         SplitClient client;
 
         final String url = mWebServer.url("/").url().toString();
diff --git a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java
index 6301a628d..1183f7d7a 100644
--- a/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java
+++ b/src/androidTest/java/tests/integration/SplitsTwoDifferentApiKeyTest.java
@@ -170,12 +170,12 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher(int factoryNumb
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
 
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     int hit = 0;
-                    long changeNumber = Long.parseLong(getSinceFromUri(uri));//new Integer(uri.getQuery().split("&")[1].split("=")[1]);
+                    long changeNumber = Long.parseLong(getSinceFromUri(uri));
                     if (factoryNumber == 1) {
                         System.out.println("hit 1 cn: " + changeNumber);
                         f1ChangeNumbers.add(changeNumber);
@@ -191,9 +191,13 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
                         return createResponse(200, getSplitChanges(factoryNumber, hit));
                     }
                     String data = IntegrationHelper.emptySplitChanges(respChangeNumber, respChangeNumber);
-                    CountDownLatch latch = mSplitsUpdateLatch.get(factoryNumber - 1);
-                    if (latch != null) {
-                        latch.countDown();
+                    try {
+                        CountDownLatch latch = mSplitsUpdateLatch.get(factoryNumber - 1);
+                        if (latch != null) {
+                            latch.countDown();
+                        }
+                    } catch (Exception ex) {
+                        ex.printStackTrace();
                     }
                     return createResponse(200, data);
                 } else if (uri.getPath().contains("/auth")) {
diff --git a/src/androidTest/java/tests/integration/TrackTest.java b/src/androidTest/java/tests/integration/TrackTest.java
index fcb436666..2db076a2b 100644
--- a/src/androidTest/java/tests/integration/TrackTest.java
+++ b/src/androidTest/java/tests/integration/TrackTest.java
@@ -13,7 +13,6 @@
 import org.junit.Test;
 
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -21,7 +20,6 @@
 import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 import helper.DatabaseHelper;
 import helper.ImpressionListenerHelper;
@@ -77,8 +75,8 @@ private void setupServer() {
 
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[]}");
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.emptyAllSegments());
 
                 } else if (request.getPath().contains("/splitChanges")) {
                     return new MockResponse().setResponseCode(200)
diff --git a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java
index 03bc9a759..b8ef6be27 100644
--- a/src/androidTest/java/tests/integration/encryption/EncryptionTest.java
+++ b/src/androidTest/java/tests/integration/encryption/EncryptionTest.java
@@ -33,10 +33,10 @@
 import io.split.android.client.events.SplitEvent;
 import io.split.android.client.events.SplitEventTask;
 import io.split.android.client.service.impressions.ImpressionsMode;
-import io.split.android.client.shared.UserConsent;
 import io.split.android.client.storage.db.EventEntity;
 import io.split.android.client.storage.db.ImpressionEntity;
 import io.split.android.client.storage.db.ImpressionsCountEntity;
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
@@ -70,6 +70,7 @@ public void onPostExecutionView(SplitClient client) {
 
         verifySplits(testDatabase);
         verifySegments(testDatabase);
+        verifyLargeSegments(testDatabase);
         verifyEvents(testDatabase);
     }
 
@@ -118,7 +119,7 @@ public void onPostExecutionView(SplitClient client) {
                     }
                 }
                 try {
-                    Thread.sleep(200);
+                    Thread.sleep(500);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
@@ -148,12 +149,16 @@ public void onPostExecutionView(SplitClient client) {
                 client.getTreatment("FACUNDO_TEST");
                 client.getTreatment("testing");
                 factory.destroy();
-                latch.countDown();
+                try {
+                    Thread.sleep(1000);
+                    latch.countDown();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
             }
         });
 
-        assertTrue(latch.await(2, TimeUnit.SECONDS));
-        Thread.sleep(250);
+        assertTrue(latch.await(5, TimeUnit.SECONDS));
 
         verifyUniqueKeys(testDatabase);
     }
@@ -213,8 +218,8 @@ private static void verifySplits(SplitRoomDatabase testDatabase) {
                 fail("Split name not encrypted, it was " + splitEntity.getName());
             }
 
-            boolean bodyCondition = splitEntity.getBody().trim().endsWith("=");
-            if (!bodyCondition) {
+            boolean bodyCondition = splitEntity.getBody().trim().endsWith("}");
+            if (bodyCondition) {
                 fail("Body not encrypted, it was " + splitEntity.getBody());
             }
         }
@@ -230,7 +235,7 @@ private static void verifySegments(SplitRoomDatabase testDatabase) {
                 fail("Segment user key not encrypted, it was " + segmentEntity.getUserKey());
             }
 
-            boolean bodyCondition = segmentEntity.getSegmentList().trim().endsWith("=");
+            boolean bodyCondition = !segmentEntity.getSegmentList().trim().endsWith("}");
             if (!bodyCondition) {
                 fail("Segment list not encrypted, it was " + segmentEntity.getSegmentList());
             }
@@ -239,6 +244,23 @@ private static void verifySegments(SplitRoomDatabase testDatabase) {
         assertEquals(1, all.size());
     }
 
+    private static void verifyLargeSegments(SplitRoomDatabase testDatabase) {
+        List<MyLargeSegmentEntity> all = testDatabase.myLargeSegmentDao().getAll();
+        for (MyLargeSegmentEntity segmentEntity : all) {
+            boolean nameCondition = segmentEntity.getUserKey().trim().contains("=");
+            if (!nameCondition) {
+                fail("Large segment user key not encrypted, it was " + segmentEntity.getUserKey());
+            }
+
+            boolean bodyCondition = segmentEntity.getSegmentList().trim().contains("large-segment");
+            if (bodyCondition) {
+                fail("Large segment list not encrypted, it was " + segmentEntity.getSegmentList());
+            }
+        }
+
+        assertEquals(1, all.size());
+    }
+
     private static void verifyEvents(SplitRoomDatabase testDatabase) {
         List<EventEntity> all = testDatabase.eventDao().getAll();
         for (EventEntity entity : all) {
@@ -308,6 +330,7 @@ private SplitFactory createFactory(
                 .impressionsRefreshRate(1000)
                 .impressionsCountersRefreshRate(1000)
                 .streamingEnabled(false)
+                .enableDebug()
                 .eventFlushInterval(1000)
                 .encryptionEnabled(encryptionEnabled)
                 .build();
@@ -327,7 +350,7 @@ private SplitFactory createFactory(
     private Map<String, IntegrationHelper.ResponseClosure> getResponses() {
         Map<String, IntegrationHelper.ResponseClosure> responses = new HashMap<>();
         responses.put("splitChanges", (uri, httpMethod, body) -> new HttpResponseMock(200, loadSplitChanges()));
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.dummyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.dummyAllSegments()));
         responses.put("events/bulk", (uri, httpMethod, body) -> new HttpResponseMock(404, ""));
         responses.put("testImpressions/bulk", (uri, httpMethod, body) -> new HttpResponseMock(404, ""));
         return responses;
diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java
new file mode 100644
index 000000000..f8d876a69
--- /dev/null
+++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentTestHelper.java
@@ -0,0 +1,160 @@
+package tests.integration.largesegments;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import helper.FileHelper;
+import helper.IntegrationHelper;
+import helper.TestableSplitConfigBuilder;
+import io.split.android.client.ServiceEndpoints;
+import io.split.android.client.SplitClient;
+import io.split.android.client.SplitFactory;
+import io.split.android.client.dtos.SplitChange;
+import io.split.android.client.events.SplitEvent;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import tests.integration.shared.TestingHelper;
+
+public class LargeSegmentTestHelper {
+
+    protected final FileHelper mFileHelper = new FileHelper();
+    protected final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+    protected MockWebServer mWebServer;
+    protected Map<String, AtomicInteger> mEndpointHits;
+    protected Map<String, CountDownLatch> mLatches;
+    protected final AtomicLong mMySegmentsDelay = new AtomicLong(0L);
+    protected final AtomicBoolean mRandomizeMyLargeSegments = new AtomicBoolean(false);
+    protected final AtomicBoolean mEmptyMyLargeSegments = new AtomicBoolean(false);
+    protected final AtomicBoolean mBrokenApi = new AtomicBoolean(false);
+
+    @Before
+    public void setUp() throws IOException {
+        mEndpointHits = new ConcurrentHashMap<>();
+        mMySegmentsDelay.set(0L);
+        mRandomizeMyLargeSegments.set(false);
+        mEmptyMyLargeSegments.set(false);
+        mBrokenApi.set(false);
+        initializeLatches();
+
+        mWebServer = new MockWebServer();
+        mWebServer.setDispatcher(new Dispatcher() {
+            @Override
+            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
+                System.out.println("Receiving request to " + request.getRequestUrl().toString());
+
+                if (mBrokenApi.get()) {
+                    return new MockResponse().setResponseCode(500);
+                }
+
+                if (request.getRequestUrl().encodedPathSegments().contains("splitChanges")) {
+                    updateEndpointHit("splitChanges");
+                    return new MockResponse().setResponseCode(200).setBody(splitChangesLargeSegments(1602796638344L, 1602796638344L));
+                } else if (request.getRequestUrl().encodedPathSegments().contains(IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    Thread.sleep(mMySegmentsDelay.get());
+                    updateEndpointHit(IntegrationHelper.ServicePath.MEMBERSHIPS);
+
+                    String body = (mEmptyMyLargeSegments.get()) ? IntegrationHelper.emptyAllSegments() : IntegrationHelper.dummyAllSegments();
+                    if (mRandomizeMyLargeSegments.get()) {
+                        body = IntegrationHelper.randomizedAllSegments();
+                    }
+
+                    return new MockResponse().setResponseCode(200).setBody(body);
+                } else {
+                    return new MockResponse().setResponseCode(404);
+                }
+            }
+        });
+        mWebServer.start();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        mWebServer.close();
+    }
+
+    private void initializeLatches() {
+        mLatches = new ConcurrentHashMap<>();
+        mLatches.put("splitChanges", new CountDownLatch(1));
+        mLatches.put(IntegrationHelper.ServicePath.MEMBERSHIPS, new CountDownLatch(1));
+    }
+
+    private void updateEndpointHit(String splitChanges) {
+        if (mEndpointHits.containsKey(splitChanges)) {
+            mEndpointHits.get(splitChanges).getAndIncrement();
+        } else {
+            mEndpointHits.put(splitChanges, new AtomicInteger(1));
+        }
+
+        if (mLatches.containsKey(splitChanges)) {
+            mLatches.get(splitChanges).countDown();
+        }
+    }
+
+    protected SplitFactory getFactory() {
+        return getFactory(null, 2500, null);
+    }
+
+    protected SplitFactory getFactory(Integer segmentsRefreshRate,
+                                      Integer ready, SplitRoomDatabase database) {
+        TestableSplitConfigBuilder configBuilder = new TestableSplitConfigBuilder()
+                .enableDebug()
+                .serviceEndpoints(ServiceEndpoints.builder()
+                        .apiEndpoint("http://" + mWebServer.getHostName() + ":" + mWebServer.getPort())
+                        .build());
+
+        if (segmentsRefreshRate != null) {
+            configBuilder.streamingEnabled(false);
+            configBuilder.segmentsRefreshRate(segmentsRefreshRate);
+        }
+        if (ready != null) {
+            configBuilder.ready(ready);
+        }
+        return IntegrationHelper.buildFactory(
+                IntegrationHelper.dummyApiKey(),
+                IntegrationHelper.dummyUserKey(),
+                configBuilder.build(),
+                mContext,
+                null, database, null, null, null);
+    }
+
+    protected SplitClient getReadyClient(String matchingKey, SplitFactory factory) {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        SplitClient client = factory.client(matchingKey);
+        client.on(SplitEvent.SDK_READY, TestingHelper.testTask(countDownLatch));
+        try {
+            countDownLatch.await(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+
+        return client;
+    }
+
+    private String splitChangesLargeSegments(long since, long till) {
+        String change = mFileHelper.loadFileContent(mContext, "split_changes_large_segments-0.json");
+        SplitChange parsedChange = Json.fromJson(change, SplitChange.class);
+        parsedChange.since = since;
+        parsedChange.till = till;
+
+        return Json.toJson(parsedChange);
+    }
+}
diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java
new file mode 100644
index 000000000..2b95f70ca
--- /dev/null
+++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsStreamingTest.java
@@ -0,0 +1,245 @@
+package tests.integration.largesegments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import fake.HttpClientMock;
+import fake.HttpResponseMock;
+import fake.HttpResponseMockDispatcher;
+import helper.DatabaseHelper;
+import helper.FileHelper;
+import helper.IntegrationHelper;
+import helper.TestableSplitConfigBuilder;
+import io.split.android.client.SplitClient;
+import io.split.android.client.SplitFactory;
+import io.split.android.client.dtos.SegmentsChange;
+import io.split.android.client.dtos.SplitChange;
+import io.split.android.client.events.SplitEvent;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
+import io.split.android.client.utils.logger.Logger;
+import tests.integration.shared.TestingData;
+import tests.integration.shared.TestingHelper;
+
+public class LargeSegmentsStreamingTest {
+
+    public static final String SPLIT_CHANGES = "splitChanges";
+    public static final String MY_SEGMENTS = IntegrationHelper.ServicePath.MEMBERSHIPS;
+    public static final String AUTH = "v2/auth";
+    public static final String SSE = "sse";
+    private final FileHelper mFileHelper = new FileHelper();
+    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private Map<String, AtomicInteger> mEndpointHits;
+    private Map<String, CountDownLatch> mLatches;
+    private final AtomicInteger mMyLargeSegmentsStatusCode = new AtomicInteger(200);
+    private final AtomicBoolean mRandomizeMyLargeSegments = new AtomicBoolean(false);
+    private BlockingQueue<String> mStreamingData;
+
+    @Before
+    public void setUp() throws IOException, InterruptedException {
+        mStreamingData = new LinkedBlockingQueue<>();
+        mEndpointHits = new ConcurrentHashMap<>();
+        mMyLargeSegmentsStatusCode.set(200);
+        mRandomizeMyLargeSegments.set(false);
+        initializeLatches();
+    }
+
+    @Test
+    public void unboundedLargeSegmentsUpdateTriggersSdkUpdate() throws IOException, InterruptedException {
+        TestSetup testSetup = getTestSetup();
+
+        boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(10, TimeUnit.SECONDS);
+        boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(10, TimeUnit.SECONDS);
+        String initialSegmentList = testSetup.database.myLargeSegmentDao().getByUserKey(IntegrationHelper.dummyUserKey().matchingKey()).getSegmentList();
+        mRandomizeMyLargeSegments.set(true);
+
+        pushMyLargeSegmentsMessage(TestingData.largeSegmentsUnboundedNoCompression("100"));
+        boolean updateAwait = testSetup.updateLatch.await(15, TimeUnit.SECONDS);
+
+        assertTrue(testSetup.await);
+        assertTrue(testSetup.authAwait);
+        assertTrue(mySegmentsAwait);
+        assertTrue(splitsAwait);
+        assertTrue(updateAwait);
+        assertEquals(2, mEndpointHits.get(SPLIT_CHANGES).get());
+        assertTrue(initialSegmentList.contains("large-segment1") && initialSegmentList.contains("large-segment2") && initialSegmentList.contains("large-segment3"));
+        assertEquals(2, Json.fromJson(testSetup.database.myLargeSegmentDao().getByUserKey(IntegrationHelper.dummyUserKey().matchingKey()).getSegmentList(), SegmentsChange.class).getSegments().size());
+        assertEquals(3, mEndpointHits.get(MY_SEGMENTS).get());
+    }
+
+    @Test
+    public void segmentRemovalTriggersSdkUpdateAndRemovesSegmentFromStorage() throws IOException, InterruptedException {
+        TestSetup testSetup = getTestSetup();
+
+        boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(10, TimeUnit.SECONDS);
+        boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(10, TimeUnit.SECONDS);
+
+        SplitRoomDatabase db = testSetup.database;
+        String initialLargeSegmentsSize = db.myLargeSegmentDao()
+                .getByUserKey(IntegrationHelper.dummyUserKey().matchingKey())
+                .getSegmentList();
+
+        pushMyLargeSegmentsMessage(TestingData.largeSegmentsRemoval());
+        boolean updateAwait = testSetup.updateLatch.await(10, TimeUnit.SECONDS);
+
+        assertTrue(testSetup.await);
+        assertTrue(testSetup.authAwait);
+        assertTrue(mySegmentsAwait);
+        assertTrue(splitsAwait);
+        assertTrue(updateAwait);
+        assertTrue(initialLargeSegmentsSize.contains("large-segment1") && initialLargeSegmentsSize.contains("large-segment2") && initialLargeSegmentsSize.contains("large-segment3"));
+        String body = db.myLargeSegmentDao().getByUserKey(IntegrationHelper.dummyUserKey().matchingKey()).getSegmentList();
+        SegmentsChange segmentsChange = Json.fromJson(body, SegmentsChange.class);
+        assertEquals(1702507130121L, segmentsChange.getChangeNumber().longValue());
+        assertEquals(1, segmentsChange.getSegments().size());
+    }
+
+    @NonNull
+    private TestSetup getTestSetup() throws IOException, InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+        CountDownLatch updateLatch = new CountDownLatch(1);
+        SplitRoomDatabase database = DatabaseHelper.getTestDatabase(mContext);
+        SplitFactory factory = getFactory(database);
+
+        SplitClient client = factory.client();
+        client.on(SplitEvent.SDK_READY, TestingHelper.testTask(latch));
+        client.on(SplitEvent.SDK_UPDATE, TestingHelper.testTask(updateLatch));
+        boolean await = latch.await(5, TimeUnit.SECONDS);
+        boolean authAwait = mLatches.get(AUTH).await(6, TimeUnit.SECONDS);
+
+        // Wait for streaming connection
+        mLatches.get(SSE).await(15, TimeUnit.SECONDS);
+
+        // Keep alive on streaming channel confirms connection
+        // so full sync is fired
+        TestingHelper.pushKeepAlive(mStreamingData);
+        return new TestSetup(updateLatch, database, await, authAwait);
+    }
+
+    private SplitFactory getFactory(SplitRoomDatabase database) throws IOException {
+        if (database == null) {
+            database = DatabaseHelper.getTestDatabase(mContext);
+        }
+        TestableSplitConfigBuilder configBuilder = new TestableSplitConfigBuilder()
+                .streamingEnabled(true)
+                .enableDebug();
+
+        return IntegrationHelper.buildFactory(
+                IntegrationHelper.dummyApiKey(),
+                IntegrationHelper.dummyUserKey(),
+                configBuilder.build(),
+                mContext,
+                new HttpClientMock(buildDispatcher()), database, null, null, null);
+    }
+
+    private HttpResponseMockDispatcher buildDispatcher() {
+        Map<String, IntegrationHelper.ResponseClosure> responses = new HashMap<>();
+        responses.put(SPLIT_CHANGES, (path, query, body) -> {
+            updateEndpointHit(SPLIT_CHANGES);
+            return new HttpResponseMock(200, splitChangesLargeSegments(1602796638344L, 1602796638344L));
+        });
+
+        String key = IntegrationHelper.dummyUserKey().matchingKey();
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + key, (path, query, body) -> {
+            updateEndpointHit(MY_SEGMENTS);
+            if (mMyLargeSegmentsStatusCode.get() != 200) {
+                return new HttpResponseMock(mMyLargeSegmentsStatusCode.get());
+            } else {
+                String responseBody = IntegrationHelper.dummyAllSegments();
+                if (mRandomizeMyLargeSegments.get()) {
+                    responseBody = IntegrationHelper.randomizedAllSegments();
+                }
+                return new HttpResponseMock(200, responseBody);
+            }
+        });
+
+        responses.put(AUTH, (path, query, body) -> {
+            try {
+                return new HttpResponseMock(200, IntegrationHelper.streamingEnabledToken());
+            } finally {
+                updateEndpointHit(AUTH);
+            }
+        });
+
+        return IntegrationHelper.buildDispatcher(responses, mStreamingData, mLatches.get(SSE));
+    }
+
+    private String splitChangesLargeSegments(long since, long till) {
+        String change = mFileHelper.loadFileContent(mContext, "split_changes_large_segments-0.json");
+        SplitChange parsedChange = Json.fromJson(change, SplitChange.class);
+        parsedChange.since = since;
+        parsedChange.till = till;
+
+        return Json.toJson(parsedChange);
+    }
+
+    private void initializeLatches() {
+        mLatches = new ConcurrentHashMap<>();
+        mLatches.put(SPLIT_CHANGES, new CountDownLatch(2));
+        mLatches.put(MY_SEGMENTS, new CountDownLatch(2));
+        mLatches.put(AUTH, new CountDownLatch(1));
+        mLatches.put(SSE, new CountDownLatch(1));
+    }
+
+    private void updateEndpointHit(String splitChanges) {
+        if (mEndpointHits.containsKey(splitChanges)) {
+            mEndpointHits.get(splitChanges).getAndIncrement();
+        } else {
+            mEndpointHits.put(splitChanges, new AtomicInteger(1));
+        }
+
+        if (mLatches.containsKey(splitChanges)) {
+            mLatches.get(splitChanges).countDown();
+        }
+    }
+
+    private void pushMyLargeSegmentsMessage(String msg) {
+        String MSG_SEGMENT_UPDATE_TEMPLATE = "push_msg-largesegment_update.txt";
+        BlockingQueue<String> queue = mStreamingData;
+        String message = loadMockedData(MSG_SEGMENT_UPDATE_TEMPLATE);
+        message = message.replace("$TIMESTAMP$", String.valueOf(System.currentTimeMillis()));
+        message = message.replace(TestingHelper.MSG_DATA_FIELD, msg);
+        try {
+            queue.put(message + "" + "\n");
+            Logger.d("Pushed message: " + message);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    private String loadMockedData(String fileName) {
+        return mFileHelper.loadFileContent(mContext, fileName);
+    }
+
+    private static class TestSetup {
+        public final CountDownLatch updateLatch;
+        public final SplitRoomDatabase database;
+        public final boolean await;
+        public final boolean authAwait;
+
+        public TestSetup(CountDownLatch updateLatch, SplitRoomDatabase database, boolean await, boolean authAwait) {
+            this.updateLatch = updateLatch;
+            this.database = database;
+            this.await = await;
+            this.authAwait = authAwait;
+        }
+    }
+}
diff --git a/src/androidTest/java/tests/integration/largesegments/LargeSegmentsTest.java b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsTest.java
new file mode 100644
index 000000000..a2f286827
--- /dev/null
+++ b/src/androidTest/java/tests/integration/largesegments/LargeSegmentsTest.java
@@ -0,0 +1,168 @@
+package tests.integration.largesegments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import helper.DatabaseHelper;
+import helper.IntegrationHelper;
+import helper.TestableSplitConfigBuilder;
+import io.split.android.client.SplitClient;
+import io.split.android.client.SplitFactory;
+import io.split.android.client.SplitFactoryBuilder;
+import io.split.android.client.dtos.SegmentsChange;
+import io.split.android.client.events.SplitEvent;
+import io.split.android.client.exceptions.SplitInstantiationException;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
+import tests.integration.shared.TestingHelper;
+
+public class LargeSegmentsTest extends LargeSegmentTestHelper {
+
+    @Test
+    public void sdkReadyIsEmittedAfterLargeSegmentsAreSynced() {
+        SplitFactory factory = getFactory();
+
+        SplitClient readyClient = getReadyClient(IntegrationHelper.dummyUserKey().matchingKey(), factory);
+
+        assertEquals(1, mEndpointHits.get("splitChanges").get());
+        assertEquals(1, mEndpointHits.get(IntegrationHelper.ServicePath.MEMBERSHIPS).get());
+    }
+
+    @Test
+    public void sdkReadyIsEmittedAfterLargeSegmentsAreSyncedWhenWaitForLargeSegmentsIsFalse() {
+        mMySegmentsDelay.set(4000);
+        SplitFactory factory = getFactory();
+        getReadyClient(IntegrationHelper.dummyUserKey().matchingKey(), factory);
+
+
+        assertEquals(1, mEndpointHits.get("splitChanges").get());
+        assertEquals(1, mEndpointHits.get(IntegrationHelper.ServicePath.MEMBERSHIPS).get());
+    }
+
+    @Test
+    public void sdkUpdateIsEmittedForLargeSegmentsWhenLargeSegmentsChange() throws InterruptedException {
+        mMySegmentsDelay.set(0L);
+        mRandomizeMyLargeSegments.set(true);
+        CountDownLatch updateLatch = new CountDownLatch(3);
+        CountDownLatch readyLatch = new CountDownLatch(1);
+        SplitFactory factory = getFactory(1, null, null);
+
+        SplitClient client = factory.client();
+        client.on(SplitEvent.SDK_READY, TestingHelper.testTask(readyLatch));
+        client.on(SplitEvent.SDK_UPDATE, TestingHelper.testTask(updateLatch));
+
+        boolean readyAwait = readyLatch.await(5, TimeUnit.SECONDS);
+        boolean await = updateLatch.await(5, TimeUnit.SECONDS);
+
+        assertTrue(readyAwait);
+        assertTrue(await);
+    }
+
+    @Test
+    public void sdkReadyFromCacheIsEmittedAfterLargeSegmentsAreSynced() throws InterruptedException {
+        SplitRoomDatabase testDatabase = DatabaseHelper.getTestDatabase(mContext);
+        // first, prepopulate local cache
+        SplitFactory factory = getFactory(null, null, testDatabase);
+        SplitClient readyClient = getReadyClient(IntegrationHelper.dummyUserKey().matchingKey(), factory);
+
+        String firstEval = readyClient.getTreatment("ls_split");
+
+        // make all api requests fail, we only want sdk_ready_from_cache
+        mBrokenApi.set(true);
+
+        factory = getFactory(null, 1000, testDatabase);
+        CountDownLatch fromCacheLatch = new CountDownLatch(1);
+        CountDownLatch timeoutLatch = new CountDownLatch(1);
+        SplitClient client = factory.client();
+        client.on(SplitEvent.SDK_READY_FROM_CACHE, TestingHelper.testTask(fromCacheLatch));
+        client.on(SplitEvent.SDK_READY_TIMED_OUT, TestingHelper.testTask(timeoutLatch));
+
+        boolean fromCacheAwait = fromCacheLatch.await(5, TimeUnit.SECONDS);
+        boolean timeoutAwait = timeoutLatch.await(5, TimeUnit.SECONDS);
+
+        String lsSplit = client.getTreatment("ls_split");
+
+        assertTrue(fromCacheAwait);
+        assertTrue(timeoutAwait);
+        assertEquals("on", firstEval);
+        assertEquals("on", lsSplit);
+
+    }
+
+    @Test
+    public void successfulSyncOfLargeSegmentsContainsSegmentsInDatabase() {
+        SplitRoomDatabase testDatabase = DatabaseHelper.getTestDatabase(mContext);
+
+        SplitFactory factory = getFactory(null, null, testDatabase);
+        getReadyClient(IntegrationHelper.dummyUserKey().matchingKey(), factory);
+
+        SegmentsChange largeSegments = Json.fromJson(testDatabase.myLargeSegmentDao()
+                .getByUserKey(IntegrationHelper.dummyUserKey().matchingKey())
+                .getSegmentList(), SegmentsChange.class);
+        List<String> mySegments = largeSegments.getNames();
+        assertEquals(3, mySegments.size());
+        assertTrue(mySegments.contains("large-segment1") && mySegments.contains("large-segment2") && mySegments.contains("large-segment3"));
+        assertEquals(1702507130121L, largeSegments.getChangeNumber().longValue());
+    }
+
+    @Test
+    public void syncOfLargeSegmentsForMultiClient() throws InterruptedException {
+        mRandomizeMyLargeSegments.set(true);
+        SplitRoomDatabase testDatabase = DatabaseHelper.getTestDatabase(mContext);
+        SplitFactory factory = getFactory(10, null, testDatabase);
+
+        SplitClient client1 = factory.client();
+        SplitClient client2 = factory.client("key2");
+
+        CountDownLatch latch = new CountDownLatch(2);
+        client1.on(SplitEvent.SDK_READY, TestingHelper.testTask(latch));
+        client2.on(SplitEvent.SDK_READY, TestingHelper.testTask(latch));
+
+        latch.await(10, TimeUnit.SECONDS);
+
+        SegmentsChange segmentList1 = Json.fromJson(testDatabase.myLargeSegmentDao().getByUserKey("CUSTOMER_ID").getSegmentList(), SegmentsChange.class);
+        SegmentsChange segmentList2 = Json.fromJson(testDatabase.myLargeSegmentDao().getByUserKey("key2").getSegmentList(), SegmentsChange.class);
+
+        assertEquals(2, segmentList1.getNames().size());
+        assertEquals(2, segmentList2.getNames().size());
+        assertNotEquals(segmentList1,
+                segmentList2);
+        assertEquals(1702507130121L, segmentList1.getChangeNumber().longValue());
+        assertEquals(1702507130121L, segmentList2.getChangeNumber().longValue());
+    }
+
+    @Test
+    public void emptyMyLargeSegmentsSdkIsReady() throws InterruptedException {
+        mMySegmentsDelay.set(0L);
+        mEmptyMyLargeSegments.set(true);
+        SplitFactory factory = getFactory(null, null, null);
+        SplitClient client = factory.client();
+        CountDownLatch readyLatch = new CountDownLatch(1);
+        client.on(SplitEvent.SDK_READY, TestingHelper.testTask(readyLatch));
+        boolean readyAwait = readyLatch.await(5, TimeUnit.SECONDS);
+
+        assertEquals(1, mEndpointHits.get("splitChanges").get());
+        assertEquals(1, mEndpointHits.get(IntegrationHelper.ServicePath.MEMBERSHIPS).get());
+        assertTrue(readyAwait);
+    }
+
+    @Test
+    public void localhostModeIsReadyWhenWaitForLargeSegmentsIsTrue() throws SplitInstantiationException, InterruptedException {
+        SplitFactory factory = SplitFactoryBuilder.build("localhost", IntegrationHelper.dummyUserKey(),
+                new TestableSplitConfigBuilder().build(), mContext);
+
+        CountDownLatch readyLatch = new CountDownLatch(1);
+        factory.client().on(SplitEvent.SDK_READY, TestingHelper.testTask(readyLatch));
+
+        boolean await = readyLatch.await(5, TimeUnit.SECONDS);
+
+        assertTrue(await);
+    }
+}
diff --git a/src/androidTest/java/tests/integration/matcher/SemverMatcherTest.java b/src/androidTest/java/tests/integration/matcher/SemverMatcherTest.java
index d09de02e6..d0e63c4e8 100644
--- a/src/androidTest/java/tests/integration/matcher/SemverMatcherTest.java
+++ b/src/androidTest/java/tests/integration/matcher/SemverMatcherTest.java
@@ -147,7 +147,7 @@ private HttpResponseMockDispatcher getDispatcher() {
             }
         });
 
-        responses.put("mySegments/test", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/test", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         responses.put("v2/auth", (uri, httpMethod, body) -> {
             mAuthLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java b/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java
index e0d40995d..928d1d93a 100644
--- a/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java
+++ b/src/androidTest/java/tests/integration/matcher/UnsupportedMatcherTest.java
@@ -128,7 +128,7 @@ private HttpResponseMockDispatcher getDispatcher() {
             }
         });
 
-        responses.put("mySegments/test", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/test", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         responses.put("v2/auth", (uri, httpMethod, body) -> {
             mAuthLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/pin/CertPinningTest.java b/src/androidTest/java/tests/integration/pin/CertPinningTest.java
index 088167a40..16f6b45cc 100644
--- a/src/androidTest/java/tests/integration/pin/CertPinningTest.java
+++ b/src/androidTest/java/tests/integration/pin/CertPinningTest.java
@@ -79,11 +79,11 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
                         mEndpointHits.put("splitChanges", new AtomicInteger(1));
                     }
                     return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.emptySplitChanges(1602796638344L, 1602796638344L));
-                } else if (request.getRequestUrl().encodedPathSegments().contains("mySegments")) {
-                    if (mEndpointHits.containsKey("mySegments")) {
-                        mEndpointHits.get("mySegments").getAndIncrement();
+                } else if (request.getRequestUrl().encodedPathSegments().contains(IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    if (mEndpointHits.containsKey(IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                        mEndpointHits.get(IntegrationHelper.ServicePath.MEMBERSHIPS).getAndIncrement();
                     } else {
-                        mEndpointHits.put("mySegments", new AtomicInteger(1));
+                        mEndpointHits.put(IntegrationHelper.ServicePath.MEMBERSHIPS, new AtomicInteger(1));
                     }
                     return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.emptyMySegments());
                 } else {
diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java
index 51fe77397..86f42034f 100644
--- a/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java
+++ b/src/androidTest/java/tests/integration/sets/FlagSetsEvaluationTest.java
@@ -136,7 +136,7 @@ private SplitClient getClient(
             }
         });
 
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         HttpResponseMockDispatcher httpResponseMockDispatcher = IntegrationHelper.buildDispatcher(responses);
 
diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java
index 932c505b9..059d7c97d 100644
--- a/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java
+++ b/src/androidTest/java/tests/integration/sets/FlagSetsMultipleFactoryTest.java
@@ -142,7 +142,7 @@ private HttpResponseMockDispatcher getDispatcher(int setsCount) {
             }
         });
 
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         return IntegrationHelper.buildDispatcher(responses);
     }
diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java
index f9bedbf44..6faaab513 100644
--- a/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java
+++ b/src/androidTest/java/tests/integration/sets/FlagSetsPollingTest.java
@@ -211,7 +211,7 @@ private SplitFactory createFactory(
             }
         });
 
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
 
         HttpResponseMockDispatcher httpResponseMockDispatcher = IntegrationHelper.buildDispatcher(responses);
 
diff --git a/src/androidTest/java/tests/integration/sets/FlagSetsStreamingTest.java b/src/androidTest/java/tests/integration/sets/FlagSetsStreamingTest.java
index 20d3afa06..50f478d44 100644
--- a/src/androidTest/java/tests/integration/sets/FlagSetsStreamingTest.java
+++ b/src/androidTest/java/tests/integration/sets/FlagSetsStreamingTest.java
@@ -227,7 +227,7 @@ private SplitClient getReadyClient(
             mSplitChangesHits.incrementAndGet();
             return new HttpResponseMock(200, IntegrationHelper.emptySplitChanges(-1, 1));
         });
-        responses.put("mySegments/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/CUSTOMER_ID", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
         responses.put("v2/auth", (uri, httpMethod, body) -> {
             authLatch.countDown();
             return new HttpResponseMock(200, IntegrationHelper.streamingEnabledToken());
diff --git a/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java b/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java
index 56a7b8662..65167f866 100644
--- a/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java
+++ b/src/androidTest/java/tests/integration/shared/InterdependentSplitsTest.java
@@ -9,7 +9,6 @@
 import java.util.concurrent.TimeUnit;
 
 import helper.IntegrationHelper;
-import helper.TestingHelper;
 import io.split.android.client.SplitClient;
 import io.split.android.client.api.Key;
 import io.split.android.client.events.SplitEvent;
@@ -29,16 +28,16 @@ protected Dispatcher getDispatcher() {
         return new Dispatcher() {
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments/key1")) {
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key1")) {
                     return new MockResponse()
                             .setResponseCode(200)
-                            .setBody("{\"mySegments\":[{ \"id\":\"id0\", \"name\":\"android_test\"}]}");
-                } else if (request.getPath().contains("/mySegments/key2")) {
+                            .setBody(IntegrationHelper.dummySingleSegment("android_test"));
+                } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key2")) {
                     return new MockResponse()
                             .setResponseCode(200)
                             .setBody(IntegrationHelper.emptyMySegments());
                 } else if (request.getPath().contains("/splitChanges")) {
-                    String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]}],\"since\":-1,\"till\":1648733409158}";
+                    String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]}],\"since\":1648733409158,\"till\":1648733409158}";
 
                     return new MockResponse().setResponseCode(200)
                             .setBody(splitChange);
diff --git a/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java b/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java
index c78ea56f1..7b5423f8d 100644
--- a/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java
+++ b/src/androidTest/java/tests/integration/shared/MySegmentsBeforeSplitsTest.java
@@ -8,7 +8,6 @@
 import java.util.concurrent.TimeUnit;
 
 import helper.IntegrationHelper;
-import helper.TestingHelper;
 import io.split.android.client.SplitClient;
 import io.split.android.client.api.Key;
 import io.split.android.client.events.SplitEvent;
@@ -23,16 +22,16 @@ public Dispatcher getDispatcher() {
         return new Dispatcher() {
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                if (request.getPath().contains("/mySegments/key1")) {
+                if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key1")) {
                     return new MockResponse()
                             .setResponseCode(200)
-                            .setBody("{\"mySegments\":[{ \"id\":\"id0\", \"name\":\"android_test\"}]}");
-                } else if (request.getPath().contains("/mySegments/key2")) {
+                            .setBody(IntegrationHelper.dummySingleSegment("android_test"));
+                } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key2")) {
                     return new MockResponse()
                             .setResponseCode(200)
                             .setBody(IntegrationHelper.emptyMySegments());
                 } else if (request.getPath().contains("/splitChanges")) {
-                    String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"}]}],\"since\":-1,\"till\":1648733409158}";
+                    String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"}]}],\"since\":1648733409158,\"till\":1648733409158}";
                     new CountDownLatch(1).await(500, TimeUnit.MILLISECONDS);
                     return new MockResponse().setResponseCode(200)
                             .setBody(splitChange);
diff --git a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java
index 034311c4b..11d8af38e 100644
--- a/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java
+++ b/src/androidTest/java/tests/integration/shared/SharedClientsIntegrationTest.java
@@ -39,7 +39,6 @@
 import io.split.android.client.storage.db.SplitEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
 import io.split.android.client.utils.Json;
-import io.split.android.client.utils.logger.Logger;
 import io.split.android.client.utils.logger.SplitLogLevel;
 import okhttp3.mockwebserver.Dispatcher;
 import okhttp3.mockwebserver.MockResponse;
@@ -292,14 +291,14 @@ private void setupServer() {
 
                 @Override
                 public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
-                    if (request.getPath().contains("/mySegments/key1")) {
+                    if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key1")) {
                         return new MockResponse()
                                 .setResponseCode(200)
-                                .setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
-                    } else if (request.getPath().contains("/mySegments/key2")) {
+                                .setBody(IntegrationHelper.dummyAllSegments());
+                    } else if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key2")) {
                         return new MockResponse()
                                 .setResponseCode(200)
-                                .setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                                .setBody(IntegrationHelper.dummySingleSegment("segment2"));
                     } else if (request.getPath().contains("/splitChanges")) {
                         return new MockResponse().setResponseCode(200)
                                 .setBody(splitsPerRequest());
diff --git a/src/androidTest/java/tests/integration/shared/TestingData.java b/src/androidTest/java/tests/integration/shared/TestingData.java
index eab9b6852..2a096a950 100644
--- a/src/androidTest/java/tests/integration/shared/TestingData.java
+++ b/src/androidTest/java/tests/integration/shared/TestingData.java
@@ -1,26 +1,23 @@
 package tests.integration.shared;
 
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
-import io.split.android.client.utils.Json;
-
 public class TestingData {
 
     public final static String UNBOUNDED_NOTIFICATION = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 0," +
             "\\\"c\\\": 0," +
             "\\\"d\\\": \\\"\\\"," +
-            "\\\"segmentName\\\": \\\"pepe\\\"," +
-            "\\\"changeNumber\\\": 28" +
+            "\\\"n\\\": [\\\"pepe\\\"]," +
+            "\\\"cn\\\": 28" +
             "}";
 
     public final static String SEGMENT_REMOVAL_NOTIFICATION = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 3," +
             "\\\"c\\\": 0," +
             "\\\"d\\\": \\\"\\\"," +
-            "\\\"segmentName\\\": \\\"segment1\\\"," +
-            "\\\"changeNumber\\\": 28" +
+            "\\\"n\\\": [\\\"segment1\\\"]," +
+            "\\\"cn\\\": 28" +
             "}";
 
     /**
@@ -39,28 +36,28 @@ public class TestingData {
       8492584437244343049 11382796718859679607 11383137936375052427 17699699514337596928 17001541343685934583 8355202062888946034]
      */
     public final static String BOUNDED_NOTIFICATION_GZIP = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 1," +
             "\"d\": \"H4sIAAAAAAAA/2IYBfgAx0A7YBTgB4wD7YABAAID7QC6g5EYy8MEMA20A+gMFAbaAYMZDPXqlGWgHTAKRsEoGAWjgCzQQFjJkKqiiPAPAQAIAAD//5L7VQwAEAAA\"" +
             "}";
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_GZIP = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAA/2IYBfgAx0A7YBTgB4wD7YABAAID7QC6g5EYy8MEMA20A+gMFAbaAYMZDPXqlGWgHTAKRsEoGAWjgCzQQFjJkKqiiPAPAQAIAAD//5L7VQwAEAAA\\\"" +
             "}";
 
     public final static String BOUNDED_NOTIFICATION_ZLIB = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 2," +
             "\"d\": \"eJxiGAX4AMdAO2AU4AeMA+2AAQACA+0AuoORGMvDBDANtAPoDBQG2gGDGQz16pRloB0wCkbBKBgFo4As0EBYyZCqoojwDwEACAAA//+W/QFR\"" +
             "}";
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_ZLIB = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 2," +
             "\\\"d\\\": \\\"eJxiGAX4AMdAO2AU4AeMA+2AAQACA+0AuoORGMvDBDANtAPoDBQG2gGDGQz16pRloB0wCkbBKBgFo4As0EBYyZCqoojwDwEACAAA//+W/QFR\\\"" +
@@ -68,7 +65,7 @@ public class TestingData {
 
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_MALFORMED = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAAg5EYy8MEMA20A+//5L7VQwAEAAA\\\"" +
@@ -80,49 +77,75 @@ public class TestingData {
      * = a: [key1, key2] , r: [key3, key4]
      */
     public final static String KEY_LIST_NOTIFICATION_GZIP = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 2," +
             "\"c\": 1," +
             "\"d\": \"H4sIAAAAAAAA/wTAsRHDUAgD0F2ofwEIkPAqPhdZIW0uu/v97GPXHU004ULuMGrYR6XUbIjlXULPPse+dt1yhJibBODjrTmj3GJ4emduuDDP/w0AAP//18WLsl0AAAA=\"" +
             "}";
 
     public final static String ESCAPED_KEY_LIST_NOTIFICATION_GZIP = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
-            "\\\"segmentName\\\": \\\"new_segment_added\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
+            "\\\"n\\\": [\\\"new_segment_added\\\"]," +
             "\\\"u\\\": 2," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAA/wTAsRHDUAgD0F2ofwEIkPAqPhdZIW0uu/v97GPXHU004ULuMGrYR6XUbIjlXULPPse+dt1yhJibBODjrTmj3GJ4emduuDDP/w0AAP//18WLsl0AAAA=\\\"" +
             "}";
 
 public final static String BOUNDED_NOTIFICATION_ZLIB_2 = "{" +
-            "\"changeNumber\": 1629754722111, " +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"cn\": 1629754722111, " +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 2," +
             "\"d\": \"eJzMVk3OhDAIVdNFl9/22zVzEo8yR5mjT6LGsRTKg2LiW8yPUnjQB+2kIwM2ThTIKtVU1oknFcRzufz+YGYM/phnHW8sdPvs9EzXW2I+HFzhNyTNgCD/PpW9xpGiHD0Bw1U5HLSS644FbGZgoPovmjpmX5wAzhIxJyN7IAnFQWX1htj+LUl6ZQRV3umMqYG1LCrOJGLPV8+IidBQZFt6sOUA6CqsX5iEFY2gqufs2mfqRtsVWytRnO+iYMN7xIBqJhDqAydV+HidkGOGEJYvk4fhe/8iIukphG/XfFcfVxnMVcALCOF77qL/EU7ODepxlLST6qxFLYRdOyW8EBY4BqVjObnm3V5ZMkZIKf++8+hM7zM1Kd3aFqVZeSHzDQAA//+QUQ3a\"" +
             "}";
 //    c: 2
-//    changeNumber: 1629754722111
+//    cn: 1629754722111
 //    d: "eJzMVk3OhDAIVdNFl9/22zVzEo8yR5mjT6LGsRTKg2LiW8yPUnjQB+2kIwM2ThTIKtVU1oknFcRzufz+YGYM/phnHW8sdPvs9EzXW2I+HFzhNyTNgCD/PpW9xpGiHD0Bw1U5HLSS644FbGZgoPovmjpmX5wAzhIxJyN7IAnFQWX1htj+LUl6ZQRV3umMqYG1LCrOJGLPV8+IidBQZFt6sOUA6CqsX5iEFY2gqufs2mfqRtsVWytRnO+iYMN7xIBqJhDqAydV+HidkGOGEJYvk4fhe/8iIukphG/XfFcfVxnMVcALCOF77qL/EU7ODepxlLST6qxFLYRdOyW8EBY4BqVjObnm3V5ZMkZIKf++8+hM7zM1Kd3aFqVZeSHzDQAA//+QUQ3a"
-//    segmentName: ""
-//    type: "MY_SEGMENTS_UPDATE_V2"
+//    n: ""
+//    type: "MEMBERSHIPS_MS_UPDATE"
 //    u: 1
 
     public final static String DECOMPRESSED_KEY_LIST_PAYLOAD_GZIP = "{\"a\":[1573573083296714675,8482869187405483569],\"r\":[8031872927333060586,6829471020522910836]}";
 
-    public static String encodedKeyListPayloadGzip() {
-        return (Json.fromJson(KEY_LIST_NOTIFICATION_GZIP, MySegmentChangeV2Notification.class)).getData();
-    }
-
-    public static String encodedBoundedPayloadZlib() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB, MySegmentChangeV2Notification.class)).getData();
+    public static String segmentsUnboundedNoCompression(String intervalMs) {
+        return "{" +
+                "\\\"type\\\":\\\"MEMBERSHIPS_MS_UPDATE\\\"" +
+                ",\\\"cn\\\":1702507130121," +
+                "\\\"n\\\":[\\\"android_test\\\",\\\"ios_test\\\"]," +
+                "\\\"c\\\":0," +
+                "\\\"u\\\":0," +
+                "\\\"d\\\":\\\"\\\"," +
+                "\\\"i\\\":" + intervalMs + "," +
+                "\\\"h\\\":0," +
+                "\\\"s\\\":0" +
+                "}";
     }
 
-    public static String encodedBoundedPayloadZlib2() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB_2, MySegmentChangeV2Notification.class)).getData();
+    public static String largeSegmentsUnboundedNoCompression(String intervalMs) {
+        return "{" +
+                "\\\"type\\\":\\\"MEMBERSHIPS_LS_UPDATE\\\"" +
+                ",\\\"cn\\\":1702507130121," +
+                "\\\"n\\\":[\\\"android_test\\\",\\\"ios_test\\\"]," +
+                "\\\"c\\\":0," +
+                "\\\"u\\\":0," +
+                "\\\"d\\\":\\\"\\\"," +
+                "\\\"i\\\":" + intervalMs + "," +
+                "\\\"h\\\":0," +
+                "\\\"s\\\":0" +
+                "}";
     }
 
-    public static String encodedBoundedPayloadGzip() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_GZIP, MySegmentChangeV2Notification.class)).getData();
+    public static String largeSegmentsRemoval() {
+        return "{" +
+                "\\\"type\\\":\\\"MEMBERSHIPS_LS_UPDATE\\\"" +
+                ",\\\"cn\\\":1702507130121," +
+                "\\\"n\\\":[\\\"large-segment1\\\",\\\"large-segment2\\\"]," +
+                "\\\"c\\\":0," +
+                "\\\"u\\\":3," +
+                "\\\"d\\\":\\\"\\\"," +
+                "\\\"i\\\":100," +
+                "\\\"h\\\":0," +
+                "\\\"s\\\":0" +
+                "}";
     }
 }
diff --git a/src/androidTest/java/tests/integration/streaming/AblyErrorBaseTest.java b/src/androidTest/java/tests/integration/streaming/AblyErrorBaseTest.java
index e9cd41a9b..407c612e3 100644
--- a/src/androidTest/java/tests/integration/streaming/AblyErrorBaseTest.java
+++ b/src/androidTest/java/tests/integration/streaming/AblyErrorBaseTest.java
@@ -120,10 +120,10 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsSyncLatch.countDown();
 
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     mSplitsSyncLatch.countDown();
                     String data = IntegrationHelper.emptySplitChanges(1000, 1000);
diff --git a/src/androidTest/java/tests/integration/streaming/CleanUpDatabaseTest.java b/src/androidTest/java/tests/integration/streaming/CleanUpDatabaseTest.java
index 244d85f67..c3556e838 100644
--- a/src/androidTest/java/tests/integration/streaming/CleanUpDatabaseTest.java
+++ b/src/androidTest/java/tests/integration/streaming/CleanUpDatabaseTest.java
@@ -244,8 +244,8 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     String data = IntegrationHelper.emptySplitChanges(-1, 1000);
                     return createResponse(200, data);
@@ -284,11 +284,4 @@ private void pushMessage(String fileName) {
         } catch (InterruptedException e) {
         }
     }
-
-    private String updatedMySegments() {
-        return "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, " +
-                " { \"id\":\"id1\", \"name\":\"segment2\"}, " +
-                "{ \"id\":\"id3\", \"name\":\"segment3\"}]}";
-    }
-
 }
diff --git a/src/androidTest/java/tests/integration/streaming/ControlTest.java b/src/androidTest/java/tests/integration/streaming/ControlTest.java
index 200fb5138..a2eb0cb8e 100644
--- a/src/androidTest/java/tests/integration/streaming/ControlTest.java
+++ b/src/androidTest/java/tests/integration/streaming/ControlTest.java
@@ -1,7 +1,7 @@
 package tests.integration.streaming;
 
 import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.assertEquals;
+import static junit.framework.Assert.fail;
 import static java.lang.Thread.sleep;
 
 import android.content.Context;
@@ -9,7 +9,6 @@
 import androidx.core.util.Pair;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -20,10 +19,12 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import fake.HttpClientMock;
 import fake.HttpResponseMock;
 import fake.HttpResponseMockDispatcher;
+import fake.HttpStreamResponseMock;
 import fake.SynchronizerSpyImpl;
 import helper.DatabaseHelper;
 import helper.FileHelper;
@@ -43,7 +44,7 @@
 import io.split.android.client.telemetry.storage.TelemetryStorage;
 import io.split.android.client.utils.Json;
 import io.split.android.client.utils.logger.Logger;
-import fake.HttpStreamResponseMock;
+import tests.integration.shared.TestingData;
 import tests.integration.shared.TestingHelper;
 
 public class ControlTest {
@@ -66,6 +67,8 @@ public class ControlTest {
     private int mSseConnectionCount;
 
     String mSplitChange;
+    private final AtomicReference<String> mCurrentResponse = new AtomicReference<>();
+    private CountDownLatch mSplitChangesLatch = null;
 
     @Before
     public void setup() {
@@ -80,6 +83,7 @@ public void setup() {
         mApiKey = apiKeyAndDb.first;
 
         mUserKey = IntegrationHelper.dummyUserKey();
+        mCurrentResponse.set(IntegrationHelper.emptyAllSegments());
     }
 
     @Test
@@ -90,9 +94,8 @@ public void controlNotification() throws IOException, InterruptedException {
         SplitRoomDatabase db = DatabaseHelper.getTestDatabase(mContext);
         db.clearAllTables();
 
-
         CountDownLatch readyLatch = new CountDownLatch(1);
-        CountDownLatch updateLatch = new CountDownLatch(3);
+        CountDownLatch updateLatch = new CountDownLatch(1);
         SplitEventTaskHelper updateTask = new SplitEventTaskHelper(updateLatch);
 
         HttpClientMock httpClientMock = new HttpClientMock(createBasicResponseDispatcher());
@@ -104,7 +107,6 @@ public void controlNotification() throws IOException, InterruptedException {
                 mApiKey, mUserKey,
                 config, mContext, httpClientMock, db, synchronizerSpy, null, null, telemetryStorage);
 
-//        mClient = mFactory.client();
         SplitClient mClient = mFactory.client();
         String splitName = "workm";
 
@@ -120,31 +122,36 @@ public void controlNotification() throws IOException, InterruptedException {
         String treatmentReady = mClient.getTreatment(splitName);
 
         // Pause streaming
-        synchronizerSpy.startPeriodicFetchLatch = new CountDownLatch(1);
         pushControl("STREAMING_PAUSED");
-        synchronizerSpy.startPeriodicFetchLatch.await(10, TimeUnit.SECONDS);
-
-        pushMySegmentsUpdatePayload("new_segment");
 
-        sleep(1000);
+        pushMySegmentsUpdatePayload();
 
         String treatmentPaused = mClient.getTreatment(splitName);
         // Enable streaming, push a new my segments payload update and check data again
-        synchronizerSpy.stopPeriodicFetchLatch = new CountDownLatch(1);
+        mSplitChangesLatch = new CountDownLatch(1);
         pushControl("STREAMING_RESUMED");
-        synchronizerSpy.stopPeriodicFetchLatch.await(10, TimeUnit.SECONDS);
 
-        pushMySegmentsUpdatePayload("new_segment");
-        updateLatch.await(10, TimeUnit.SECONDS);
+        boolean await1 = mSplitChangesLatch.await(10, TimeUnit.SECONDS);
+        if (!await1) {
+            fail("Split changes latch count is " + mSplitChangesLatch.getCount());
+        }
+
+        mClient.on(SplitEvent.SDK_UPDATE, TestingHelper.testTask(updateLatch));
+        mCurrentResponse.set(IntegrationHelper.dummySingleSegment("new_segment"));
+        pushMySegmentsUpdatePayload();
+        boolean await = updateLatch.await(10, TimeUnit.SECONDS);
+        if (!await) {
+            fail("Update latch count is " + updateLatch.getCount());
+        }
 
         String treatmentEnabled = mClient.getTreatment(splitName);
 
         //Enable streaming, push a new my segments payload update and check data again
-        updateLatch = new CountDownLatch(1);
+        mCurrentResponse.set(IntegrationHelper.emptyAllSegments());
         pushControl("STREAMING_DISABLED");
-        updateLatch.await(5, TimeUnit.SECONDS);
-        pushMySegmentsUpdatePayload("new_segment");
-        updateLatch.await(5, TimeUnit.SECONDS);
+
+        pushMySegmentsUpdatePayload();
+
         String treatmentDisabled = mClient.getTreatment(splitName);
 
         assertTrue(telemetryStorage.popStreamingEvents().stream().anyMatch(event -> {
@@ -203,19 +210,12 @@ public void streamResetControlNotification() throws IOException, InterruptedExce
         mClient.destroy();
     }
 
-    private void pushMySegmentsUpdatePayload(String segmentName) throws IOException, InterruptedException {
+    private void pushMySegmentsUpdatePayload() throws InterruptedException {
         mPushLatch = new CountDownLatch(1);
-        pushMySegmentMessage(segmentName);
+        pushMySegmentMessage();
         mPushLatch.await(10, TimeUnit.SECONDS);
     }
 
-    @After
-    public void tearDown() {
-//        if (mFactory != null) {
-//            mFactory.destroy();
-//        }
-    }
-
     private HttpResponseMock createResponse(int status, String data) {
         return new HttpResponseMock(status, data);
     }
@@ -233,11 +233,15 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
 
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
-                    return createResponse(200, IntegrationHelper.emptyMySegments());
-                } else if (uri.getPath().contains("/splitChanges")) {
 
+                    return createResponse(200, mCurrentResponse.get());
+
+                } else if (uri.getPath().contains("/splitChanges")) {
+                    if (mSplitChangesLatch != null) {
+                        mSplitChangesLatch.countDown();
+                    }
                     Logger.i("** Split Changes hit");
                     return createResponse(200, mSplitChange);
                 } else if (uri.getPath().contains("/auth")) {
@@ -275,28 +279,15 @@ private String loadSplitChanges() {
         return Json.toJson(change);
     }
 
-    private void pushMySegmentMessage(String segmentName) {
-        String message = loadMockedData(MSG_SEGMENT_UPDATE_PAYLOAD);
-        message = message.replace("[SEGMENT_NAME]", segmentName);
-        mTimestamp += 100;
-        message = message.replace(CONTROL_TIMESTAMP_PLACEHOLDER, String.valueOf(mTimestamp));
+    private void pushMySegmentMessage() {
+        String msg = TestingData.segmentsUnboundedNoCompression("1");
+        String MSG_SEGMENT_UPDATE_TEMPLATE = "push_msg-largesegment_update.txt";
+        BlockingQueue<String> queue = mStreamingData;
+        String message = loadMockedData(MSG_SEGMENT_UPDATE_TEMPLATE);
+        message = message.replace("$TIMESTAMP$", String.valueOf(System.currentTimeMillis()));
+        message = message.replace(TestingHelper.MSG_DATA_FIELD, msg);
         try {
-            mStreamingData.put(message + "" + "\n");
-            sleep(500);
-            mPushLatch.countDown();
-            Logger.d("Pushed message: " + message);
-        } catch (InterruptedException e) {
-        }
-    }
-
-    private void pushMessage(String fileName) {
-        String message = loadMockedData(fileName);
-        mTimestamp += 100;
-        message = message.replace(CONTROL_TIMESTAMP_PLACEHOLDER, String.valueOf(mTimestamp));
-        try {
-            mStreamingData.put(message + "" + "\n");
-            sleep(200);
-            mPushLatch.countDown();
+            queue.put(message + "" + "\n");
             Logger.d("Pushed message: " + message);
         } catch (InterruptedException e) {
         }
@@ -310,6 +301,7 @@ private void pushControl(String controlType) {
 
         try {
             mStreamingData.put(message + "" + "\n");
+            Logger.e("Pushed control: " + controlType);
             Thread.sleep(500);
             Logger.d("Pushed message: " + message);
         } catch (InterruptedException ignored) {
diff --git a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java
index 1b32c3ec0..d816ee747 100644
--- a/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java
+++ b/src/androidTest/java/tests/integration/streaming/ImpressionsCountTest.java
@@ -195,8 +195,8 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
             boolean first = true;
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     if (first) {
                         first = false;
diff --git a/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2MultiClientTest.java b/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2MultiClientTest.java
index 6790672fe..f12b98ca6 100644
--- a/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2MultiClientTest.java
+++ b/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2MultiClientTest.java
@@ -34,12 +34,14 @@
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
 import io.split.android.client.api.Key;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEvent;
 import io.split.android.client.network.HttpMethod;
 import io.split.android.client.storage.db.MySegmentDao;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
 import io.split.android.client.telemetry.storage.InMemoryTelemetryStorage;
+import io.split.android.client.utils.Json;
 import io.split.android.client.utils.logger.Logger;
 import tests.integration.shared.TestingData;
 import tests.integration.shared.TestingHelper;
@@ -157,21 +159,19 @@ mApiKey, new Key(userKey),
         pushMessage(TestingData.ESCAPED_KEY_LIST_NOTIFICATION_GZIP);
         l1.await(5, TimeUnit.SECONDS);
 
-        MySegmentEntity e = mySegmentsDao.getByUserKey(userKey);
-        MySegmentEntity e1 = mySegmentsDao.getByUserKey(userKey2);
-
         l1 = new CountDownLatch(1);
         updateTask.setLatch(l1);
         pushMessage(TestingData.SEGMENT_REMOVAL_NOTIFICATION);
         l1.await(5, TimeUnit.SECONDS);
 
-        MySegmentEntity mySegmentEntity = getByKey(userKey, mDb);
-        MySegmentEntity mySegmentEntity2 = getByKey(userKey2, mDb);
-        Assert.assertTrue(mySegmentEntity.getSegmentList().contains("new_segment_added"));
-        Assert.assertFalse(mySegmentEntity.getSegmentList().contains("segment1"));
+        SegmentsChange mySegmentEntity = Json.fromJson(getByKey(userKey, mDb).getSegmentList(), SegmentsChange.class);
+        SegmentsChange mySegmentEntity2 = Json.fromJson(getByKey(userKey2, mDb).getSegmentList(), SegmentsChange.class);
+        Assert.assertTrue(mySegmentEntity.getNames().contains("new_segment_added"));
+        Assert.assertFalse(mySegmentEntity.getNames().contains("segment1"));
 
+        Assert.assertEquals(1, mySegmentEntity2.getSegments().size());
+        Assert.assertEquals("new_segment_added", mySegmentEntity2.getNames().get(0));
         Assert.assertEquals(4, mTelemetryStorage.popUpdatesFromSSE().getMySegments());
-        Assert.assertEquals("new_segment_added", mySegmentEntity2.getSegmentList());
     }
 
     @After
@@ -191,11 +191,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher(int number) {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments/key2")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "key2")) {
                     mMySegmentsSyncLatch2.countDown();
                     mMySegmentsUpdateLatch2.countDown();
-                    return createResponse(200, IntegrationHelper.emptyMySegments());
-                } else if (uri.getPath().contains("/mySegments")) {
+                    return createResponse(200, IntegrationHelper.emptyAllSegments());
+                } else if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitCount++;
                     Logger.i("** My segments hit: " + mMySegmentsHitCount);
                     mMySegmentsSyncLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2Test.java b/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2Test.java
index 374ec0e81..9a13d5921 100644
--- a/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2Test.java
+++ b/src/androidTest/java/tests/integration/streaming/MySegmentsChangeV2Test.java
@@ -150,11 +150,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher(int number) {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments/key2")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/key2")) {
                     mMySegmentsSyncLatch2.countDown();
                     mMySegmentsUpdateLatch2.countDown();
-                    return createResponse(200, IntegrationHelper.emptyMySegments());
-                } else if (uri.getPath().contains("/mySegments")) {
+                    return createResponse(200, IntegrationHelper.emptyAllSegments());
+                } else if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitCount++;
                     Logger.i("** My segments hit: " + mMySegmentsHitCount);
                     mMySegmentsSyncLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/MySegmentsSyncProcessTest.java b/src/androidTest/java/tests/integration/streaming/MySegmentsSyncProcessTest.java
index a6bfecd75..63af0388c 100644
--- a/src/androidTest/java/tests/integration/streaming/MySegmentsSyncProcessTest.java
+++ b/src/androidTest/java/tests/integration/streaming/MySegmentsSyncProcessTest.java
@@ -12,10 +12,12 @@
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 import fake.HttpClientMock;
 import fake.HttpResponseMock;
@@ -28,12 +30,16 @@
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
 import io.split.android.client.api.Key;
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEvent;
 import io.split.android.client.events.SplitEventTask;
 import io.split.android.client.network.HttpMethod;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
 import io.split.android.client.utils.logger.Logger;
+import tests.integration.shared.TestingData;
 import tests.integration.shared.TestingHelper;
 
 public class MySegmentsSyncProcessTest {
@@ -47,16 +53,13 @@ public class MySegmentsSyncProcessTest {
     private CountDownLatch mMySegmentsUpdateLatch;
     private CountDownLatch mMySegmentsPushLatch;
 
-    private final static String MSG_SEGMENT_UPDATE = "push_msg-segment_update.txt";
-    private final static String MSG_SEGMENT_UPDATE_PAYLOAD = "push_msg-segment_update_payload.txt";
-    private final static String MSG_SEGMENT_UPDATE_EMPTY_PAYLOAD = "push_msg-segment_update_empty_payload.txt";
-
     private int mMySegmentsHitCount = 0;
 
     SplitFactory mFactory;
     SplitClient mClient;
 
     SplitRoomDatabase mSplitRoomDatabase;
+    private final AtomicReference<String> mCurrentUpdate = new AtomicReference<>(IntegrationHelper.dummyAllSegments());
 
     @Before
     public void setup() {
@@ -108,18 +111,24 @@ public void mySegmentsUpdate() throws IOException, InterruptedException {
         MySegmentEntity mySegmentEntity = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
 
         mClient.on(SplitEvent.SDK_UPDATE, secondUpdateTask);
-        testMySegmentsPush(MSG_SEGMENT_UPDATE_PAYLOAD);
+        // MSG_SEGMENT_UPDATE_PAYLOAD //only segment1
+        mCurrentUpdate.set(segment1Update());
+        testMySegmentsPush(TestingData.largeSegmentsUnboundedNoCompression("1"));
         secondUpdateLatch.await(5, TimeUnit.SECONDS);
         MySegmentEntity mySegmentEntityPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
 
         mClient.on(SplitEvent.SDK_UPDATE, thirdUpdateTask);
-        testMySegmentsPush(MSG_SEGMENT_UPDATE_EMPTY_PAYLOAD);
+        // MSG_SEGMENT_UPDATE_EMPTY_PAYLOAD // empty
+        mCurrentUpdate.set(IntegrationHelper.emptyAllSegments());
+        testMySegmentsPush(TestingData.largeSegmentsUnboundedNoCompression("1"));
         thirdUpdateLatch.await(5, TimeUnit.SECONDS);
         MySegmentEntity mySegmentEntityEmptyPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
 
-        Assert.assertEquals("segment1,segment2,segment3", mySegmentEntity.getSegmentList());
-        Assert.assertEquals("segment1", mySegmentEntityPayload.getSegmentList());
-        Assert.assertEquals("", mySegmentEntityEmptyPayload.getSegmentList());
+        Assert.assertTrue(mySegmentEntity.getSegmentList().contains("segment1") && mySegmentEntity.getSegmentList().contains("segment2") && mySegmentEntity.getSegmentList().contains("segment3"));
+        String body = mySegmentEntityPayload.getSegmentList();
+        SegmentsChange segmentsChange = Json.fromJson(body, SegmentsChange.class);
+        Assert.assertEquals(Arrays.asList("segment1"), segmentsChange.getNames());
+        Assert.assertEquals("{\"cn\":null,\"k\":[]}", mySegmentEntityEmptyPayload.getSegmentList());
     }
 
     @Test
@@ -177,28 +186,30 @@ public void onPostExecutionView(SplitClient client) {
         MySegmentEntity client1SegmentEntity = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
         MySegmentEntity client2SegmentEntity = mSplitRoomDatabase.mySegmentDao().getByUserKey("key2");
 
-        testMySegmentsPush(MSG_SEGMENT_UPDATE_PAYLOAD);
+        mCurrentUpdate.set(segment1Update());
+        testMySegmentsPush(TestingData.largeSegmentsUnboundedNoCompression("1"));
         client1UpdateLatch.await(5, TimeUnit.SECONDS);
         MySegmentEntity client1SegmentEntityPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
         MySegmentEntity client2SegmentEntityPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey("key2");
 
-        testMySegmentsPush(MSG_SEGMENT_UPDATE_EMPTY_PAYLOAD);
+        mCurrentUpdate.set(IntegrationHelper.emptyAllSegments());
+        testMySegmentsPush(TestingData.largeSegmentsUnboundedNoCompression("1"));
         client1UpdateLatch.await(5, TimeUnit.SECONDS);
         MySegmentEntity client1SegmentEntityEmptyPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey(mUserKey.matchingKey());
         MySegmentEntity client2SegmentEntityEmptyPayload = mSplitRoomDatabase.mySegmentDao().getByUserKey("key2");
 
-        Assert.assertEquals("segment1,segment2,segment3", client1SegmentEntity.getSegmentList());
-        Assert.assertEquals("segment1", client1SegmentEntityPayload.getSegmentList());
-        Assert.assertEquals("", client1SegmentEntityEmptyPayload.getSegmentList());
+        Assert.assertTrue(client1SegmentEntity.getSegmentList().contains("segment1") && client1SegmentEntity.getSegmentList().contains("segment2") && client1SegmentEntity.getSegmentList().contains("segment3"));
+        Assert.assertEquals("{\"cn\":null,\"k\":[{\"n\":\"segment1\"}]}", client1SegmentEntityPayload.getSegmentList());
+        Assert.assertEquals("{\"cn\":null,\"k\":[]}", client1SegmentEntityEmptyPayload.getSegmentList());
 
-        Assert.assertEquals("", client2SegmentEntity.getSegmentList());
-        Assert.assertEquals("", client2SegmentEntityPayload.getSegmentList());
-        Assert.assertEquals("", client2SegmentEntityEmptyPayload.getSegmentList());
+        Assert.assertEquals("{\"cn\":null,\"k\":[]}", client2SegmentEntity.getSegmentList());
+        Assert.assertEquals("{\"cn\":null,\"k\":[]}", client2SegmentEntityPayload.getSegmentList());
+        Assert.assertEquals("{\"cn\":null,\"k\":[]}", client2SegmentEntityEmptyPayload.getSegmentList());
     }
 
     private void testMySegmentsUpdate() throws InterruptedException {
         mMySegmentsUpdateLatch = new CountDownLatch(1);
-        pushMessage(MSG_SEGMENT_UPDATE);
+        pushMessage(TestingData.largeSegmentsUnboundedNoCompression("1"));
         boolean await = mMySegmentsUpdateLatch.await(30, TimeUnit.SECONDS);
         if (!await) {
             Assert.fail("MySegments update not received");
@@ -234,21 +245,21 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
                 try {
                     Thread.sleep(800);
                     if (uri.getPath().contains("auth")) {
-                        return createResponse(200, IntegrationHelper.streamingEnabledV1Token());
-                    } else if (uri.getPath().contains("/mySegments/key1")) {
+                        return createResponse(200, IntegrationHelper.streamingEnabledToken());
+                    } else if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key1")) {
                         mMySegmentsHitCount++;
                         Logger.i("** My segments hit: " + mMySegmentsHitCount);
                         mMySegmentsSyncLatch.countDown();
 
                         if (mMySegmentsHitCount == 3) {
                             mMySegmentsUpdateLatch.countDown();
+
+                            mCurrentUpdate.set(updatedMySegments());
                             Logger.d("updatedMySegments SEGMENTS");
-                            return createResponse(200, updatedMySegments());
                         }
-                        Logger.d("DUMMY SEGMENTS");
-                        return createResponse(200, IntegrationHelper.dummyMySegments());
-                    } else if (uri.getPath().contains("/mySegments/key2")) {
-                        return createResponse(200, IntegrationHelper.emptyMySegments());
+                        return createResponse(200, mCurrentUpdate.get());
+                    } else if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS + "/key2")) {
+                        return createResponse(200, IntegrationHelper.emptyAllSegments());
                     } else if (uri.getPath().contains("/splitChanges")) {
                         Logger.i("** Split Changes hit");
                         String data = IntegrationHelper.emptySplitChanges(-1, 1000);
@@ -282,28 +293,30 @@ private String loadMockedData(String fileName) {
         return fileHelper.loadFileContent(mContext, fileName);
     }
 
-    private void pushMessage(String fileName) {
+    private void pushMessage(String msg) {
         new Thread(() -> {
+            String MSG_SEGMENT_UPDATE_TEMPLATE = "push_msg-largesegment_update.txt";
+            BlockingQueue<String> queue = mStreamingData;
+            String message = loadMockedData(MSG_SEGMENT_UPDATE_TEMPLATE);
+            message = message.replace("$TIMESTAMP$", String.valueOf(System.currentTimeMillis()));
+            message = message.replace(TestingHelper.MSG_DATA_FIELD, msg);
             try {
-                Thread.sleep(500);
-                String message = loadMockedData(fileName);
-                message = message.replace("$TIMESTAMP$", String.valueOf(System.currentTimeMillis()));
                 mStreamingData.put(message + "" + "\n");
                 if (mMySegmentsPushLatch != null) {
                     mMySegmentsPushLatch.countDown();
                 }
                 Logger.d("Pushed message: " + message);
-
             } catch (InterruptedException e) {
-                throw new RuntimeException(e);
+                e.printStackTrace();
             }
         }).start();
     }
 
     private String updatedMySegments() {
-        return "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, " +
-                " { \"id\":\"id1\", \"name\":\"segment2\"}, " +
-                "{ \"id\":\"id3\", \"name\":\"segment3\"}]}";
+        return Json.toJson(new AllSegmentsChange(Arrays.asList("segment1", "segment2", "segment3")));
     }
 
+    private String segment1Update() {
+        return IntegrationHelper.dummySingleSegment("segment1");
+    }
 }
diff --git a/src/androidTest/java/tests/integration/streaming/OccupancyBaseTest.java b/src/androidTest/java/tests/integration/streaming/OccupancyBaseTest.java
index 560166d67..a25b8af8d 100644
--- a/src/androidTest/java/tests/integration/streaming/OccupancyBaseTest.java
+++ b/src/androidTest/java/tests/integration/streaming/OccupancyBaseTest.java
@@ -61,10 +61,10 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsHitCount++;
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
 
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit");
diff --git a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java
index ff4262c82..07ac91545 100644
--- a/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SdkUpdateStreamingTest.java
@@ -23,16 +23,15 @@
 import fake.HttpClientMock;
 import fake.HttpResponseMock;
 import fake.HttpResponseMockDispatcher;
+import fake.HttpStreamResponseMock;
 import helper.DatabaseHelper;
 import helper.FileHelper;
 import helper.IntegrationHelper;
 import helper.SplitEventTaskHelper;
-import helper.TestingHelper;
 import io.split.android.client.SplitClient;
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
 import io.split.android.client.api.Key;
-import io.split.android.client.dtos.MySegment;
 import io.split.android.client.dtos.Partition;
 import io.split.android.client.dtos.Split;
 import io.split.android.client.dtos.SplitChange;
@@ -43,9 +42,8 @@
 import io.split.android.client.storage.db.SplitRoomDatabase;
 import io.split.android.client.utils.Json;
 import io.split.android.client.utils.logger.Logger;
-import fake.HttpStreamResponseMock;
-
-import static java.lang.Thread.sleep;
+import tests.integration.shared.TestingData;
+import tests.integration.shared.TestingHelper;
 
 public class SdkUpdateStreamingTest {
     Context mContext;
@@ -57,7 +55,6 @@ public class SdkUpdateStreamingTest {
 
     final static String MSG_SPLIT_UPDATE = "push_msg-split_update.txt";
     final static String MSG_SPLIT_KILL = "push_msg-split_kill.txt";
-    final static String MSG_SEGMENT_UPDATE_PAYLOAD = "push_msg-segment_update_payload.txt";
 
     CountDownLatch mSplitsPushLatch = null;
     CountDownLatch mMySegmentsPushLatch = null;
@@ -246,11 +243,19 @@ private void testSplitsUpdate() throws IOException, InterruptedException {
         mSplitsPushLatch = null;
     }
 
-    private void testMySegmentsUpdate() throws IOException, InterruptedException {
+    private void testMySegmentsUpdate() throws InterruptedException {
         mMySegmentsPushLatch = new CountDownLatch(1);
-        pushMessage(MSG_SEGMENT_UPDATE_PAYLOAD);
-        mMySegmentsPushLatch.await(5, TimeUnit.SECONDS);
-        mMySegmentsPushLatch = null;
+        String msg = TestingData.segmentsUnboundedNoCompression("1");
+        String MSG_SEGMENT_UPDATE_TEMPLATE = "push_msg-largesegment_update.txt";
+        BlockingQueue<String> queue = mStreamingData;
+        String message = loadMockedData(MSG_SEGMENT_UPDATE_TEMPLATE);
+        message = message.replace("$TIMESTAMP$", String.valueOf(System.currentTimeMillis()));
+        message = message.replace(TestingHelper.MSG_DATA_FIELD, msg);
+        try {
+            queue.put(message + "" + "\n");
+            Logger.d("Pushed message: " + message);
+        } catch (InterruptedException e) {
+        }
     }
 
     @After
@@ -270,9 +275,10 @@ private HttpResponseMockDispatcher createNoChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                Logger.e("PATH IS " + uri.getPath());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("NO CHANGWES MY S");
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("NO CHANGES changes");
                     return createResponse(200, IntegrationHelper.emptySplitChanges(99999, 99999));
@@ -301,8 +307,8 @@ private HttpResponseMockDispatcher createSplitChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     mSplitChangesHitCount++;
                     if(mSplitsPushLatch != null) {
@@ -333,19 +339,21 @@ private HttpResponseMockDispatcher createMySegmentsChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    Logger.d("*** Memberships hit; " + mMySegmentsHitCount);
                     mMySegmentsHitCount++;
                     int hit = mMySegmentsHitCount;
-                    String json = IntegrationHelper.emptyMySegments();
-                    if (mMySegmentsHitCount > 2) {
-                        List<MySegment> mySegments = new ArrayList<>();
+                    String json = IntegrationHelper.emptyAllSegments();
+                    if (mMySegmentsHitCount > 1) {
+                        StringBuilder mySegments = new StringBuilder();
+                        mySegments.append("{\"ms\":{\"k\":[");
+                        List<String> segmentList = new ArrayList<>();
                         for (int i = 0; i <= hit; i++) {
-                            MySegment segment = new MySegment();
-                            segment.id = "s" + i;
-                            segment.name = "segment" + i;
-                            mySegments.add(segment);
+                            segmentList.add("{\"n\":\"" + "s" + i + "\"}");
                         }
-                        json = "{\"mySegments\": " + Json.toJson(mySegments) + "}";
+                        mySegments.append(String.join(",", segmentList));
+                        mySegments.append("],\"cn\":99999}}");
+                        json = mySegments.toString();
                     }
                     if(mMySegmentsPushLatch != null) {
                         mMySegmentsPushLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/SplitChangeNotificationIntegrationTest.java b/src/androidTest/java/tests/integration/streaming/SplitChangeNotificationIntegrationTest.java
index b62385aa2..8b13a657b 100644
--- a/src/androidTest/java/tests/integration/streaming/SplitChangeNotificationIntegrationTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SplitChangeNotificationIntegrationTest.java
@@ -195,9 +195,9 @@ private HttpResponseMockDispatcher createStreamingResponseDispatcher(final Strin
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("mySegments")) {
+                if (uri.getPath().contains(IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitsCountLatch.countDown();
-                    return createResponse(IntegrationHelper.dummyMySegments());
+                    return createResponse(IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     mSplitsHitsCountLatch.countDown();
                     mSplitsHitsCountHit.incrementAndGet();
diff --git a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java
index 5e1b6c6c6..bf7fea927 100644
--- a/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SplitsKillProcessTest.java
@@ -1,5 +1,8 @@
 package tests.integration.streaming;
 
+import static java.lang.Thread.sleep;
+import static helper.IntegrationHelper.ResponseClosure.getSinceFromUri;
+
 import android.content.Context;
 
 import androidx.core.util.Pair;
@@ -41,9 +44,6 @@
 import io.split.android.client.utils.logger.Logger;
 import tests.integration.shared.TestingHelper;
 
-import static java.lang.Thread.sleep;
-import static helper.IntegrationHelper.ResponseClosure.getSinceFromUri;
-
 public class SplitsKillProcessTest {
     Context mContext;
     BlockingQueue<String> mStreamingData;
@@ -83,7 +83,7 @@ public void setup() {
         mSplitRoomDatabase.clearAllTables();
         mUserKey = IntegrationHelper.dummyUserKey();
         mSplitRoomDatabase.generalInfoDao().update(
-                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000 - 30));
+                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() - 30));
         loadSplitChanges();
     }
 
@@ -168,11 +168,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsSyncLatch.countDown();
 
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
 
                     mSplitChangesHitCount++;
@@ -242,6 +242,7 @@ private String getSplitChanges(int hit) {
         split.changeNumber = CHANGE_NUMBER;
         split.killed = true;
         split.defaultTreatment = "off";
+        mSplitChange.since = CHANGE_NUMBER;
         mSplitChange.till = CHANGE_NUMBER;
         return Json.toJson(mSplitChange);
     }
diff --git a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java
index 970eb47e1..536a9c7d4 100644
--- a/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SplitsSyncProcessTest.java
@@ -1,5 +1,8 @@
 package tests.integration.streaming;
 
+import static java.lang.Thread.sleep;
+import static helper.IntegrationHelper.ResponseClosure.getSinceFromUri;
+
 import android.content.Context;
 
 import androidx.core.util.Pair;
@@ -40,9 +43,6 @@
 import io.split.android.client.utils.logger.Logger;
 import tests.integration.shared.TestingHelper;
 
-import static java.lang.Thread.sleep;
-import static helper.IntegrationHelper.ResponseClosure.getSinceFromUri;
-
 public class SplitsSyncProcessTest {
     Context mContext;
     BlockingQueue<String> mStreamingData;
@@ -82,7 +82,7 @@ public void setup() {
         mSplitRoomDatabase = Room.inMemoryDatabaseBuilder(mContext, SplitRoomDatabase.class).build();
         mSplitRoomDatabase.clearAllTables();
         mSplitRoomDatabase.generalInfoDao().update(
-                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000 - 30));
+                new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() - 30));
         mUserKey = IntegrationHelper.dummyUserKey();
         loadSplitChanges();
     }
@@ -158,11 +158,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsSyncLatch.countDown();
 
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
 
                     mSplitChangesHitCount++;
@@ -173,7 +173,7 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
                         mSplitsUpdateLatch.countDown();
                         return createResponse(200, getSplitChanges(mSplitChangesHitCount - 1));
                     }
-                    String data = IntegrationHelper.emptySplitChanges(-1, CHANGE_NUMBER - 1000);
+                    String data = IntegrationHelper.emptySplitChanges(CHANGE_NUMBER - 1000, CHANGE_NUMBER - 1000);
                     return createResponse(200, data);
                 } else if (uri.getPath().contains("/auth")) {
                     Logger.i("** SSE Auth hit");
@@ -220,6 +220,7 @@ private void loadSplitChanges() {
 
     private String getSplitChanges(int hit) {
         mSplitChange.splits.get(0).changeNumber = CHANGE_NUMBER;
+        mSplitChange.since = CHANGE_NUMBER;
         mSplitChange.till = CHANGE_NUMBER;
         return Json.toJson(mSplitChange);
     }
diff --git a/src/androidTest/java/tests/integration/streaming/SseAuthFail4xxTest.java b/src/androidTest/java/tests/integration/streaming/SseAuthFail4xxTest.java
index 0eefda569..de167a6fa 100644
--- a/src/androidTest/java/tests/integration/streaming/SseAuthFail4xxTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SseAuthFail4xxTest.java
@@ -102,11 +102,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsHitsCountLatch.countDown();
                     mySegmentsHitsCountHit++;
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit");
                     mSplitsHitsCountLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/SseAuthFail5xxTest.java b/src/androidTest/java/tests/integration/streaming/SseAuthFail5xxTest.java
index 44a7585ab..fa567ec2f 100644
--- a/src/androidTest/java/tests/integration/streaming/SseAuthFail5xxTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SseAuthFail5xxTest.java
@@ -132,14 +132,14 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher(){
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit: " + mMySegmentsHitsCountHit);
                     if(!mIsStreamingConnected) {
                         mMySegmentsHitsCountHit++;
                     } else {
                         mMySegmentsHitsCountHitAfterSseConn++;
                     }
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit: " + mSplitsHitsCountHit);
                     if(!mIsStreamingConnected) {
diff --git a/src/androidTest/java/tests/integration/streaming/SseConnectionExpiredTokenTest.java b/src/androidTest/java/tests/integration/streaming/SseConnectionExpiredTokenTest.java
index 2cbf236c1..8b826c408 100644
--- a/src/androidTest/java/tests/integration/streaming/SseConnectionExpiredTokenTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SseConnectionExpiredTokenTest.java
@@ -114,8 +114,8 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     String data = IntegrationHelper.emptySplitChanges(-1, 1000);
                     return createResponse(200, data);
diff --git a/src/androidTest/java/tests/integration/streaming/SseConnectionFail5xxTest.java b/src/androidTest/java/tests/integration/streaming/SseConnectionFail5xxTest.java
index 32bb2f094..8671d41b2 100644
--- a/src/androidTest/java/tests/integration/streaming/SseConnectionFail5xxTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SseConnectionFail5xxTest.java
@@ -1,5 +1,7 @@
 package tests.integration.streaming;
 
+import static java.lang.Thread.sleep;
+
 import android.content.Context;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -18,8 +20,8 @@
 import fake.HttpClientMock;
 import fake.HttpResponseMock;
 import fake.HttpResponseMockDispatcher;
-import helper.DatabaseHelper;
 import fake.HttpStreamResponseMock;
+import helper.DatabaseHelper;
 import helper.IntegrationHelper;
 import helper.SplitEventTaskHelper;
 import io.split.android.client.SplitClient;
@@ -30,8 +32,6 @@
 import io.split.android.client.utils.logger.Logger;
 import tests.integration.shared.TestingHelper;
 
-import static java.lang.Thread.sleep;
-
 public class SseConnectionFail5xxTest {
     Context mContext;
     BlockingQueue<String> mStreamingData;
@@ -130,14 +130,14 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit: " + mMySegmentsHitsCountHit);
                     if (!mIsStreamingConnected) {
                         mMySegmentsHitsCountHit++;
                     } else {
                         mMySegmentsHitsCountHitAfterSseConn++;
                     }
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit: " + mSplitsHitsCountHit);
                     if (!mIsStreamingConnected) {
diff --git a/src/androidTest/java/tests/integration/streaming/StreamingDisabledInConfigTest.java b/src/androidTest/java/tests/integration/streaming/StreamingDisabledInConfigTest.java
index 637e7a70c..a4183413f 100644
--- a/src/androidTest/java/tests/integration/streaming/StreamingDisabledInConfigTest.java
+++ b/src/androidTest/java/tests/integration/streaming/StreamingDisabledInConfigTest.java
@@ -106,11 +106,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher(){
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsHitsCountLatch.countDown();
                     mySegmentsHitsCountHit++;
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit");
                     mSplitsHitsCountLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/StreamingDisabledTest.java b/src/androidTest/java/tests/integration/streaming/StreamingDisabledTest.java
index c66830c97..7fed4ee16 100644
--- a/src/androidTest/java/tests/integration/streaming/StreamingDisabledTest.java
+++ b/src/androidTest/java/tests/integration/streaming/StreamingDisabledTest.java
@@ -158,11 +158,11 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher(){
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
                     mMySegmentsHitsCountLatch.countDown();
                     mySegmentsHitsCountHit++;
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit");
                     mSplitsHitsCountLatch.countDown();
diff --git a/src/androidTest/java/tests/integration/streaming/StreamingInitializationTest.java b/src/androidTest/java/tests/integration/streaming/StreamingInitializationTest.java
index 77a831041..74f88ff47 100644
--- a/src/androidTest/java/tests/integration/streaming/StreamingInitializationTest.java
+++ b/src/androidTest/java/tests/integration/streaming/StreamingInitializationTest.java
@@ -92,9 +92,9 @@ private HttpResponseMockDispatcher createBasicResponseDispatcher() {
         return new HttpResponseMockDispatcher(){
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("** My segments hit");
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     Logger.i("** Split Changes hit");
                     String data = IntegrationHelper.emptySplitChanges(-1, 1000);
diff --git a/src/androidTest/java/tests/integration/streaming/SyncGuardianIntegrationTest.java b/src/androidTest/java/tests/integration/streaming/SyncGuardianIntegrationTest.java
index 41f9109a9..fd51fd8a6 100644
--- a/src/androidTest/java/tests/integration/streaming/SyncGuardianIntegrationTest.java
+++ b/src/androidTest/java/tests/integration/streaming/SyncGuardianIntegrationTest.java
@@ -290,9 +290,9 @@ private HttpResponseMockDispatcher createStreamingResponseDispatcher(final Strin
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("mySegments")) {
+                if (uri.getPath().contains(IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitsCountLatch.countDown();
-                    return createResponse(IntegrationHelper.dummyMySegments());
+                    return createResponse(IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     mSplitsHitsCountLatch.countDown();
                     mSplitsHitsCountHit.incrementAndGet();
diff --git a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java
index 3415e6c14..50d747591 100644
--- a/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java
+++ b/src/androidTest/java/tests/integration/telemetry/TelemetryIntegrationTest.java
@@ -114,12 +114,12 @@ public void evaluationByFlagsInfoIsInPayload() throws InterruptedException {
             @Override
             public MockResponse dispatch(RecordedRequest request) {
                 String path = request.getPath();
-                if (path.contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (path.contains("/splitChanges")) {
                     long changeNumber = -1;
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber + 1000) + "}");
+                            .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}");
                 } else if (path.contains("/events/bulk")) {
                     return new MockResponse().setResponseCode(200);
                 } else if (path.contains("metrics/usage")) {
@@ -214,12 +214,12 @@ public void recordAuthRejections() throws InterruptedException {
             @Override
             public MockResponse dispatch(RecordedRequest request) {
                 String path = request.getPath();
-                if (path.contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (path.contains("/splitChanges")) {
                     long changeNumber = -1;
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber + 1000) + "}");
+                            .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}");
                 } else if (path.contains("/events/bulk")) {
                     return new MockResponse().setResponseCode(200);
                 } else if (path.contains("metrics/usage")) {
@@ -267,12 +267,12 @@ public void flagSetsAreIncludedInPayload() throws InterruptedException {
             @Override
             public MockResponse dispatch(RecordedRequest request) {
                 String path = request.getPath();
-                if (path.contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (path.contains("/splitChanges")) {
                     long changeNumber = -1;
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber + 1000) + "}");
+                            .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}");
                 } else if (path.contains("/events/bulk")) {
                     return new MockResponse().setResponseCode(200);
                 } else if (path.contains("metrics/usage")) {
@@ -367,7 +367,7 @@ private void insertSplitsFromFileIntoDB() {
 
         testDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.DATBASE_MIGRATION_STATUS, GeneralInfoEntity.DATBASE_MIGRATION_STATUS_DONE));
         testDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.CHANGE_NUMBER_INFO, 1));
-        testDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis() / 1000));
+        testDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis()));
 
         testDatabase.splitDao().insert(entities);
     }
@@ -389,12 +389,12 @@ private void setupServer() {
             @Override
             public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                 String path = request.getPath();
-                if (path.contains("/mySegments")) {
-                    return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id1\", \"name\":\"segment2\"}]}");
+                if (path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
                 } else if (path.contains("/splitChanges")) {
                     long changeNumber = -1;
                     return new MockResponse().setResponseCode(200)
-                            .setBody("{\"splits\":[], \"since\":" + changeNumber + ", \"till\":" + (changeNumber + 1000) + "}");
+                            .setBody("{\"splits\":[], \"since\":" + (changeNumber + 1000) + ", \"till\":" + (changeNumber + 1000) + "}");
                 } else if (path.contains("/events/bulk")) {
                     return new MockResponse().setResponseCode(200);
                 } else if (path.contains("/metrics")) {
diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt
index 4c0d9eb02..c86df4698 100644
--- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt
+++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeDebugTest.kt
@@ -226,8 +226,8 @@ class UserConsentModeDebugTest {
 
             override fun getResponse(uri: URI, method: HttpMethod, body: String): HttpResponseMock {
                 println(uri.path)
-                return if (uri.path.contains("/mySegments")) {
-                    HttpResponseMock(200, IntegrationHelper.emptyMySegments())
+                return if (uri.path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    HttpResponseMock(200, IntegrationHelper.emptyAllSegments())
                 } else if (uri.path.contains("/splitChanges")) {
                     if (mChangeHit == 0) {
                         mChangeHit+=1
diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt
index 691b21369..eac6083a6 100644
--- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt
+++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeNoneTest.kt
@@ -230,8 +230,8 @@ class UserConsentModeNoneTest {
 
             override fun getResponse(uri: URI, method: HttpMethod, body: String): HttpResponseMock {
                 println(uri.path)
-                return if (uri.path.contains("/mySegments")) {
-                    HttpResponseMock(200, IntegrationHelper.emptyMySegments())
+                return if (uri.path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    HttpResponseMock(200, IntegrationHelper.emptyAllSegments())
                 } else if (uri.path.contains("/splitChanges")) {
                     if (mChangeHit == 0) {
                         mChangeHit += 1
diff --git a/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt b/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt
index bc864fa4d..686b3436e 100644
--- a/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt
+++ b/src/androidTest/java/tests/integration/userconsent/UserConsentModeOptimizedTest.kt
@@ -238,8 +238,8 @@ class UserConsentModeOptimizedTest {
 
             override fun getResponse(uri: URI, method: HttpMethod, body: String): HttpResponseMock {
                 println(uri.path)
-                return if (uri.path.contains("/mySegments")) {
-                    HttpResponseMock(200, IntegrationHelper.emptyMySegments())
+                return if (uri.path.contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    HttpResponseMock(200, IntegrationHelper.emptyAllSegments())
                 } else if (uri.path.contains("/splitChanges")) {
                     if (mChangeHit == 0) {
                         mChangeHit+=1
diff --git a/src/androidTest/java/tests/service/ImpressionsRequestTest.java b/src/androidTest/java/tests/service/ImpressionsRequestTest.java
index 8b7df88ae..d9124428a 100644
--- a/src/androidTest/java/tests/service/ImpressionsRequestTest.java
+++ b/src/androidTest/java/tests/service/ImpressionsRequestTest.java
@@ -110,7 +110,7 @@ private Map<String, IntegrationHelper.ResponseClosure> getMockResponses() {
         });
 
         responses.put("splitChanges", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptySplitChanges(9999, 9999)));
-        responses.put("mySegments/key1", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "key1", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments()));
         responses.put("v2/auth", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.streamingEnabledToken()));
 
         return responses;
diff --git a/src/androidTest/java/tests/service/SdkUpdatePollingTest.java b/src/androidTest/java/tests/service/SdkUpdatePollingTest.java
index 087cda511..ee20e6bd6 100644
--- a/src/androidTest/java/tests/service/SdkUpdatePollingTest.java
+++ b/src/androidTest/java/tests/service/SdkUpdatePollingTest.java
@@ -32,7 +32,7 @@
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFactory;
 import io.split.android.client.api.Key;
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.dtos.Partition;
 import io.split.android.client.dtos.Split;
 import io.split.android.client.dtos.SplitChange;
@@ -227,9 +227,9 @@ private HttpResponseMockDispatcher createNoChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     Logger.i("NO CHANGWES MY S");
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     String json = IntegrationHelper.emptySplitChanges(99999, 99999);
                     Logger.i("NO CHANGES changes: " + json);
@@ -259,8 +259,8 @@ private HttpResponseMockDispatcher createSplitChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
-                    return createResponse(200, IntegrationHelper.dummyMySegments());
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
+                    return createResponse(200, IntegrationHelper.dummyAllSegments());
                 } else if (uri.getPath().contains("/splitChanges")) {
                     mSplitChangesHitCount++;
                     String json = getChanges(mSplitChangesHitCount);
@@ -290,17 +290,14 @@ private HttpResponseMockDispatcher createMySegmentsChangesDispatcher() {
         return new HttpResponseMockDispatcher() {
             @Override
             public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
-                if (uri.getPath().contains("/mySegments")) {
+                if (uri.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
                     mMySegmentsHitCount++;
                     int hit = mMySegmentsHitCount;
-                    List<MySegment> mySegments = new ArrayList<>();
+                    List<String> mySegments = new ArrayList<>();
                     for (int i = 0; i <= hit; i++) {
-                        MySegment segment = new MySegment();
-                        segment.id = "s" + i;
-                        segment.name = "segment" + i;
-                        mySegments.add(segment);
+                        mySegments.add("segment" + i);
                     }
-                    String json = "{\"mySegments\": " + Json.toJson(mySegments) + "}";
+                    String json = Json.toJson(new AllSegmentsChange(mySegments));
                     return createResponse(200, json);
                 } else if (uri.getPath().contains("/splitChanges")) {
 
diff --git a/src/androidTest/java/tests/service/SseJwtTokenParserTest.java b/src/androidTest/java/tests/service/SseJwtTokenParserTest.java
index 80e3922dc..ed9bef446 100644
--- a/src/androidTest/java/tests/service/SseJwtTokenParserTest.java
+++ b/src/androidTest/java/tests/service/SseJwtTokenParserTest.java
@@ -165,4 +165,34 @@ public void nullToken() {
         Assert.assertNotNull(thrownEx);
     }
 
+    @Test
+    public void testLargeSegmentsToken() throws InvalidJwtTokenException {
+        String jwtToken = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ." +
+                "ewogICJ4LWFibHktY2FwYWJpbGl0eSI6ICJ7XCJNek01TmpjME9EY3lOZz09X01URXhNemd3Tm" +
+                "pneF9NVGN3TlRJMk1UTTBNZz09X215U2VnbWVudHNcIjpbXCJzdWJzY3JpYmVcIl0sXCJNek01" +
+                "TmpjME9EY3lOZz09X01URXhNemd3TmpneF9NVGN3TlRJMk1UTTBNZz09X215bGFyZ2VzZWdtZW" +
+                "50c1wiOltcInN1YnNjcmliZVwiXSxcIk16TTVOamMwT0RjeU5nPT1fTVRFeE16Z3dOamd4X3Nw" +
+                "bGl0c1wiOltcInN1YnNjcmliZVwiXSxcImNvbnRyb2xfcHJpXCI6W1wic3Vic2NyaWJlXCIsXC" +
+                "JjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl0sXCJjb250cm9sX3NlY1wiOltcInN1YnNj" +
+                "cmliZVwiLFwiY2hhbm5lbC1tZXRhZGF0YTpwdWJsaXNoZXJzXCJdfSIsCiAgIngtYWJseS1jbG" +
+                "llbnRJZCI6ICJjbGllbnRJZCIsCiAgImV4cCI6IDIyMDg5ODg4MDAsCiAgImlhdCI6IDE1ODc0M" +
+                "DQzODgKfQ==.LcKAXnkr-CiYVxZ7l38w9i98Y-BMAv9JlGP2i92nVQY";
+
+        SseJwtParser parser = new SseJwtParser();
+        SseJwtToken parsedToken = parser.parse(jwtToken);
+        List<String> channels = parsedToken.getChannels();
+
+        Assert.assertEquals(2208988800L, parsedToken.getExpirationTime());
+        Assert.assertEquals(jwtToken, parsedToken.getRawJwt());
+        Assert.assertEquals("MzM5Njc0ODcyNg==_MTExMzgwNjgx_MTcwNTI2MTM0Mg==_mySegments",
+                channels.get(0));
+        Assert.assertEquals("MzM5Njc0ODcyNg==_MTExMzgwNjgx_MTcwNTI2MTM0Mg==_mylargesegments",
+                channels.get(1));
+        Assert.assertEquals("MzM5Njc0ODcyNg==_MTExMzgwNjgx_splits",
+                channels.get(2));
+        Assert.assertEquals("[?occupancy=metrics.publishers]control_pri",
+                channels.get(3));
+        Assert.assertEquals("[?occupancy=metrics.publishers]control_sec",
+                channels.get(4));
+    }
 }
diff --git a/src/androidTest/java/tests/service/UniqueKeysIntegrationTest.java b/src/androidTest/java/tests/service/UniqueKeysIntegrationTest.java
index 8b807b378..244bf685c 100644
--- a/src/androidTest/java/tests/service/UniqueKeysIntegrationTest.java
+++ b/src/androidTest/java/tests/service/UniqueKeysIntegrationTest.java
@@ -9,7 +9,6 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -248,14 +247,14 @@ private Map<String, IntegrationHelper.ResponseClosure> getMockResponses() {
         });
 
         responses.put("splitChanges", (uri, httpMethod, body) -> {
-            String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"}]}],\"since\":-1,\"till\":1648733409158}";
+            String splitChange = "{\"splits\":[{\"trafficTypeName\":\"account\",\"name\":\"android_test_2\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1955610140,\"seed\":-633015570,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733409158,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SPLIT_TREATMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":{\"split\":\"android_test_3\",\"treatments\":[\"on\"]},\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in split android_test_3 treatment [on]\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"account\",\"name\":\"android_test_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-397942789,\"seed\":1852089605,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1648733496087,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"account\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"android_test\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"in segment android_test\"}]}],\"since\":1648733409158,\"till\":1648733409158}";
 
             return new HttpResponseMock(200, splitChange);
         });
 
         IntegrationHelper.ResponseClosure mySegmentsResponse = (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.emptyMySegments());
-        responses.put("mySegments/key1", mySegmentsResponse);
-        responses.put("mySegments/key2", mySegmentsResponse);
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/key1", mySegmentsResponse);
+        responses.put(IntegrationHelper.ServicePath.MEMBERSHIPS + "/" + "/key2", mySegmentsResponse);
 
         responses.put("v2/auth", (uri, httpMethod, body) -> new HttpResponseMock(200, IntegrationHelper.streamingEnabledToken()));
         return responses;
diff --git a/src/androidTest/java/tests/storage/LoadMySegmentsTaskTest.java b/src/androidTest/java/tests/storage/LoadMySegmentsTaskTest.java
index 509c6c5dd..84897a005 100644
--- a/src/androidTest/java/tests/storage/LoadMySegmentsTaskTest.java
+++ b/src/androidTest/java/tests/storage/LoadMySegmentsTaskTest.java
@@ -16,7 +16,9 @@
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
 import io.split.android.client.service.mysegments.LoadMySegmentsTask;
+import io.split.android.client.service.mysegments.LoadMySegmentsTaskConfig;
 import io.split.android.client.storage.cipher.SplitCipherFactory;
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
@@ -29,9 +31,12 @@ public class LoadMySegmentsTaskTest {
     SplitRoomDatabase mRoomDb;
     Context mContext;
     PersistentMySegmentsStorage mPersistentMySegmentsStorage;
+    PersistentMySegmentsStorage mPersistentMyLargeSegmentsStorage;
     MySegmentsStorage mMySegmentsStorage;
+    MySegmentsStorage mMyLargeSegmentsStorage;
     final String mUserKey = "userkey-1";
     private MySegmentsStorageContainer mMySegmentsStorageContainer;
+    private MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
 
     @Before
     public void setUp() {
@@ -51,22 +56,43 @@ public void setUp() {
         entity.setUpdatedAt(System.currentTimeMillis() / 1000);
         mRoomDb.mySegmentDao().update(entity);
 
-        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(mRoomDb, SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false));
+        MyLargeSegmentEntity largeEntity = new MyLargeSegmentEntity();
+        largeEntity.setUserKey(mUserKey);
+        largeEntity.setSegmentList("ls1,ls2,ls3");
+        largeEntity.setUpdatedAt(System.currentTimeMillis() / 1000);
+        mRoomDb.myLargeSegmentDao().update(largeEntity);
+
+        largeEntity = new MyLargeSegmentEntity();
+        largeEntity.setUserKey("userkey-2");
+        largeEntity.setSegmentList("ls10,ls20");
+        largeEntity.setUpdatedAt(System.currentTimeMillis() / 1000);
+        mRoomDb.myLargeSegmentDao().update(largeEntity);
+
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false), mRoomDb.mySegmentDao(), MySegmentEntity.creator());
+        mPersistentMyLargeSegmentsStorage = new SqLitePersistentMySegmentsStorage(SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false), mRoomDb.myLargeSegmentDao(), MyLargeSegmentEntity.creator());
         mMySegmentsStorageContainer = new MySegmentsStorageContainerImpl(mPersistentMySegmentsStorage);
+        mMyLargeSegmentsStorageContainer = new MySegmentsStorageContainerImpl(mPersistentMyLargeSegmentsStorage);
         mMySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
+        mMyLargeSegmentsStorage = mMyLargeSegmentsStorageContainer.getStorageForKey(mUserKey);
     }
 
     @Test
     public void execute() {
 
-        SplitTask task = new LoadMySegmentsTask(mMySegmentsStorage);
+        SplitTask task = new LoadMySegmentsTask(mMySegmentsStorage, mMyLargeSegmentsStorage, LoadMySegmentsTaskConfig.get());
         SplitTaskExecutionInfo result = task.execute();
-        Set<String> snapshot = new HashSet(mMySegmentsStorage.getAll());
+        Set<String> snapshot = new HashSet<>(mMySegmentsStorage.getAll());
+        Set<String> largeSnapshot = new HashSet<>(mMyLargeSegmentsStorage.getAll());
 
         Assert.assertEquals(3, snapshot.size());
         Assert.assertTrue(snapshot.contains("s1"));
         Assert.assertTrue(snapshot.contains("s2"));
         Assert.assertTrue(snapshot.contains("s3"));
+
+        Assert.assertEquals(3, largeSnapshot.size());
+        Assert.assertTrue(largeSnapshot.contains("ls1"));
+        Assert.assertTrue(largeSnapshot.contains("ls2"));
+        Assert.assertTrue(largeSnapshot.contains("ls3"));
         Assert.assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus());
     }
 }
diff --git a/src/androidTest/java/tests/storage/MySegmentsStorageTest.java b/src/androidTest/java/tests/storage/MySegmentsStorageTest.java
index ecbd9764f..4cea487ad 100644
--- a/src/androidTest/java/tests/storage/MySegmentsStorageTest.java
+++ b/src/androidTest/java/tests/storage/MySegmentsStorageTest.java
@@ -10,15 +10,15 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import helper.DatabaseHelper;
+import helper.IntegrationHelper;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.storage.cipher.SplitCipherFactory;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
@@ -45,18 +45,18 @@ public void setUp() {
 
         MySegmentEntity entity = new MySegmentEntity();
         entity.setUserKey(mUserKey);
-        entity.setSegmentList("s1,s2,s3");
+        entity.setSegmentList("{\"k\":[{\"n\":\"s1\"},{\"n\":\"s2\"},{\"n\":\"s3\"}],\"cn\":-1}");
         entity.setUpdatedAt(System.currentTimeMillis() / 1000);
         mRoomDb.mySegmentDao().update(entity);
 
         entity = new MySegmentEntity();
         entity.setUserKey("userkey-2");
-        entity.setSegmentList("s10,s20");
+        entity.setSegmentList("{\"k\":[{\"n\":\"s10\"},{\"n\":\"s20\"}],\"cn\":-1}");
         entity.setUpdatedAt(System.currentTimeMillis() / 1000);
         mRoomDb.mySegmentDao().update(entity);
 
-        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(mRoomDb,
-                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false));
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false), mRoomDb.mySegmentDao(), MySegmentEntity.creator());
         mMySegmentsStorageContainer = new MySegmentsStorageContainerImpl(mPersistentMySegmentsStorage);
         mMySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
     }
@@ -69,7 +69,7 @@ public void noLocalLoaded() {
     }
 
     @Test
-    public void getMySegments() {
+    public void getSegments() {
         mMySegmentsStorage.loadLocal();
         Set<String> snapshot = new HashSet(mMySegmentsStorage.getAll());
 
@@ -82,30 +82,34 @@ public void getMySegments() {
     @Test
     public void updateSegments() {
         mMySegmentsStorage.loadLocal();
-        mMySegmentsStorage.set(Arrays.asList("a1", "a2", "a3", "a4"));
+        mMySegmentsStorage.set(SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), 2222222L));
         MySegmentsStorage mySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
         mySegmentsStorage.loadLocal();
 
         Set<String> snapshot = new HashSet<>(mMySegmentsStorage.getAll());
+        long till = mMySegmentsStorage.getChangeNumber();
         Set<String> newSnapshot = new HashSet<>(mySegmentsStorage.getAll());
+        long newTill = mySegmentsStorage.getChangeNumber();
 
         assertEquals(4, snapshot.size());
         assertTrue(snapshot.contains("a1"));
         assertTrue(snapshot.contains("a2"));
         assertTrue(snapshot.contains("a3"));
         assertTrue(snapshot.contains("a4"));
+        assertEquals(2222222, till);
 
         assertEquals(4, newSnapshot.size());
         assertTrue(newSnapshot.contains("a1"));
         assertTrue(newSnapshot.contains("a2"));
         assertTrue(newSnapshot.contains("a3"));
         assertTrue(newSnapshot.contains("a4"));
+        assertEquals(2222222, newTill);
     }
 
     @Test
     public void updateEmptyMySegment() {
         mMySegmentsStorage.loadLocal();
-        mMySegmentsStorage.set(new ArrayList<>());
+        mMySegmentsStorage.set(SegmentsChange.create(Collections.emptySet(), 11124442L));
 
         MySegmentsStorage mySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
         mySegmentsStorage.loadLocal();
@@ -115,12 +119,13 @@ public void updateEmptyMySegment() {
 
         assertEquals(0, snapshot.size());
         assertEquals(0, newSnapshot.size());
+        assertEquals(11124442, mySegmentsStorage.getChangeNumber());
     }
 
     @Test
     public void addNullMySegmentsList() {
 
-        mPersistentMySegmentsStorage.set(mUserKey, null);
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(null, -1)); // till will be ignored
         mMySegmentsStorage.loadLocal();
         MySegmentsStorage mySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
         mySegmentsStorage.loadLocal();
@@ -130,6 +135,7 @@ public void addNullMySegmentsList() {
 
         assertEquals(3, snapshot.size());
         assertEquals(3, newSnapshot.size());
+        assertEquals(-1, mySegmentsStorage.getChangeNumber());
     }
 
     @Test
@@ -144,16 +150,17 @@ public void clear() {
         Set<String> snapshot = new HashSet<>(mMySegmentsStorage.getAll());
 
         assertEquals(0, snapshot.size());
+        assertEquals(-1, mySegmentsStorage.getChangeNumber());
     }
 
     @Test
     public void originalValuesCanBeRetrievedWhenStorageIsEncrypted() {
-        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(mRoomDb,
-                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", true));
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", true), mRoomDb.mySegmentDao(), MySegmentEntity.creator());
         mMySegmentsStorageContainer = new MySegmentsStorageContainerImpl(mPersistentMySegmentsStorage);
         mMySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
 
-        mMySegmentsStorage.set(Arrays.asList("a1", "a2", "a3", "a4"));
+        mMySegmentsStorage.set(SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), 999820));
         MySegmentsStorage mySegmentsStorage = mMySegmentsStorageContainer.getStorageForKey(mUserKey);
         mySegmentsStorage.loadLocal();
 
@@ -163,6 +170,7 @@ public void originalValuesCanBeRetrievedWhenStorageIsEncrypted() {
         assertTrue(all.contains("a3"));
         assertTrue(all.contains("a4"));
         assertEquals(4, all.size());
+        assertEquals(999820, mySegmentsStorage.getChangeNumber());
     }
 
     @Test
@@ -170,11 +178,11 @@ public void updateToStorageConcurrency() throws InterruptedException {
         mMySegmentsStorage.loadLocal();
         CountDownLatch latch = new CountDownLatch(2);
 
-        new Thread(new Runnable() {
+        Thread thread1 = new Thread(new Runnable() {
             @Override
             public void run() {
                 for (int j = 1000; j < 1200; j += 10) {
-                    List<String> segments = new ArrayList<>();
+                    Set<String> segments = new HashSet<>();
 
                     for (int i = 0; i < 10; i++) {
                         segments.add("segment_" + j + "_" + i);
@@ -183,34 +191,40 @@ public void run() {
                         Thread.sleep(80);
                     } catch (InterruptedException e) {
                     }
-                    mMySegmentsStorage.set(segments);
+                    mMySegmentsStorage.set(SegmentsChange.create(segments, 112421 + j));
                 }
                 latch.countDown();
             }
-        }).start();
+        });
 
-        new Thread(new Runnable() {
+        Thread thread2 = new Thread(new Runnable() {
             @Override
             public void run() {
 
                 for (int j = 0; j < 200; j += 10) {
-                    List<String> segments = new ArrayList<>();
+                    Set<String> segments = new HashSet<>();
 
                     for (int i = 0; i < 10; i++) {
                         segments.add("segment_" + j + "_" + i);
                     }
                     try {
                         Thread.sleep(80);
-                        mMySegmentsStorage.set(segments);
+                        mMySegmentsStorage.set(SegmentsChange.create(segments, 112421 + j));
                         Thread.sleep(80);
                     } catch (InterruptedException e) {
                     }
                 }
                 latch.countDown();
             }
-        }).start();
+        });
+
+        thread1.start();
+        thread2.start();
+        thread1.join();
+        thread2.join();
+
         latch.await(40, TimeUnit.SECONDS);
-        Set<String> l = mMySegmentsStorage.getAll();
         assertEquals(10, mMySegmentsStorage.getAll().size());
+        assertEquals(112421 + 190, mMySegmentsStorage.getChangeNumber());
     }
 }
diff --git a/src/androidTest/java/tests/storage/PersistentMyLargeSegmentStorageTest.java b/src/androidTest/java/tests/storage/PersistentMyLargeSegmentStorageTest.java
new file mode 100644
index 000000000..05a3024f6
--- /dev/null
+++ b/src/androidTest/java/tests/storage/PersistentMyLargeSegmentStorageTest.java
@@ -0,0 +1,116 @@
+package tests.storage;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import helper.DatabaseHelper;
+import helper.IntegrationHelper;
+import io.split.android.client.dtos.SegmentsChange;
+import io.split.android.client.storage.cipher.SplitCipherFactory;
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.storage.mysegments.PersistentMySegmentsStorage;
+import io.split.android.client.storage.mysegments.SqLitePersistentMySegmentsStorage;
+
+public class PersistentMyLargeSegmentStorageTest {
+    SplitRoomDatabase mRoomDb;
+    Context mContext;
+    PersistentMySegmentsStorage mPersistentMySegmentsStorage;
+    private final String mUserKey = "userkey-1";
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mRoomDb = DatabaseHelper.getTestDatabase(mContext);
+        mRoomDb.clearAllTables();
+
+        MyLargeSegmentEntity entity = new MyLargeSegmentEntity();
+        entity.setUserKey(mUserKey);
+        entity.setSegmentList("{\"k\":[{\"n\":\"s1\"},{\"n\":\"s2\"},{\"n\":\"s3\"}],\"cn\":null}");
+        entity.setUpdatedAt(System.currentTimeMillis() / 1000);
+        mRoomDb.myLargeSegmentDao().update(entity);
+
+        entity = new MyLargeSegmentEntity();
+        String mUserKey2 = "userkey-2";
+        entity.setUserKey(mUserKey2);
+        entity.setSegmentList("{\"k\":[{\"n\":\"s10\"},{\"n\":\"s20\"}],\"cn\":-1}");
+        entity.setUpdatedAt(System.currentTimeMillis() / 1000);
+        mRoomDb.myLargeSegmentDao().update(entity);
+
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false), mRoomDb.myLargeSegmentDao(), MyLargeSegmentEntity.creator());
+    }
+
+    @Test
+    public void getMySegments() {
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        Assert.assertEquals(3, snapshot.getNames().size());
+        Assert.assertTrue(snapshot.getNames().contains("s1"));
+        Assert.assertTrue(snapshot.getNames().contains("s2"));
+        Assert.assertTrue(snapshot.getNames().contains("s3"));
+        Assert.assertNull(snapshot.getChangeNumber());
+    }
+
+    @Test
+    public void updateSegments() {
+
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), 2002012));
+
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        Assert.assertEquals(4, snapshot.getNames().size());
+        Assert.assertTrue(snapshot.getNames().contains("a1"));
+        Assert.assertTrue(snapshot.getNames().contains("a2"));
+        Assert.assertTrue(snapshot.getNames().contains("a3"));
+        Assert.assertTrue(snapshot.getNames().contains("a4"));
+        Assert.assertEquals(2002012, snapshot.getChangeNumber().longValue());
+    }
+
+    @Test
+    public void updateSegmentsEncrypted() {
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", true), mRoomDb.myLargeSegmentDao(), MyLargeSegmentEntity.creator());
+
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), 2002012));
+
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        List<String> mySegments = snapshot.getNames();
+        Assert.assertEquals(4, mySegments.size());
+        Assert.assertTrue(mySegments.contains("a1"));
+        Assert.assertTrue(mySegments.contains("a2"));
+        Assert.assertTrue(mySegments.contains("a3"));
+        Assert.assertTrue(mySegments.contains("a4"));
+        Assert.assertEquals(2002012, snapshot.getChangeNumber().longValue());
+    }
+
+    @Test
+    public void updateEmptyMySegment() {
+
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(Collections.emptySet(), 2002012));
+
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        Assert.assertEquals(0, snapshot.getNames().size());
+        Assert.assertEquals(2002012, snapshot.getChangeNumber().longValue());
+    }
+
+    @Test
+    public void addNullMySegmentsList() {
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(null, 2002012));
+
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        Assert.assertEquals(3, snapshot.getNames().size());
+        Assert.assertNull(snapshot.getChangeNumber());
+    }
+}
diff --git a/src/androidTest/java/tests/storage/PersistentMySegmentStorageTest.java b/src/androidTest/java/tests/storage/PersistentMySegmentStorageTest.java
index f620f8022..8a7bd7e64 100644
--- a/src/androidTest/java/tests/storage/PersistentMySegmentStorageTest.java
+++ b/src/androidTest/java/tests/storage/PersistentMySegmentStorageTest.java
@@ -8,14 +8,11 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 import helper.DatabaseHelper;
+import helper.IntegrationHelper;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.storage.cipher.SplitCipherFactory;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
@@ -37,77 +34,85 @@ public void setUp() {
 
         MySegmentEntity entity = new MySegmentEntity();
         entity.setUserKey(mUserKey);
-        entity.setSegmentList("s1,s2,s3");
+        entity.setSegmentList("{\"k\":[{\"n\":\"s1\"},{\"n\":\"s2\"},{\"n\":\"s3\"}],\"cn\":null}");
         entity.setUpdatedAt(System.currentTimeMillis() / 1000);
         mRoomDb.mySegmentDao().update(entity);
 
         entity = new MySegmentEntity();
         String mUserKey2 = "userkey-2";
         entity.setUserKey(mUserKey2);
-        entity.setSegmentList("s10,s20");
+        entity.setSegmentList("{\"k\":[{\"n\":\"s10\"},{\"n\":\"s20\"}],\"cn\":-1}");
         entity.setUpdatedAt(System.currentTimeMillis() / 1000);
         mRoomDb.mySegmentDao().update(entity);
 
-        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(mRoomDb,
-                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false));
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", false), mRoomDb.mySegmentDao(), MySegmentEntity.creator());
     }
 
     @Test
-    public void getMySegments() {
-        Set<String> snapshot = new HashSet<>(mPersistentMySegmentsStorage.getSnapshot(mUserKey));
-
-        Assert.assertEquals(3, snapshot.size());
-        Assert.assertTrue(snapshot.contains("s1"));
-        Assert.assertTrue(snapshot.contains("s2"));
-        Assert.assertTrue(snapshot.contains("s3"));
+    public void getNames() {
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+
+        List<String> mySegments = snapshot.getNames();
+        Assert.assertEquals(3, mySegments.size());
+        Assert.assertTrue(mySegments.contains("s1"));
+        Assert.assertTrue(mySegments.contains("s2"));
+        Assert.assertTrue(mySegments.contains("s3"));
     }
 
     @Test
     public void updateSegments() {
 
-        mPersistentMySegmentsStorage.set(mUserKey, Collections.singletonList("a1,a2,a3,a4"));
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), 2002012L));
 
-        Set<String> snapshot = new HashSet<>(mPersistentMySegmentsStorage.getSnapshot(mUserKey));
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
 
-        Assert.assertEquals(4, snapshot.size());
-        Assert.assertTrue(snapshot.contains("a1"));
-        Assert.assertTrue(snapshot.contains("a2"));
-        Assert.assertTrue(snapshot.contains("a3"));
-        Assert.assertTrue(snapshot.contains("a4"));
+        List<String> mySegments = snapshot.getNames();
+        Long till = snapshot.getChangeNumber();
+        Assert.assertEquals(4, mySegments.size());
+        Assert.assertTrue(mySegments.contains("a1"));
+        Assert.assertTrue(mySegments.contains("a2"));
+        Assert.assertTrue(mySegments.contains("a3"));
+        Assert.assertTrue(mySegments.contains("a4"));
+        Assert.assertEquals(2002012, till.longValue());
     }
 
     @Test
     public void updateSegmentsEncrypted() {
-        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(mRoomDb,
-                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", true));
+        mPersistentMySegmentsStorage = new SqLitePersistentMySegmentsStorage(
+                SplitCipherFactory.create("abcdefghijlkmnopqrstuvxyz", true), mRoomDb.mySegmentDao(), MySegmentEntity.creator());
 
-        mPersistentMySegmentsStorage.set(mUserKey, Collections.singletonList("a1,a2,a3,a4"));
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(IntegrationHelper.asSet("a1", "a2", "a3", "a4"), -1L));
 
-        Set<String> snapshot = new HashSet<>(mPersistentMySegmentsStorage.getSnapshot(mUserKey));
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
 
-        Assert.assertEquals(4, snapshot.size());
-        Assert.assertTrue(snapshot.contains("a1"));
-        Assert.assertTrue(snapshot.contains("a2"));
-        Assert.assertTrue(snapshot.contains("a3"));
-        Assert.assertTrue(snapshot.contains("a4"));
+        List<String> mySegments = snapshot.getNames();
+        Assert.assertEquals(4, mySegments.size());
+        Assert.assertTrue(mySegments.contains("a1"));
+        Assert.assertTrue(mySegments.contains("a2"));
+        Assert.assertTrue(mySegments.contains("a3"));
+        Assert.assertTrue(mySegments.contains("a4"));
+        Assert.assertEquals(-1, snapshot.getChangeNumber().longValue());
     }
 
     @Test
     public void updateEmptyMySegment() {
 
-        mPersistentMySegmentsStorage.set(mUserKey, new ArrayList<>());
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(IntegrationHelper.asSet(), 22121L));
 
-        List<String> snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
 
-        Assert.assertEquals(0, snapshot.size());
+        Assert.assertEquals(0, snapshot.getNames().size());
+        Assert.assertEquals(22121, snapshot.getChangeNumber().longValue());
     }
 
     @Test
     public void addNullMySegmentsList() {
-        mPersistentMySegmentsStorage.set(mUserKey, null);
+        mPersistentMySegmentsStorage.set(mUserKey, SegmentsChange.create(null, -1L));
 
-        List<String> snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
+        SegmentsChange snapshot = mPersistentMySegmentsStorage.getSnapshot(mUserKey);
 
-        Assert.assertEquals(3, snapshot.size());
+        Assert.assertEquals(3, snapshot.getNames().size());
+        Assert.assertNull(snapshot.getChangeNumber());
     }
 }
diff --git a/src/androidTest/java/tests/workmanager/WorkManagerWrapperTest.java b/src/androidTest/java/tests/workmanager/WorkManagerWrapperTest.java
index 0c7ce2b24..bc9867946 100644
--- a/src/androidTest/java/tests/workmanager/WorkManagerWrapperTest.java
+++ b/src/androidTest/java/tests/workmanager/WorkManagerWrapperTest.java
@@ -1,6 +1,7 @@
 package tests.workmanager;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -26,6 +27,7 @@
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.concurrent.TimeUnit;
 
 import io.split.android.client.ServiceEndpoints;
@@ -38,6 +40,9 @@
 import io.split.android.client.service.workmanager.ImpressionsRecorderWorker;
 import io.split.android.client.service.workmanager.MySegmentsSyncWorker;
 import io.split.android.client.service.workmanager.splits.SplitsSyncWorker;
+import io.split.android.client.utils.logger.LogPrinter;
+import io.split.android.client.utils.logger.Logger;
+import io.split.android.client.utils.logger.SplitLogLevel;
 
 public class WorkManagerWrapperTest {
 
@@ -51,7 +56,13 @@ public void setUp() throws Exception {
 
         when(mWorkManager.getWorkInfosByTagLiveData(any())).thenReturn(mock(LiveData.class));
 
-        SplitClientConfig splitClientConfig = new SplitClientConfig.Builder()
+        SplitClientConfig splitClientConfig = buildConfig(true);
+
+        mWrapper = getWrapper(splitClientConfig);
+    }
+
+    private static @NonNull SplitClientConfig buildConfig(boolean useCertificatePinning) {
+        SplitClientConfig.Builder configBuilder = new SplitClientConfig.Builder()
                 .serviceEndpoints(
                         ServiceEndpoints.builder()
                                 .sseAuthServiceEndpoint("https://test.split.io/serviceEndpoint")
@@ -64,23 +75,31 @@ public void setUp() throws Exception {
                 .eventsPerPush(526)
                 .impressionsPerPush(256)
                 .backgroundSyncWhenWifiOnly(true)
-                .backgroundSyncWhenBatteryNotLow(false)
-                .certificatePinningConfiguration(CertificatePinningConfiguration.builder()
-                        .addPin("events.split.io", "sha256/sDKdggs")
-                        .addPin("sdk.split.io", "sha256/jIUe51")
-                        .addPin("events.split.io", "sha1/jLeisDf")
-                        .build())
-                .build();
+                .backgroundSyncWhenBatteryNotLow(false);
+
+        if (useCertificatePinning) {
+            configBuilder.certificatePinningConfiguration(CertificatePinningConfiguration.builder()
+                    .addPin("events.split.io", "sha256/sDKdggs")
+                    .addPin("sdk.split.io", "sha256/jIUe51")
+                    .addPin("events.split.io", "sha1/jLeisDf")
+                    .build());
+        }
+
+        SplitClientConfig config = configBuilder.build();
 
         try {
-            Method method = splitClientConfig.getClass().getDeclaredMethod("enableTelemetry");
+            Method method = config.getClass().getDeclaredMethod("enableTelemetry");
             method.setAccessible(true);
-            method.invoke(splitClientConfig);
+            method.invoke(config);
         } catch (Exception exception) {
             exception.printStackTrace();
         }
 
-        mWrapper = new WorkManagerWrapper(
+        return config;
+    }
+
+    private @NonNull WorkManagerWrapper getWrapper(SplitClientConfig splitClientConfig) {
+        return new WorkManagerWrapper(
                 mWorkManager,
                 splitClientConfig,
                 "api_key",
@@ -97,6 +116,7 @@ public void removeWorkCancelsJobs() {
         verify(mWorkManager).cancelUniqueWork(SplitTaskType.MY_SEGMENTS_SYNC.toString());
         verify(mWorkManager).cancelUniqueWork(SplitTaskType.EVENTS_RECORDER.toString());
         verify(mWorkManager).cancelUniqueWork(SplitTaskType.IMPRESSIONS_RECORDER.toString());
+        verify(mWorkManager).cancelUniqueWork(SplitTaskType.UNIQUE_KEYS_RECORDER_TASK.toString());
     }
 
     @Test
@@ -109,7 +129,7 @@ public void scheduleWorkSchedulesSplitsJob() {
                 .putBoolean("shouldRecordTelemetry", true)
                 .putStringArray("configuredFilterValues", new String[]{"set_1", "set_2"})
                 .putString("configuredFilterType", SplitFilter.Type.BY_SET.queryStringField())
-                .putString("flagsSpec", "1.1")
+                .putString("flagsSpec", "1.2")
                 .putString("certificatePins", certificatePinsJson())
                 .build();
 
@@ -221,6 +241,44 @@ public void scheduleMySegmentsWorkSchedulesJob() {
         assertWorkSpecMatches(argumentCaptor.getValue().getWorkSpec(), expectedRequest.getWorkSpec());
     }
 
+    @Test
+    public void schedulingWithoutCertificatePinning() {
+        SplitClientConfig splitClientConfig = buildConfig(false);
+        LinkedList<String> logs = new LinkedList<>();
+        mWrapper = getWrapper(splitClientConfig);
+        Logger.instance().setLevel(SplitLogLevel.ERROR);
+        Logger.instance().setPrinter(getLogPrinter(logs));
+
+        mWrapper.scheduleWork();
+
+        Data inputData = new Data.Builder()
+                .putLong("splitCacheExpiration", 864000)
+                .putString("endpoint", "https://test.split.io/api")
+                .putBoolean("shouldRecordTelemetry", true)
+                .putStringArray("configuredFilterValues", new String[]{"set_1", "set_2"})
+                .putString("configuredFilterType", SplitFilter.Type.BY_SET.queryStringField())
+                .putString("flagsSpec", "1.2")
+                .build();
+
+        PeriodicWorkRequest expectedRequest = new PeriodicWorkRequest
+                .Builder(SplitsSyncWorker.class, 5263, TimeUnit.MINUTES)
+                .setInputData(buildInputData(inputData))
+                .setConstraints(buildConstraints())
+                .setInitialDelay(15L, TimeUnit.MINUTES)
+                .build();
+
+        ArgumentCaptor<PeriodicWorkRequest> argumentCaptor = ArgumentCaptor.forClass(PeriodicWorkRequest.class);
+
+        verify(mWorkManager).enqueueUniquePeriodicWork(
+                eq(SplitTaskType.SPLITS_SYNC.toString()),
+                eq(ExistingPeriodicWorkPolicy.REPLACE),
+                argumentCaptor.capture()
+        );
+
+        assertTrue(logs.isEmpty());
+        assertWorkSpecMatches(argumentCaptor.getValue().getWorkSpec(), expectedRequest.getWorkSpec());
+    }
+
     private void assertWorkSpecMatches(WorkSpec workSpec, WorkSpec expectedWorkSpec) {
         assertEquals(expectedWorkSpec.backoffPolicy, workSpec.backoffPolicy);
         assertEquals(expectedWorkSpec.backoffDelayDuration, workSpec.backoffDelayDuration);
@@ -256,4 +314,39 @@ private Constraints buildConstraints() {
     private static String certificatePinsJson() {
         return "{\"events.split.io\":[{\"algo\":\"sha256\",\"pin\":[-80,50,-99,-126,11]},{\"algo\":\"sha1\",\"pin\":[-116,-73,-94,-80,55]}],\"sdk.split.io\":[{\"algo\":\"sha256\",\"pin\":[-116,-123,30,-25]}]}";
     }
+
+
+    private static @NonNull LogPrinter getLogPrinter(LinkedList<String> logs) {
+        return new LogPrinter() {
+            @Override
+            public void v(String tag, String msg, Throwable tr) {
+                logs.add("V: " + tag + " - " + msg);
+            }
+
+            @Override
+            public void d(String tag, String msg, Throwable tr) {
+                logs.add("D: " + tag + " - " + msg);
+            }
+
+            @Override
+            public void i(String tag, String msg, Throwable tr) {
+                logs.add("I: " + tag + " - " + msg);
+            }
+
+            @Override
+            public void w(String tag, String msg, Throwable tr) {
+                logs.add("W: " + tag + " - " + msg);
+            }
+
+            @Override
+            public void e(String tag, String msg, Throwable tr) {
+                logs.add("E: " + tag + " - " + msg);
+            }
+
+            @Override
+            public void wtf(String tag, String msg, Throwable tr) {
+                logs.add("!: " + tag + " - " + msg);
+            }
+        };
+    }
 }
diff --git a/src/main/java/io/split/android/client/SplitClientConfig.java b/src/main/java/io/split/android/client/SplitClientConfig.java
index 9e62f31a2..15872b80b 100644
--- a/src/main/java/io/split/android/client/SplitClientConfig.java
+++ b/src/main/java/io/split/android/client/SplitClientConfig.java
@@ -56,8 +56,7 @@ public class SplitClientConfig {
     private static final int DEFAULT_BACKGROUND_SYNC_PERIOD_MINUTES = 15;
     private static final long MIN_IMPRESSIONS_DEDUPE_TIME_INTERVAL = TimeUnit.HOURS.toMillis(1);
     private static final long MAX_IMPRESSIONS_DEDUPE_TIME_INTERVAL = TimeUnit.HOURS.toMillis(24);
-
-    private final static int DEFAULT_MTK_PER_PUSH = 30000;
+    private static final int DEFAULT_MTK_PER_PUSH = 30000;
 
     // Validation settings
     private static final int MAXIMUM_KEY_LENGTH = 250;
diff --git a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java
index ab741197f..0956c1788 100644
--- a/src/main/java/io/split/android/client/SplitClientFactoryImpl.java
+++ b/src/main/java/io/split/android/client/SplitClientFactoryImpl.java
@@ -73,7 +73,7 @@ public SplitClientFactoryImpl(@NonNull SplitFactory splitFactory,
                 validationLogger,
                 splitTaskExecutor,
                 mStorageContainer.getPersistentAttributesStorage());
-        mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer());
+        mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer());
         mSplitValidator = new SplitValidatorImpl();
         SplitsStorage splitsStorage = mStorageContainer.getSplitsStorage();
         mTreatmentManagerFactory = new TreatmentManagerFactoryImpl(
diff --git a/src/main/java/io/split/android/client/SplitFactoryHelper.java b/src/main/java/io/split/android/client/SplitFactoryHelper.java
index c70047fe8..45dacb73a 100644
--- a/src/main/java/io/split/android/client/SplitFactoryHelper.java
+++ b/src/main/java/io/split/android/client/SplitFactoryHelper.java
@@ -22,27 +22,29 @@
 import io.split.android.client.common.CompressionUtilProvider;
 import io.split.android.client.events.EventsManagerCoordinator;
 import io.split.android.client.network.HttpClient;
+import io.split.android.client.network.SdkTargetPath;
 import io.split.android.client.network.SplitHttpHeadersBuilder;
 import io.split.android.client.service.ServiceFactory;
 import io.split.android.client.service.SplitApiFacade;
 import io.split.android.client.service.executor.SplitTaskExecutionListener;
 import io.split.android.client.service.executor.SplitTaskExecutor;
 import io.split.android.client.service.executor.SplitTaskFactory;
+import io.split.android.client.service.http.mysegments.MySegmentsFetcherFactory;
 import io.split.android.client.service.http.mysegments.MySegmentsFetcherFactoryImpl;
 import io.split.android.client.service.impressions.strategy.ImpressionStrategyConfig;
 import io.split.android.client.service.impressions.strategy.ImpressionStrategyProvider;
 import io.split.android.client.service.impressions.strategy.ProcessStrategy;
+import io.split.android.client.service.mysegments.AllSegmentsResponseParser;
 import io.split.android.client.service.sseclient.EventStreamParser;
 import io.split.android.client.service.sseclient.ReconnectBackoffCounter;
 import io.split.android.client.service.sseclient.SseJwtParser;
 import io.split.android.client.service.sseclient.feedbackchannel.PushManagerEventBroadcaster;
-import io.split.android.client.service.sseclient.notifications.MySegmentsPayloadDecoder;
 import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
 import io.split.android.client.service.sseclient.notifications.NotificationParser;
 import io.split.android.client.service.sseclient.notifications.NotificationProcessor;
 import io.split.android.client.service.sseclient.notifications.SplitsChangeNotification;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorFactory;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorFactoryImpl;
+import io.split.android.client.service.sseclient.notifications.mysegments.MembershipsNotificationProcessorFactory;
+import io.split.android.client.service.sseclient.notifications.mysegments.MembershipsNotificationProcessorFactoryImpl;
 import io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorkerRegistry;
 import io.split.android.client.service.sseclient.reactor.SplitUpdatesWorker;
 import io.split.android.client.service.sseclient.sseclient.BackoffCounterTimer;
@@ -161,6 +163,7 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus,
         return new SplitStorageContainer(
                 StorageFactory.getSplitsStorage(splitRoomDatabase, splitCipher),
                 StorageFactory.getMySegmentsStorage(splitRoomDatabase, splitCipher),
+                StorageFactory.getMyLargeSegmentsStorage(splitRoomDatabase, splitCipher),
                 StorageFactory.getPersistentSplitsStorage(splitRoomDatabase, splitCipher),
                 StorageFactory.getEventsStorage(persistentEventsStorage, isPersistenceEnabled),
                 persistentEventsStorage,
@@ -182,7 +185,8 @@ SplitApiFacade buildApiFacade(SplitClientConfig splitClientConfig,
                 ServiceFactory.getSplitsFetcher(httpClient,
                         splitClientConfig.endpoint(), splitsFilterQueryString),
                 new MySegmentsFetcherFactoryImpl(httpClient,
-                        splitClientConfig.endpoint()),
+                        splitClientConfig.endpoint(), new AllSegmentsResponseParser(),
+                        new MySegmentsUriBuilder(splitClientConfig.endpoint())),
                 ServiceFactory.getSseAuthenticationFetcher(httpClient,
                         splitClientConfig.authServiceUrl()),
                 ServiceFactory.getEventsRecorder(httpClient,
@@ -298,14 +302,11 @@ public ClientComponentsRegisterImpl getClientComponentsRegister(SplitClientConfi
         if (config.persistentAttributesEnabled()) {
             attributesStorage = storageContainer.getPersistentAttributesStorage();
         }
-        MySegmentsSynchronizerFactory mySegmentsSynchronizerFactory = new MySegmentsSynchronizerFactoryImpl(
-                new RetryBackoffCounterTimerFactory(),
-                taskExecutor,
-                config.segmentsRefreshRate());
+        MySegmentsSynchronizerFactory mySegmentsSynchronizerFactory = new MySegmentsSynchronizerFactoryImpl(new RetryBackoffCounterTimerFactory(), taskExecutor);
 
-        MySegmentsNotificationProcessorFactory mySegmentsNotificationProcessorFactory = null;
+        MembershipsNotificationProcessorFactory membershipsNotificationProcessorFactory = null;
         if (config.syncEnabled()) {
-            mySegmentsNotificationProcessorFactory = new MySegmentsNotificationProcessorFactoryImpl(notificationParser,
+            membershipsNotificationProcessorFactory = new MembershipsNotificationProcessorFactoryImpl(notificationParser,
                     taskExecutor,
                     mySegmentsV2PayloadDecoder,
                     compressionProvider);
@@ -322,7 +323,7 @@ public ClientComponentsRegisterImpl getClientComponentsRegister(SplitClientConfi
                 eventsManagerCoordinator,
                 sseAuthenticator,
                 notificationProcessor,
-                mySegmentsNotificationProcessorFactory,
+                membershipsNotificationProcessorFactory,
                 mySegmentsV2PayloadDecoder);
     }
 
@@ -343,7 +344,7 @@ public StreamingComponents buildStreamingComponents(@NonNull SplitTaskExecutor s
         NotificationParser notificationParser = new NotificationParser();
 
         NotificationProcessor notificationProcessor = new NotificationProcessor(splitTaskExecutor, splitTaskFactory,
-                notificationParser, splitsUpdateNotificationQueue, new MySegmentsPayloadDecoder());
+                notificationParser, splitsUpdateNotificationQueue);
 
         PushManagerEventBroadcaster pushManagerEventBroadcaster = new PushManagerEventBroadcaster();
 
@@ -469,4 +470,17 @@ private TelemetryStorage getTelemetryStorage(boolean shouldRecordTelemetry, Tele
         }
         return StorageFactory.getTelemetryStorage(shouldRecordTelemetry);
     }
+
+    static class MySegmentsUriBuilder implements MySegmentsFetcherFactory.UriBuilder {
+        private final String mEndpoint;
+
+        public MySegmentsUriBuilder(String endpoint) {
+            mEndpoint = endpoint;
+        }
+
+        @Override
+        public URI build(String matchingKey) throws URISyntaxException {
+            return SdkTargetPath.mySegments(mEndpoint, matchingKey);
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/SplitFactoryImpl.java b/src/main/java/io/split/android/client/SplitFactoryImpl.java
index a62861e06..da1db63ff 100644
--- a/src/main/java/io/split/android/client/SplitFactoryImpl.java
+++ b/src/main/java/io/split/android/client/SplitFactoryImpl.java
@@ -11,9 +11,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 
 import io.split.android.android_client.BuildConfig;
 import io.split.android.client.api.Key;
@@ -328,7 +325,7 @@ public void run() {
 
         // Initialize default client
         client();
-        SplitParser mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer());
+        SplitParser mSplitParser = new SplitParser(mStorageContainer.getMySegmentsStorageContainer(), mStorageContainer.getMyLargeSegmentsStorageContainer());
         mManager = new SplitManagerImpl(
                 mStorageContainer.getSplitsStorage(),
                 new SplitValidatorImpl(), mSplitParser);
diff --git a/src/main/java/io/split/android/client/dtos/AllSegmentsChange.java b/src/main/java/io/split/android/client/dtos/AllSegmentsChange.java
new file mode 100644
index 000000000..0be63f89d
--- /dev/null
+++ b/src/main/java/io/split/android/client/dtos/AllSegmentsChange.java
@@ -0,0 +1,53 @@
+package io.split.android.client.dtos;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AllSegmentsChange {
+
+    @SerializedName("ms")
+    private SegmentsChange mMySegmentsChange;
+
+    @SerializedName("ls")
+    private SegmentsChange mMyLargeSegmentsChange;
+
+    public AllSegmentsChange() {
+
+    }
+
+    // TODO legacy endpoint support during development
+    @Deprecated
+    public AllSegmentsChange(List<String> mySegments) {
+        Set<Segment> segments = new HashSet<>();
+        for (String name : mySegments) {
+            Segment segment = new Segment();
+            segment.setName(name);
+            segments.add(segment);
+        }
+        mMySegmentsChange = new SegmentsChange(segments, null);
+    }
+
+    @Nullable
+    public SegmentsChange getSegmentsChange() {
+        return mMySegmentsChange;
+    }
+
+    @Nullable
+    public SegmentsChange getLargeSegmentsChange() {
+        return mMyLargeSegmentsChange;
+    }
+
+    @VisibleForTesting
+    public static AllSegmentsChange create(SegmentsChange mySegmentsChange, SegmentsChange myLargeSegmentsChange) {
+        AllSegmentsChange allSegmentsChange = new AllSegmentsChange();
+        allSegmentsChange.mMySegmentsChange = mySegmentsChange;
+        allSegmentsChange.mMyLargeSegmentsChange = myLargeSegmentsChange;
+        return allSegmentsChange;
+    }
+}
diff --git a/src/main/java/io/split/android/client/dtos/Matcher.java b/src/main/java/io/split/android/client/dtos/Matcher.java
index 8007065b7..69113a58a 100644
--- a/src/main/java/io/split/android/client/dtos/Matcher.java
+++ b/src/main/java/io/split/android/client/dtos/Matcher.java
@@ -1,19 +1,34 @@
 package io.split.android.client.dtos;
 
+import com.google.gson.annotations.SerializedName;
+
 /**
  * A leaf class representing a matcher.
  *
  */
 public class Matcher {
+    @SerializedName("keySelector")
     public KeySelector keySelector;
+    @SerializedName("matcherType")
     public MatcherType matcherType;
+    @SerializedName("negate")
     public boolean negate;
+    @SerializedName("userDefinedSegmentMatcherData")
     public UserDefinedSegmentMatcherData userDefinedSegmentMatcherData;
+    @SerializedName("userDefinedLargeSegmentMatcherData")
+    public UserDefinedLargeSegmentMatcherData userDefinedLargeSegmentMatcherData;
+    @SerializedName("whitelistMatcherData")
     public WhitelistMatcherData whitelistMatcherData;
+    @SerializedName("unaryNumericMatcherData")
     public UnaryNumericMatcherData unaryNumericMatcherData;
+    @SerializedName("betweenMatcherData")
     public BetweenMatcherData betweenMatcherData;
+    @SerializedName("dependencyMatcherData")
     public DependencyMatcherData dependencyMatcherData;
+    @SerializedName("booleanMatcherData")
     public Boolean booleanMatcherData;
+    @SerializedName("stringMatcherData")
     public String stringMatcherData;
+    @SerializedName("betweenStringMatcherData")
     public BetweenStringMatcherData betweenStringMatcherData;
 }
diff --git a/src/main/java/io/split/android/client/dtos/MatcherType.java b/src/main/java/io/split/android/client/dtos/MatcherType.java
index fafa56204..580e97f2e 100644
--- a/src/main/java/io/split/android/client/dtos/MatcherType.java
+++ b/src/main/java/io/split/android/client/dtos/MatcherType.java
@@ -7,6 +7,8 @@ public enum MatcherType {
     ALL_KEYS,
     @SerializedName("IN_SEGMENT")
     IN_SEGMENT,
+    @SerializedName("IN_LARGE_SEGMENT")
+    IN_LARGE_SEGMENT,
     @SerializedName("WHITELIST")
     WHITELIST,
 
diff --git a/src/main/java/io/split/android/client/dtos/MySegment.java b/src/main/java/io/split/android/client/dtos/MySegment.java
deleted file mode 100644
index a1132780a..000000000
--- a/src/main/java/io/split/android/client/dtos/MySegment.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.split.android.client.dtos;
-
-public class MySegment {
-    public String id;
-    public String name;
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        MySegment mySegment = (MySegment) o;
-
-        return name.equals(mySegment.name);
-    }
-
-    @Override
-    public int hashCode() {
-        return name.hashCode();
-    }
-}
diff --git a/src/main/java/io/split/android/client/dtos/Segment.java b/src/main/java/io/split/android/client/dtos/Segment.java
new file mode 100644
index 000000000..dce92693e
--- /dev/null
+++ b/src/main/java/io/split/android/client/dtos/Segment.java
@@ -0,0 +1,17 @@
+package io.split.android.client.dtos;
+
+import com.google.gson.annotations.SerializedName;
+
+public class Segment {
+
+    @SerializedName("n")
+    private String mName;
+
+    public String getName() {
+        return mName;
+    }
+
+    void setName(String name) {
+        mName = name;
+    }
+}
diff --git a/src/main/java/io/split/android/client/dtos/SegmentChange.java b/src/main/java/io/split/android/client/dtos/SegmentChange.java
deleted file mode 100644
index 5ec6da727..000000000
--- a/src/main/java/io/split/android/client/dtos/SegmentChange.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.split.android.client.dtos;
-
-import java.util.List;
-
-public class SegmentChange {
-    public String id;
-    public String name;
-    public List<String> added;
-    public List<String> removed;
-    public long since;
-    public long till;
-}
diff --git a/src/main/java/io/split/android/client/dtos/SegmentResponse.java b/src/main/java/io/split/android/client/dtos/SegmentResponse.java
new file mode 100644
index 000000000..f5c4b2ab8
--- /dev/null
+++ b/src/main/java/io/split/android/client/dtos/SegmentResponse.java
@@ -0,0 +1,12 @@
+package io.split.android.client.dtos;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public interface SegmentResponse {
+    @NonNull
+    List<String> getSegments();
+
+    long getTill();
+}
diff --git a/src/main/java/io/split/android/client/dtos/SegmentsChange.java b/src/main/java/io/split/android/client/dtos/SegmentsChange.java
new file mode 100644
index 000000000..d01bf5437
--- /dev/null
+++ b/src/main/java/io/split/android/client/dtos/SegmentsChange.java
@@ -0,0 +1,67 @@
+package io.split.android.client.dtos;
+
+import androidx.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class SegmentsChange {
+    @SerializedName("k")
+    private Set<Segment> mSegments;
+
+    @SerializedName("cn")
+    private Long mChangeNumber;
+
+    public SegmentsChange(Set<Segment> segments, Long changeNumber) {
+        mSegments = segments;
+        mChangeNumber = changeNumber;
+    }
+
+    public Set<Segment> getSegments() {
+        return mSegments == null ? Collections.emptySet() : mSegments;
+    }
+
+    @Nullable
+    public Long getChangeNumber() {
+        return mChangeNumber;
+    }
+
+    public List<String> getNames() {
+        Set<Segment> segments = new HashSet<>(getSegments());
+        List<String> names = new ArrayList<>(segments.size());
+        for (Segment segment : segments) {
+            names.add(segment.getName());
+        }
+        return names;
+    }
+
+    public static SegmentsChange createEmpty() {
+        return new SegmentsChange(Collections.emptySet(), null);
+    }
+
+    @Nullable
+    public static SegmentsChange create(Set<String> segments, long changeNumber) {
+        if (segments == null) {
+            return null;
+        }
+        return create(segments, Long.valueOf(changeNumber));
+    }
+
+    public static SegmentsChange create(Set<String> segments, @Nullable Long changeNumber) {
+        if (segments == null) {
+            return SegmentsChange.createEmpty();
+        }
+        Set<Segment> segmentSet = new HashSet<>();
+        for (String segment : segments) {
+            Segment segmentObj = new Segment();
+            segmentObj.setName(segment);
+            segmentSet.add(segmentObj);
+        }
+        return new SegmentsChange(segmentSet, changeNumber);
+    }
+}
diff --git a/src/main/java/io/split/android/client/dtos/UserDefinedLargeSegmentMatcherData.java b/src/main/java/io/split/android/client/dtos/UserDefinedLargeSegmentMatcherData.java
new file mode 100644
index 000000000..058e7611c
--- /dev/null
+++ b/src/main/java/io/split/android/client/dtos/UserDefinedLargeSegmentMatcherData.java
@@ -0,0 +1,8 @@
+package io.split.android.client.dtos;
+
+import com.google.gson.annotations.SerializedName;
+
+public class UserDefinedLargeSegmentMatcherData {
+    @SerializedName("largeSegmentName")
+    public String largeSegmentName;
+}
diff --git a/src/main/java/io/split/android/client/dtos/UserDefinedSegmentMatcherData.java b/src/main/java/io/split/android/client/dtos/UserDefinedSegmentMatcherData.java
index b1f147cee..4044520f7 100644
--- a/src/main/java/io/split/android/client/dtos/UserDefinedSegmentMatcherData.java
+++ b/src/main/java/io/split/android/client/dtos/UserDefinedSegmentMatcherData.java
@@ -1,5 +1,8 @@
 package io.split.android.client.dtos;
 
+import com.google.gson.annotations.SerializedName;
+
 public class UserDefinedSegmentMatcherData {
+    @SerializedName("segmentName")
     public String segmentName;
 }
diff --git a/src/main/java/io/split/android/client/events/SplitEventsManager.java b/src/main/java/io/split/android/client/events/SplitEventsManager.java
index 80d269d98..f6150157e 100644
--- a/src/main/java/io/split/android/client/events/SplitEventsManager.java
+++ b/src/main/java/io/split/android/client/events/SplitEventsManager.java
@@ -28,6 +28,10 @@ public class SplitEventsManager extends BaseEventsManager implements ISplitEvent
     private final SplitTaskExecutor mSplitTaskExecutor;
 
     public SplitEventsManager(SplitClientConfig config, SplitTaskExecutor splitTaskExecutor) {
+        this(splitTaskExecutor, config.blockUntilReady());
+    }
+
+    public SplitEventsManager(SplitTaskExecutor splitTaskExecutor, final int blockUntilReady) {
         super();
         mSplitTaskExecutor = splitTaskExecutor;
         mSubscriptions = new ConcurrentHashMap<>();
@@ -39,8 +43,8 @@ public SplitEventsManager(SplitClientConfig config, SplitTaskExecutor splitTaskE
             @Override
             public void run() {
                 try {
-                    if (config.blockUntilReady() > 0) {
-                        Thread.sleep(config.blockUntilReady());
+                    if (blockUntilReady > 0) {
+                        Thread.sleep(blockUntilReady);
                         notifyInternalEvent(SplitInternalEvent.SDK_READY_TIMEOUT_REACHED);
                     }
                 } catch (InterruptedException e) {
@@ -84,6 +88,7 @@ public void notifyInternalEvent(SplitInternalEvent internalEvent) {
         // These events were added to handle updated event logic in this component
         // and also to fix some issues when processing queue that made sdk update
         // fire on init
+
         if ((internalEvent == SplitInternalEvent.SPLITS_FETCHED
                 || internalEvent == SplitInternalEvent.MY_SEGMENTS_FETCHED) &&
                 isTriggered(SplitEvent.SDK_READY)) {
@@ -129,6 +134,7 @@ protected void triggerEventsWhenAreAvailable() {
             switch (event) {
                 case SPLITS_UPDATED:
                 case MY_SEGMENTS_UPDATED:
+                case MY_LARGE_SEGMENTS_UPDATED:
                     if (isTriggered(SplitEvent.SDK_READY)) {
                         trigger(SplitEvent.SDK_UPDATE);
                         return;
@@ -182,7 +188,7 @@ private boolean isTriggered(SplitEvent event) {
     }
 
     private void triggerSdkReadyIfNeeded() {
-        if ((wasTriggered(SplitInternalEvent.MY_SEGMENTS_UPDATED) || wasTriggered(SplitInternalEvent.MY_SEGMENTS_FETCHED)) &&
+        if ((wasTriggered(SplitInternalEvent.MY_SEGMENTS_UPDATED) || wasTriggered(SplitInternalEvent.MY_SEGMENTS_FETCHED) || wasTriggered(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED)) &&
                 (wasTriggered(SplitInternalEvent.SPLITS_UPDATED) || wasTriggered(SplitInternalEvent.SPLITS_FETCHED)) &&
                 !isTriggered(SplitEvent.SDK_READY)) {
             trigger(SplitEvent.SDK_READY);
@@ -195,15 +201,17 @@ private void trigger(SplitEvent event) {
             return;
             // If executionTimes is grater than zero, maximum executions decrease 1
         } else if (mExecutionTimes.get(event) > 0) {
-            if (event != null) {
-                Logger.d(event.name() + " event triggered");
-            }
             mExecutionTimes.put(event, mExecutionTimes.get(event) - 1);
         } //If executionTimes is lower than zero, execute it without limitation
+        if (event != null) {
+            Logger.d(event.name() + " event triggered");
+        }
         if (mSubscriptions.containsKey(event)) {
             List<SplitEventTask> toExecute = mSubscriptions.get(event);
-            for (SplitEventTask task : toExecute) {
-                executeTask(event, task);
+            if (toExecute != null) {
+                for (SplitEventTask task : toExecute) {
+                    executeTask(event, task);
+                }
             }
         }
     }
diff --git a/src/main/java/io/split/android/client/events/SplitInternalEvent.java b/src/main/java/io/split/android/client/events/SplitInternalEvent.java
index 665f871f8..eaa25767d 100644
--- a/src/main/java/io/split/android/client/events/SplitInternalEvent.java
+++ b/src/main/java/io/split/android/client/events/SplitInternalEvent.java
@@ -15,4 +15,5 @@ public enum SplitInternalEvent {
     SPLIT_KILLED_NOTIFICATION,
     ATTRIBUTES_LOADED_FROM_STORAGE,
     ENCRYPTION_MIGRATION_DONE,
+    MY_LARGE_SEGMENTS_UPDATED,
 }
diff --git a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java
index 7c2c89321..25092466b 100644
--- a/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java
+++ b/src/main/java/io/split/android/client/localhost/LocalhostSplitFactory.java
@@ -66,7 +66,7 @@ public LocalhostSplitFactory(String key, Context context,
         EventsManagerCoordinator eventsManagerCoordinator = new EventsManagerCoordinator();
         FileStorage fileStorage = new FileStorage(context.getCacheDir(), ServiceConstants.LOCALHOST_FOLDER);
         SplitsStorage splitsStorage = new LocalhostSplitsStorage(mLocalhostFileName, context, fileStorage, eventsManagerCoordinator);
-        SplitParser splitParser = new SplitParser(new LocalhostMySegmentsStorageContainer());
+        SplitParser splitParser = new SplitParser(new LocalhostMySegmentsStorageContainer(), new LocalhostMySegmentsStorageContainer());
         SplitTaskExecutorImpl taskExecutor = new SplitTaskExecutorImpl();
         AttributesManagerFactory attributesManagerFactory = new AttributesManagerFactoryImpl(new AttributesValidatorImpl(), new ValidationMessageLoggerImpl());
 
diff --git a/src/main/java/io/split/android/client/localhost/shared/LocalhostSplitClientContainerImpl.java b/src/main/java/io/split/android/client/localhost/shared/LocalhostSplitClientContainerImpl.java
index d722b50c6..b0da1451f 100644
--- a/src/main/java/io/split/android/client/localhost/shared/LocalhostSplitClientContainerImpl.java
+++ b/src/main/java/io/split/android/client/localhost/shared/LocalhostSplitClientContainerImpl.java
@@ -56,7 +56,7 @@ public LocalhostSplitClientContainerImpl(LocalhostSplitFactory splitFactory,
 
     @Override
     protected void createNewClient(Key key) {
-        SplitEventsManager eventsManager = new SplitEventsManager(mConfig, mSplitTaskExecutor);
+        SplitEventsManager eventsManager = new SplitEventsManager(mSplitTaskExecutor, mConfig.blockUntilReady());
         eventsManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
         eventsManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_FETCHED);
         eventsManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
diff --git a/src/main/java/io/split/android/client/network/SdkTargetPath.java b/src/main/java/io/split/android/client/network/SdkTargetPath.java
index c977485dc..ff98fd6b9 100644
--- a/src/main/java/io/split/android/client/network/SdkTargetPath.java
+++ b/src/main/java/io/split/android/client/network/SdkTargetPath.java
@@ -1,5 +1,7 @@
 package io.split.android.client.network;
 
+import androidx.annotation.Nullable;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 
@@ -7,7 +9,7 @@
 
 public class SdkTargetPath {
     public static final String SPLIT_CHANGES = "/splitChanges";
-    public static final String MY_SEGMENTS = "/mySegments";
+    public static final String MEMBERSHIPS = "/memberships";
     public static final String EVENTS = "/events/bulk";
     public static final String IMPRESSIONS = "/testImpressions/bulk";
     public static final String IMPRESSIONS_COUNT = "/testImpressions/count";
@@ -21,8 +23,7 @@ public static URI splitChanges(String baseUrl, String queryString) throws URISyn
     }
 
     public static URI mySegments(String baseUrl, String key) throws URISyntaxException {
-        String encodedKey = key != null ? UrlEscapers.urlPathSegmentEscaper().escape(key) : null;
-        return buildUrl(baseUrl, MY_SEGMENTS + "/" + encodedKey);
+        return buildUrl(baseUrl, MEMBERSHIPS + "/" + getUrlEncodedKey(key));
     }
 
     public static URI events(String baseUrl) throws URISyntaxException {
@@ -73,4 +74,9 @@ private static String removeLastChar(String sourceString) {
                 ? sourceString
                 : (sourceString.substring(0, sourceString.length() - 1));
     }
+
+    @Nullable
+    private static String getUrlEncodedKey(String key) {
+        return key != null ? UrlEscapers.urlPathSegmentEscaper().escape(key) : null;
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/ServiceConstants.java b/src/main/java/io/split/android/client/service/ServiceConstants.java
index 29663e437..097b043b8 100644
--- a/src/main/java/io/split/android/client/service/ServiceConstants.java
+++ b/src/main/java/io/split/android/client/service/ServiceConstants.java
@@ -59,4 +59,5 @@ public class ServiceConstants {
     public static final long DEFAULT_OBSERVER_CACHE_EXPIRATION_PERIOD_MS = TimeUnit.HOURS.toMillis(4);
     public static final String FLAGS_SPEC_PARAM = "s";
     public static final long DEFAULT_IMPRESSIONS_DEDUPE_TIME_INTERVAL = 3600L * 1000L; // 1 hour
+    public static final int ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES = 10;
 }
diff --git a/src/main/java/io/split/android/client/service/ServiceFactory.java b/src/main/java/io/split/android/client/service/ServiceFactory.java
index 4c98eb9a8..9af8f5093 100644
--- a/src/main/java/io/split/android/client/service/ServiceFactory.java
+++ b/src/main/java/io/split/android/client/service/ServiceFactory.java
@@ -5,9 +5,9 @@
 import java.net.URISyntaxException;
 import java.util.List;
 
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.dtos.Event;
 import io.split.android.client.dtos.KeyImpression;
-import io.split.android.client.dtos.MySegment;
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.network.HttpClient;
 import io.split.android.client.network.SdkTargetPath;
@@ -22,7 +22,7 @@
 import io.split.android.client.service.impressions.ImpressionsRequestBodySerializer;
 import io.split.android.client.service.impressions.unique.MTK;
 import io.split.android.client.service.impressions.unique.MTKRequestBodySerializer;
-import io.split.android.client.service.mysegments.MySegmentsResponseParser;
+import io.split.android.client.service.mysegments.AllSegmentsResponseParser;
 import io.split.android.client.service.splits.SplitChangeResponseParser;
 import io.split.android.client.service.sseauthentication.SseAuthenticationResponseParser;
 import io.split.android.client.telemetry.TelemetryConfigBodySerializer;
@@ -43,14 +43,14 @@ public static HttpFetcher<SplitChange> getSplitsFetcher(
                 new SplitChangeResponseParser());
     }
 
-    public static HttpFetcher<List<MySegment>> getMySegmentsFetcher(
+    public static HttpFetcher<AllSegmentsChange> getMySegmentsFetcher(
             HttpClient httpClient,
             String endPoint,
             String key) throws URISyntaxException {
 
         return new HttpFetcherImpl<>(httpClient,
                 SdkTargetPath.mySegments(endPoint, key),
-                new MySegmentsResponseParser());
+                new AllSegmentsResponseParser());
     }
 
     public static HttpRecorder<List<Event>> getEventsRecorder(
diff --git a/src/main/java/io/split/android/client/service/SplitApiFacade.java b/src/main/java/io/split/android/client/service/SplitApiFacade.java
index b0b2251dd..bad8763c6 100644
--- a/src/main/java/io/split/android/client/service/SplitApiFacade.java
+++ b/src/main/java/io/split/android/client/service/SplitApiFacade.java
@@ -6,9 +6,9 @@
 
 import java.util.List;
 
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.dtos.Event;
 import io.split.android.client.dtos.KeyImpression;
-import io.split.android.client.dtos.MySegment;
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.service.http.HttpRecorder;
@@ -54,7 +54,7 @@ public HttpFetcher<SplitChange> getSplitFetcher() {
         return mSplitFetcher;
     }
 
-    public HttpFetcher<List<MySegment>> getMySegmentsFetcher(String matchingKey) {
+    public HttpFetcher<AllSegmentsChange> getMySegmentsFetcher(String matchingKey) {
         return mMySegmentsFetcherFactory.getFetcher(matchingKey);
     }
 
diff --git a/src/main/java/io/split/android/client/service/events/EventsRecorderTask.java b/src/main/java/io/split/android/client/service/events/EventsRecorderTask.java
index 75bcb064d..d380af4e7 100644
--- a/src/main/java/io/split/android/client/service/events/EventsRecorderTask.java
+++ b/src/main/java/io/split/android/client/service/events/EventsRecorderTask.java
@@ -76,7 +76,7 @@ public SplitTaskExecutionInfo execute() {
 
                     mTelemetryRuntimeProducer.recordSyncError(OperationType.EVENTS, e.getHttpStatus());
 
-                    if (HttpStatus.fromCode(e.getHttpStatus()) == HttpStatus.INTERNAL_NON_RETRYABLE) {
+                    if (HttpStatus.isNotRetryable(e.getHttpStatus())) {
                         doNotRetry = true;
                         break;
                     }
diff --git a/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java b/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java
index d05000bf4..2c167cda0 100644
--- a/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java
+++ b/src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java
@@ -224,6 +224,7 @@ private TelemetryTaskFactory initializeTelemetryTaskFactory(@NonNull SplitClient
                 splitClientConfig,
                 mSplitsStorageContainer.getSplitsStorage(),
                 mSplitsStorageContainer.getMySegmentsStorageContainer(),
+                mSplitsStorageContainer.getMyLargeSegmentsStorageContainer(),
                 totalFlagSetCount,
                 invalidFlagSetCount);
         return mTelemetryTaskFactory;
diff --git a/src/main/java/io/split/android/client/service/executor/SplitTaskType.java b/src/main/java/io/split/android/client/service/executor/SplitTaskType.java
index 6eb182489..601b79b7f 100644
--- a/src/main/java/io/split/android/client/service/executor/SplitTaskType.java
+++ b/src/main/java/io/split/android/client/service/executor/SplitTaskType.java
@@ -2,10 +2,11 @@
 
 public enum SplitTaskType {
     SPLITS_SYNC, MY_SEGMENTS_SYNC, EVENTS_RECORDER, IMPRESSIONS_RECORDER,
-    LOAD_LOCAL_SPLITS, LOAD_LOCAL_MY_SYGMENTS, SSE_AUTHENTICATION_TASK,
-    MY_SEGMENTS_OVERWRITE, SPLIT_KILL, FILTER_SPLITS_CACHE, GENERIC_TASK,
+    LOAD_LOCAL_SPLITS, LOAD_LOCAL_MY_SEGMENTS, SSE_AUTHENTICATION_TASK,
+    SPLIT_KILL, FILTER_SPLITS_CACHE, GENERIC_TASK,
     CLEAN_UP_DATABASE, IMPRESSIONS_COUNT_RECORDER, SAVE_IMPRESSIONS_COUNT,
     MY_SEGMENTS_UPDATE, LOAD_LOCAL_ATTRIBUTES,
     TELEMETRY_CONFIG_TASK, TELEMETRY_STATS_TASK,
     SAVE_UNIQUE_KEYS_TASK, UNIQUE_KEYS_RECORDER_TASK,
+    MY_LARGE_SEGMENTS_UPDATE,
 }
diff --git a/src/main/java/io/split/android/client/service/http/HttpFetcherImpl.java b/src/main/java/io/split/android/client/service/http/HttpFetcherImpl.java
index 381a9cc02..38c445713 100644
--- a/src/main/java/io/split/android/client/service/http/HttpFetcherImpl.java
+++ b/src/main/java/io/split/android/client/service/http/HttpFetcherImpl.java
@@ -49,6 +49,7 @@ public T execute(@NonNull Map<String, Object> params,
                 uriBuilder.addParameter(param.getKey(), value != null ? value.toString() : "");
             }
             URI builtUri = uriBuilder.build();
+
             HttpResponse response = mClient.request(builtUri, HttpMethod.GET, null, headers).execute();
             if (builtUri != null && response != null) {
                 Logger.v("Received from: " + builtUri + " -> " + response.getData());
diff --git a/src/main/java/io/split/android/client/service/http/HttpStatus.java b/src/main/java/io/split/android/client/service/http/HttpStatus.java
index 7660e65b3..2ae1c9a86 100644
--- a/src/main/java/io/split/android/client/service/http/HttpStatus.java
+++ b/src/main/java/io/split/android/client/service/http/HttpStatus.java
@@ -5,6 +5,7 @@
 public enum HttpStatus {
 
     URI_TOO_LONG(414, "URI Too Long"),
+    FORBIDDEN(403, "Forbidden"),
 
     INTERNAL_NON_RETRYABLE(9009, "Non retryable");
 
@@ -41,6 +42,11 @@ public static HttpStatus fromCode(Integer code) {
     public static boolean isNotRetryable(HttpStatus httpStatus) {
         // these are values that internally indicate that the request should not be retried
         return httpStatus == HttpStatus.URI_TOO_LONG ||
+                httpStatus == HttpStatus.FORBIDDEN ||
                 httpStatus == HttpStatus.INTERNAL_NON_RETRYABLE;
     }
+
+    public static boolean isNotRetryable(Integer code) {
+        return isNotRetryable(fromCode(code));
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactory.java b/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactory.java
index cf8a4f28e..8f3309879 100644
--- a/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactory.java
+++ b/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactory.java
@@ -1,11 +1,17 @@
 package io.split.android.client.service.http.mysegments;
 
-import java.util.List;
+import java.net.URI;
+import java.net.URISyntaxException;
 
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.service.http.HttpFetcher;
 
 public interface MySegmentsFetcherFactory {
 
-    HttpFetcher<List<MySegment>> getFetcher(String userKey);
+    HttpFetcher<AllSegmentsChange> getFetcher(String userKey);
+
+    interface UriBuilder {
+
+        URI build(String matchingKey) throws URISyntaxException;
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImpl.java b/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImpl.java
index 60ce2d244..6317cc10b 100644
--- a/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImpl.java
+++ b/src/main/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImpl.java
@@ -6,37 +6,40 @@
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.List;
 
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.network.HttpClient;
-import io.split.android.client.network.SdkTargetPath;
 import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.service.http.HttpFetcherImpl;
-import io.split.android.client.service.mysegments.MySegmentsResponseParser;
+import io.split.android.client.service.http.HttpResponseParser;
 import io.split.android.client.utils.logger.Logger;
 
 public class MySegmentsFetcherFactoryImpl implements MySegmentsFetcherFactory {
 
     private final String mEndpoint;
     private final HttpClient mHttpClient;
-    private final MySegmentsResponseParser mMySegmentsResponseParser;
+    private final HttpResponseParser<AllSegmentsChange> mMySegmentsResponseParser;
+    private final UriBuilder mUriBuilder;
 
     public MySegmentsFetcherFactoryImpl(@NonNull HttpClient httpClient,
-                                        @NonNull String endpoint) {
+                                        @NonNull String endpoint,
+                                        @NonNull HttpResponseParser<AllSegmentsChange> responseParser,
+                                        @NonNull UriBuilder uriBuilder) {
         mHttpClient = checkNotNull(httpClient);
         mEndpoint = checkNotNull(endpoint);
-        mMySegmentsResponseParser = new MySegmentsResponseParser();
+        mMySegmentsResponseParser = checkNotNull(responseParser);
+        mUriBuilder = uriBuilder;
     }
 
     @Override
-    public HttpFetcher<List<MySegment>> getFetcher(String matchingKey) {
+    public HttpFetcher<AllSegmentsChange> getFetcher(String matchingKey) {
         return new HttpFetcherImpl<>(mHttpClient, buildTargetUrl(matchingKey), mMySegmentsResponseParser);
     }
 
     private URI buildTargetUrl(String matchingKey) {
         try {
-            return SdkTargetPath.mySegments(mEndpoint, matchingKey);
+            return mUriBuilder.build(matchingKey);
         } catch (URISyntaxException e) {
             Logger.e(e.getMessage());
         }
diff --git a/src/main/java/io/split/android/client/service/mysegments/AllSegmentsResponseParser.java b/src/main/java/io/split/android/client/service/mysegments/AllSegmentsResponseParser.java
new file mode 100644
index 000000000..445d05e5e
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/mysegments/AllSegmentsResponseParser.java
@@ -0,0 +1,22 @@
+package io.split.android.client.service.mysegments;
+
+import com.google.gson.JsonSyntaxException;
+
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.service.http.HttpResponseParser;
+import io.split.android.client.service.http.HttpResponseParserException;
+import io.split.android.client.utils.Json;
+
+public class AllSegmentsResponseParser implements HttpResponseParser<AllSegmentsChange> {
+
+    @Override
+    public AllSegmentsChange parse(String responseData) throws HttpResponseParserException {
+        try {
+            return Json.fromJson(responseData, AllSegmentsChange.class);
+        } catch (JsonSyntaxException e) {
+            throw new HttpResponseParserException("Syntax error parsing my large segments http response: " + e.getLocalizedMessage());
+        } catch (Exception e) {
+            throw new HttpResponseParserException("Unknown error parsing my large segments http response: " + e.getLocalizedMessage());
+        }
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTask.java b/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTask.java
index 4084898e3..6965a249f 100644
--- a/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTask.java
+++ b/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTask.java
@@ -1,5 +1,7 @@
 package io.split.android.client.service.mysegments;
 
+import static io.split.android.client.utils.Utils.checkNotNull;
+
 import androidx.annotation.NonNull;
 
 import io.split.android.client.service.executor.SplitTask;
@@ -7,21 +9,25 @@
 import io.split.android.client.service.executor.SplitTaskType;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 
-import static io.split.android.client.utils.Utils.checkNotNull;
-
 public class LoadMySegmentsTask implements SplitTask {
 
     private final MySegmentsStorage mMySegmentsStorage;
+    private final MySegmentsStorage mMyLargeSegmentsStorage;
+    private final SplitTaskType mSplitTaskType;
 
-    public LoadMySegmentsTask(@NonNull MySegmentsStorage mySegmentsStorage) {
-
+    public LoadMySegmentsTask(@NonNull MySegmentsStorage mySegmentsStorage,
+                              @NonNull MySegmentsStorage myLargeSegmentsStorage,
+                              @NonNull LoadMySegmentsTaskConfig config) {
         mMySegmentsStorage = checkNotNull(mySegmentsStorage);
+        mMyLargeSegmentsStorage = checkNotNull(myLargeSegmentsStorage);
+        mSplitTaskType = config.getTaskType();
     }
 
     @Override
     @NonNull
     public SplitTaskExecutionInfo execute() {
         mMySegmentsStorage.loadLocal();
-        return SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SYGMENTS);
+        mMyLargeSegmentsStorage.loadLocal();
+        return SplitTaskExecutionInfo.success(mSplitTaskType);
     }
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfig.java b/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfig.java
new file mode 100644
index 000000000..451d58dad
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfig.java
@@ -0,0 +1,22 @@
+package io.split.android.client.service.mysegments;
+
+import io.split.android.client.service.executor.SplitTaskType;
+
+public class LoadMySegmentsTaskConfig {
+
+    private static final LoadMySegmentsTaskConfig LOAD_MY_SEGMENTS_TASK_CONFIG = new LoadMySegmentsTaskConfig(SplitTaskType.LOAD_LOCAL_MY_SEGMENTS);
+
+    private final SplitTaskType mTaskType;
+
+    private LoadMySegmentsTaskConfig(SplitTaskType taskType) {
+        mTaskType = taskType;
+    }
+
+    public SplitTaskType getTaskType() {
+        return mTaskType;
+    }
+
+    public static LoadMySegmentsTaskConfig get() {
+        return LOAD_MY_SEGMENTS_TASK_CONFIG;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentUpdateParams.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentUpdateParams.java
new file mode 100644
index 000000000..2372cabeb
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentUpdateParams.java
@@ -0,0 +1,26 @@
+package io.split.android.client.service.mysegments;
+
+public class MySegmentUpdateParams {
+
+    private final Long mSyncDelay;
+    private final Long mTargetSegmentsCn;
+    private final Long mTargetLargeSegmentsCn;
+
+    public MySegmentUpdateParams(Long syncDelay, Long targetSegmentsCn, Long targetLargeSegmentsCn) {
+        mSyncDelay = syncDelay;
+        mTargetSegmentsCn = targetSegmentsCn;
+        mTargetLargeSegmentsCn = targetLargeSegmentsCn;
+    }
+
+    public Long getSyncDelay() {
+        return mSyncDelay;
+    }
+
+    public Long getTargetSegmentsCn() {
+        return mTargetSegmentsCn;
+    }
+
+    public Long getTargetLargeSegmentsCn() {
+        return mTargetLargeSegmentsCn;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsOverwriteTask.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsOverwriteTask.java
deleted file mode 100644
index da18f2f42..000000000
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsOverwriteTask.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package io.split.android.client.service.mysegments;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import io.split.android.client.events.SplitEventsManager;
-import io.split.android.client.events.SplitInternalEvent;
-import io.split.android.client.service.executor.SplitTask;
-import io.split.android.client.service.executor.SplitTaskExecutionInfo;
-import io.split.android.client.service.executor.SplitTaskType;
-import io.split.android.client.service.synchronizer.MySegmentsChangeChecker;
-import io.split.android.client.storage.mysegments.MySegmentsStorage;
-import io.split.android.client.utils.logger.Logger;
-
-import static io.split.android.client.utils.Utils.checkNotNull;
-
-public class MySegmentsOverwriteTask implements SplitTask {
-
-    private final List<String> mMySegments;
-    private final MySegmentsStorage mMySegmentsStorage;
-    private final SplitEventsManager mEventsManager;
-    private MySegmentsChangeChecker mMySegmentsChangeChecker;
-
-    public MySegmentsOverwriteTask(@NonNull MySegmentsStorage mySegmentsStorage,
-                                   List<String> mySegments,
-                                   SplitEventsManager eventsManager) {
-        mMySegmentsStorage = checkNotNull(mySegmentsStorage);
-        mMySegments = mySegments;
-        mEventsManager = eventsManager;
-        mMySegmentsChangeChecker = new MySegmentsChangeChecker();
-    }
-
-    @Override
-    @NonNull
-    public SplitTaskExecutionInfo execute() {
-        try {
-            if (mMySegments == null) {
-                logError("My segment list could not be null.");
-                return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_OVERWRITE);
-            }
-            List<String> oldSegments = new ArrayList(mMySegmentsStorage.getAll());
-            if(mMySegmentsChangeChecker.mySegmentsHaveChanged(oldSegments, mMySegments)) {
-                mMySegmentsStorage.set(mMySegments);
-                mEventsManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
-            }
-        } catch (Exception e) {
-            logError("Unknown error while overwriting my segments: " + e.getLocalizedMessage());
-            return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_OVERWRITE);
-        }
-        Logger.d("My Segments have been overwritten");
-        return SplitTaskExecutionInfo.success(SplitTaskType.MY_SEGMENTS_OVERWRITE);
-    }
-
-    private void logError(String message) {
-        Logger.e("Error while executing my segments overwrite task: " + message);
-    }
-
-    @VisibleForTesting
-    public void setChangesChecker(MySegmentsChangeChecker changesChecker) {
-        mMySegmentsChangeChecker = changesChecker;
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsResponseParser.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsResponseParser.java
deleted file mode 100644
index 63725f7f0..000000000
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsResponseParser.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.split.android.client.service.mysegments;
-
-import com.google.gson.JsonSyntaxException;
-import com.google.gson.reflect.TypeToken;
-
-import java.lang.reflect.Type;
-import java.util.List;
-import java.util.Map;
-
-import io.split.android.client.dtos.MySegment;
-import io.split.android.client.service.http.HttpResponseParser;
-import io.split.android.client.service.http.HttpResponseParserException;
-import io.split.android.client.utils.Json;
-
-public class MySegmentsResponseParser implements HttpResponseParser<List<MySegment>> {
-
-    static final private Type MY_SEGMENTS_RESPONSE_TYPE
-            = new TypeToken<Map<String, List<MySegment>>>() {
-    }.getType();
-
-    @Override
-    public List<MySegment> parse(String responseData) throws HttpResponseParserException {
-        try {
-            Map<String, List<MySegment>> parsedResponse = Json.fromJson(responseData, MY_SEGMENTS_RESPONSE_TYPE);
-            return parsedResponse.get("mySegments");
-        } catch (JsonSyntaxException e) {
-            throw new HttpResponseParserException("Syntax error parsing my segments http response: " + e.getLocalizedMessage());
-        } catch (Exception e) {
-            throw new HttpResponseParserException("Unknown error parsing my segments http response: " + e.getLocalizedMessage());
-        }
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTask.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTask.java
index cb8e9c07f..2158985aa 100644
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTask.java
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTask.java
@@ -1,52 +1,116 @@
 package io.split.android.client.service.mysegments;
 
+import static io.split.android.client.service.ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_WAIT;
 import static io.split.android.client.utils.Utils.checkNotNull;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEventsManager;
 import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.network.SplitHttpHeadersBuilder;
+import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskType;
 import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.service.http.HttpFetcherException;
 import io.split.android.client.service.http.HttpStatus;
+import io.split.android.client.service.sseclient.BackoffCounter;
+import io.split.android.client.service.sseclient.ReconnectBackoffCounter;
 import io.split.android.client.service.synchronizer.MySegmentsChangeChecker;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 import io.split.android.client.telemetry.model.OperationType;
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
+import io.split.android.client.utils.Utils;
 import io.split.android.client.utils.logger.Logger;
 
 public class MySegmentsSyncTask implements SplitTask {
 
-    private final HttpFetcher<List<MySegment>> mMySegmentsFetcher;
+    private static final String TILL_PARAM = "till";
+
+    private final HttpFetcher<AllSegmentsChange> mMySegmentsFetcher;
+
     private final MySegmentsStorage mMySegmentsStorage;
-    private final boolean mAvoidCache;
+    private final MySegmentsStorage mMyLargeSegmentsStorage;
     private final SplitEventsManager mEventsManager;
     private final MySegmentsChangeChecker mMySegmentsChangeChecker;
     private final TelemetryRuntimeProducer mTelemetryRuntimeProducer;
+    private final BackoffCounter mBackoffCounter;
+
+    private final SplitTaskType mTaskType;
+    private final SplitInternalEvent mUpdateEvent;
+    private final SplitInternalEvent mFetchedEvent;
+    private final OperationType mTelemetryOperationType;
+
+    private final boolean mAvoidCache;
+    @Nullable
+    private final Long mTargetSegmentsChangeNumber;
+    @Nullable
+    private final Long mTargetLargeSegmentsChangeNumber;
+    private final int mOnDemandFetchBackoffMaxRetries;
+
+    public MySegmentsSyncTask(@NonNull HttpFetcher<AllSegmentsChange> mySegmentsFetcher,
+                              @NonNull MySegmentsStorage mySegmentsStorage,
+                              @NonNull MySegmentsStorage myLargeSegmentsStorage,
+                              boolean avoidCache,
+                              SplitEventsManager eventsManager,
+                              @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
+                              @NonNull MySegmentsSyncTaskConfig config,
+                              @Nullable Long targetSegmentsChangeNumber,
+                              @Nullable Long targetLargeSegmentsChangeNumber) {
+        this(mySegmentsFetcher,
+                mySegmentsStorage,
+                myLargeSegmentsStorage,
+                avoidCache,
+                eventsManager,
+                new MySegmentsChangeChecker(),
+                telemetryRuntimeProducer,
+                config,
+                targetSegmentsChangeNumber,
+                targetLargeSegmentsChangeNumber,
+                new ReconnectBackoffCounter(1, ON_DEMAND_FETCH_BACKOFF_MAX_WAIT),
+                ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
+    }
 
-    public MySegmentsSyncTask(@NonNull HttpFetcher<List<MySegment>> mySegmentsFetcher,
+    @VisibleForTesting
+    public MySegmentsSyncTask(@NonNull HttpFetcher<AllSegmentsChange> mySegmentsFetcher,
                               @NonNull MySegmentsStorage mySegmentsStorage,
+                              @NonNull MySegmentsStorage myLargeSegmentsStorage,
                               boolean avoidCache,
                               SplitEventsManager eventsManager,
-                              @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer) {
+                              MySegmentsChangeChecker mySegmentsChangeChecker,
+                              @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
+                              @NonNull MySegmentsSyncTaskConfig config,
+                              @Nullable Long targetSegmentsChangeNumber,
+                              @Nullable Long targetLargeSegmentsChangeNumber,
+                              BackoffCounter backoffCounter,
+                              int onDemandFetchBackoffMaxRetries) {
         mMySegmentsFetcher = checkNotNull(mySegmentsFetcher);
         mMySegmentsStorage = checkNotNull(mySegmentsStorage);
+        mMyLargeSegmentsStorage = checkNotNull(myLargeSegmentsStorage);
         mAvoidCache = avoidCache;
         mEventsManager = eventsManager;
-        mMySegmentsChangeChecker = new MySegmentsChangeChecker();
+        mMySegmentsChangeChecker = mySegmentsChangeChecker;
         mTelemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
+        mTaskType = config.getTaskType();
+        mUpdateEvent = config.getUpdateEvent();
+        mFetchedEvent = config.getFetchedEvent();
+        mTelemetryOperationType = config.getTelemetryOperationType();
+        mTargetSegmentsChangeNumber = targetSegmentsChangeNumber;
+        mTargetLargeSegmentsChangeNumber = targetLargeSegmentsChangeNumber;
+        mBackoffCounter = backoffCounter;
+        mOnDemandFetchBackoffMaxRetries = onDemandFetchBackoffMaxRetries;
     }
 
     @Override
@@ -55,46 +119,138 @@ public SplitTaskExecutionInfo execute() {
         long startTime = System.currentTimeMillis();
         long latency = 0;
         try {
-            List<MySegment> segments = mMySegmentsFetcher.execute(new HashMap<>(), getHeaders());
+            // if target change number is outdated, we don't need to fetch
+            if (targetChangeNumberIsOutdated()) {
+                Logger.v("Target CN is outdated. Skipping membership fetch");
+                return SplitTaskExecutionInfo.success(mTaskType);
+            }
+
+            fetch(mOnDemandFetchBackoffMaxRetries);
 
             long now = System.currentTimeMillis();
             latency = now - startTime;
-            List<String> oldSegments = new ArrayList<>(mMySegmentsStorage.getAll());
-            List<String> mySegments = getNameList(segments);
-            mMySegmentsStorage.set(mySegments);
 
-            mTelemetryRuntimeProducer.recordSuccessfulSync(OperationType.MY_SEGMENT, now);
-            fireMySegmentsUpdatedIfNeeded(oldSegments, mySegments);
+            mTelemetryRuntimeProducer.recordSuccessfulSync(mTelemetryOperationType, now);
         } catch (HttpFetcherException e) {
-            logError("Network error while retrieving my segments: " + e.getLocalizedMessage());
-            mTelemetryRuntimeProducer.recordSyncError(OperationType.MY_SEGMENT, e.getHttpStatus());
+            logError("Network error while retrieving memberships: " + e.getLocalizedMessage());
+            mTelemetryRuntimeProducer.recordSyncError(mTelemetryOperationType, e.getHttpStatus());
 
             if (HttpStatus.isNotRetryable(HttpStatus.fromCode(e.getHttpStatus()))) {
-                return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_SYNC,
+                return SplitTaskExecutionInfo.error(mTaskType,
                         Collections.singletonMap(SplitTaskExecutionInfo.DO_NOT_RETRY, true));
             }
 
-            return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_SYNC);
+            return SplitTaskExecutionInfo.error(mTaskType);
         } catch (Exception e) {
-            logError("Unknown error while retrieving my segments: " + e.getLocalizedMessage());
-            return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_SYNC);
+            logError("Unknown error while retrieving memberships: " + e.getLocalizedMessage());
+            return SplitTaskExecutionInfo.error(mTaskType);
         } finally {
-            mTelemetryRuntimeProducer.recordSyncLatency(OperationType.MY_SEGMENT, latency);
+            mTelemetryRuntimeProducer.recordSyncLatency(mTelemetryOperationType, latency);
         }
         Logger.d("My Segments have been updated");
-        return SplitTaskExecutionInfo.success(SplitTaskType.MY_SEGMENTS_SYNC);
+        return SplitTaskExecutionInfo.success(mTaskType);
     }
 
-    private void logError(String message) {
-        Logger.e("Error while executing my segments sync task: " + message);
+    private boolean targetChangeNumberIsOutdated() {
+        // In case both targets are present, both CN in storage should be newer for the targets to be considered outdated
+        if (mTargetSegmentsChangeNumber != null && mTargetLargeSegmentsChangeNumber != null) {
+            return isTargetOutdated(mTargetSegmentsChangeNumber, mMySegmentsStorage.getChangeNumber()) &&
+                    isTargetOutdated(mTargetLargeSegmentsChangeNumber, mMyLargeSegmentsStorage.getChangeNumber());
+        }
+
+        // If only LS target is set, there's no need to check MS storage CN
+        if (mTargetLargeSegmentsChangeNumber != null) {
+            return isTargetOutdated(mTargetLargeSegmentsChangeNumber, mMyLargeSegmentsStorage.getChangeNumber());
+        }
+
+        // If only MS target is set, there's no need to check LS storage CN
+        if (mTargetSegmentsChangeNumber != null) {
+            return isTargetOutdated(mTargetSegmentsChangeNumber, mMySegmentsStorage.getChangeNumber());
+        }
+
+        // If no targets are set, consider it not outdated
+        return false;
+    }
+
+    private boolean isTargetOutdated(@Nullable Long targetChangeNumber, long storageChangeNumber) {
+        long target = Utils.getOrDefault(targetChangeNumber, -1L);
+        return target < storageChangeNumber;
     }
 
-    private List<String> getNameList(List<MySegment> mySegments) {
-        List<String> nameList = new ArrayList<String>();
-        for (MySegment segment : mySegments) {
-            nameList.add(segment.name);
+    private void fetch(int initialRetries) throws HttpFetcherException, InterruptedException {
+        int remainingRetries = initialRetries;
+        mBackoffCounter.resetCounter();
+        while (remainingRetries > 0) {
+            AllSegmentsChange response = mMySegmentsFetcher.execute(getParams(false), getHeaders());
+            if (response == null) {
+                throw new HttpFetcherException("", "Response is null");
+            }
+
+            if (isStaleResponse(response)) {
+                Logger.d("Retrying memberships fetch due to change number mismatch");
+                long waitMillis = TimeUnit.SECONDS.toMillis(mBackoffCounter.getNextRetryTime());
+                Thread.sleep(waitMillis);
+                remainingRetries--;
+            } else {
+                updateStorage(response);
+                return;
+            }
         }
-        return nameList;
+
+        AllSegmentsChange response = mMySegmentsFetcher.execute(getParams(true), getHeaders());
+        if (response == null) {
+            throw new HttpFetcherException("", "Response is null");
+        }
+
+        updateStorage(response);
+    }
+
+    private Map<String, Object> getParams(boolean addTill) {
+        Map<String, Object> params = new HashMap<>();
+        if (addTill) {
+            params.put(TILL_PARAM, Math.max(
+                    Utils.getOrDefault(mTargetSegmentsChangeNumber, -1L),
+                    Utils.getOrDefault(mTargetLargeSegmentsChangeNumber, -1L)));
+        }
+
+        return params;
+    }
+
+    private boolean isStaleResponse(@NonNull AllSegmentsChange response) {
+        boolean segmentsTargetMatched = targetMatched(mTargetSegmentsChangeNumber, response.getSegmentsChange());
+        boolean largeSegmentsTargetMatched = targetMatched(mTargetLargeSegmentsChangeNumber, response.getLargeSegmentsChange());
+
+        return !segmentsTargetMatched || !largeSegmentsTargetMatched;
+    }
+
+    private boolean targetMatched(@Nullable Long targetChangeNumber, SegmentsChange change) {
+        Long target = Utils.getOrDefault(targetChangeNumber, -1L);
+        return target == -1 ||
+                change == null ||
+                change.getChangeNumber() == null ||
+                change.getChangeNumber() != null && target <= change.getChangeNumber();
+    }
+
+    private void updateStorage(AllSegmentsChange response) {
+        UpdateSegmentsResult segmentsResult = updateSegments(response.getSegmentsChange(), mMySegmentsStorage);
+        UpdateSegmentsResult largeSegmentsResult = updateSegments(response.getLargeSegmentsChange(), mMyLargeSegmentsStorage);
+        fireMySegmentsUpdatedIfNeeded(segmentsResult, largeSegmentsResult);
+    }
+
+    @NonNull
+    private static UpdateSegmentsResult updateSegments(SegmentsChange segmentsChange, MySegmentsStorage storage) {
+        List<String> oldSegments = new ArrayList<>();
+        List<String> mySegments = new ArrayList<>();
+        if (segmentsChange != null) {
+            oldSegments = new ArrayList<>(storage.getAll());
+            mySegments = segmentsChange.getNames();
+            storage.set(segmentsChange);
+        }
+        return new UpdateSegmentsResult(oldSegments, mySegments);
+    }
+
+    private void logError(String message) {
+        Logger.e("Error while executing memberships sync task: " + message);
     }
 
     private @Nullable Map<String, String> getHeaders() {
@@ -104,18 +260,43 @@ private List<String> getNameList(List<MySegment> mySegments) {
         return null;
     }
 
-    private void fireMySegmentsUpdatedIfNeeded(List<String> oldSegments, List<String> newSegments) {
+    private void fireMySegmentsUpdatedIfNeeded(UpdateSegmentsResult segmentsResult, UpdateSegmentsResult largeSegmentsResult) {
         if (mEventsManager == null) {
             return;
         }
-        mEventsManager.notifyInternalEvent(getInternalEvent(oldSegments, newSegments));
+
+        // MY_SEGMENTS_UPDATED event when segments have changed
+        boolean segmentsHaveChanged = mMySegmentsChangeChecker.mySegmentsHaveChanged(segmentsResult.oldSegments, segmentsResult.newSegments);
+        boolean largeSegmentsHaveChanged = mMySegmentsChangeChecker.mySegmentsHaveChanged(largeSegmentsResult.oldSegments, largeSegmentsResult.newSegments);
+
+        if (segmentsHaveChanged) {
+            Logger.v("New segments: " + segmentsResult.newSegments);
+        }
+
+        if (largeSegmentsHaveChanged) {
+            Logger.v("New large segments: " + largeSegmentsResult.newSegments);
+        }
+
+        if (segmentsHaveChanged) {
+            mEventsManager.notifyInternalEvent(mUpdateEvent);
+        } else {
+            // MY_LARGE_SEGMENTS_UPDATED event when large segments have changed
+            if (largeSegmentsHaveChanged) {
+                mEventsManager.notifyInternalEvent(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED);
+            } else {
+                // otherwise, MY_SEGMENTS_FETCHED event
+                mEventsManager.notifyInternalEvent(mFetchedEvent);
+            }
+        }
     }
 
-    private SplitInternalEvent getInternalEvent(List<String> oldSegments, List<String> newSegments) {
-        boolean haveChanged = mMySegmentsChangeChecker.mySegmentsHaveChanged(oldSegments, newSegments);
-        if (haveChanged) {
-            return SplitInternalEvent.MY_SEGMENTS_UPDATED;
+    private static class UpdateSegmentsResult {
+        public final List<String> oldSegments;
+        public final List<String> newSegments;
+
+        private UpdateSegmentsResult(List<String> oldSegments, List<String> newSegments) {
+            this.oldSegments = oldSegments;
+            this.newSegments = newSegments;
         }
-        return SplitInternalEvent.MY_SEGMENTS_FETCHED;
     }
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfig.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfig.java
new file mode 100644
index 000000000..210fb2d4e
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfig.java
@@ -0,0 +1,51 @@
+package io.split.android.client.service.mysegments;
+
+import androidx.annotation.NonNull;
+
+import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.executor.SplitTaskType;
+import io.split.android.client.telemetry.model.OperationType;
+
+public class MySegmentsSyncTaskConfig {
+
+    private static final MySegmentsSyncTaskConfig MY_SEGMENTS_TASK_CONFIG = new MySegmentsSyncTaskConfig(
+            SplitTaskType.MY_SEGMENTS_SYNC,
+            SplitInternalEvent.MY_SEGMENTS_UPDATED,
+            SplitInternalEvent.MY_SEGMENTS_FETCHED,
+            OperationType.MY_SEGMENT);
+    private final SplitTaskType mTaskType;
+    private final SplitInternalEvent mUpdateEvent;
+    private final SplitInternalEvent mFetchedEvent;
+    private final OperationType mTelemetryOperationType;
+
+    private MySegmentsSyncTaskConfig(@NonNull SplitTaskType taskType,
+                                     @NonNull SplitInternalEvent updateEvent,
+                                     @NonNull SplitInternalEvent fetchedEvent,
+                                     @NonNull OperationType telemetryOperationType) {
+        mTaskType = taskType;
+        mUpdateEvent = updateEvent;
+        mFetchedEvent = fetchedEvent;
+        mTelemetryOperationType = telemetryOperationType;
+    }
+
+    SplitTaskType getTaskType() {
+        return mTaskType;
+    }
+
+    SplitInternalEvent getUpdateEvent() {
+        return mUpdateEvent;
+    }
+
+    SplitInternalEvent getFetchedEvent() {
+        return mFetchedEvent;
+    }
+
+    OperationType getTelemetryOperationType() {
+        return mTelemetryOperationType;
+    }
+
+    @NonNull
+    public static MySegmentsSyncTaskConfig get() {
+        return MY_SEGMENTS_TASK_CONFIG;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactory.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactory.java
index 72576bc0f..37ab55373 100644
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactory.java
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactory.java
@@ -1,14 +1,14 @@
 package io.split.android.client.service.mysegments;
 
-import java.util.List;
+import java.util.Set;
 
 public interface MySegmentsTaskFactory {
 
-    MySegmentsSyncTask createMySegmentsSyncTask(boolean avoidCache);
+    MySegmentsSyncTask createMySegmentsSyncTask(boolean avoidCache, Long targetSegmentsCn, Long targetLargeSegmentsCn);
 
     LoadMySegmentsTask createLoadMySegmentsTask();
 
-    MySegmentsOverwriteTask createMySegmentsOverwriteTask(List<String> segments);
+    MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber);
 
-    MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, String segmentName);
+    MySegmentsUpdateTask createMyLargeSegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber);
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfiguration.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfiguration.java
index 94884b4c4..9cf4a9147 100644
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfiguration.java
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfiguration.java
@@ -4,39 +4,91 @@
 
 import androidx.annotation.NonNull;
 
-import java.util.List;
-
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.events.SplitEventsManager;
 import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 
 public class MySegmentsTaskFactoryConfiguration {
 
-    private final HttpFetcher<List<MySegment>> mHttpFetcher;
-    private final MySegmentsStorage mStorage;
+    private final HttpFetcher<AllSegmentsChange> mHttpFetcher;
+    private final MySegmentsStorage mMySegmentsStorage;
     private final SplitEventsManager mEventsManager;
+    private final MySegmentsSyncTaskConfig mMySegmentsSyncTaskConfig;
+    private final MySegmentsUpdateTaskConfig mMySegmentsUpdateTaskConfig;
+    private final MySegmentsUpdateTaskConfig mMyLargeSegmentsUpdateTaskConfig;
+    private final LoadMySegmentsTaskConfig mLoadMySegmentsTaskConfig;
+    private final MySegmentsStorage mMyLargeSegmentsStorage;
 
-    public MySegmentsTaskFactoryConfiguration(@NonNull HttpFetcher<List<MySegment>> httpFetcher,
-                                              @NonNull MySegmentsStorage storage,
-                                              @NonNull SplitEventsManager eventsManager) {
+    private MySegmentsTaskFactoryConfiguration(@NonNull HttpFetcher<AllSegmentsChange> httpFetcher,
+                                               @NonNull MySegmentsStorage storage,
+                                               @NonNull MySegmentsStorage myLargeSegmentsStorage,
+                                               @NonNull SplitEventsManager eventsManager,
+                                               @NonNull MySegmentsSyncTaskConfig mySegmentsSyncTaskConfig,
+                                               @NonNull MySegmentsUpdateTaskConfig mySegmentsUpdateTaskConfig,
+                                               @NonNull MySegmentsUpdateTaskConfig myLargeSegmentsUpdateTaskConfig,
+                                               @NonNull LoadMySegmentsTaskConfig loadMySegmentsTaskConfig) {
         mHttpFetcher = checkNotNull(httpFetcher);
-        mStorage = checkNotNull(storage);
+        mMySegmentsStorage = checkNotNull(storage);
+        mMyLargeSegmentsStorage = checkNotNull(myLargeSegmentsStorage);
         mEventsManager = checkNotNull(eventsManager);
+        mMySegmentsSyncTaskConfig = checkNotNull(mySegmentsSyncTaskConfig);
+        mMySegmentsUpdateTaskConfig = checkNotNull(mySegmentsUpdateTaskConfig);
+        mMyLargeSegmentsUpdateTaskConfig = checkNotNull(myLargeSegmentsUpdateTaskConfig);
+        mLoadMySegmentsTaskConfig = checkNotNull(loadMySegmentsTaskConfig);
     }
 
     @NonNull
-    public HttpFetcher<List<MySegment>> getHttpFetcher() {
+    public HttpFetcher<AllSegmentsChange> getHttpFetcher() {
         return mHttpFetcher;
     }
 
     @NonNull
-    public MySegmentsStorage getStorage() {
-        return mStorage;
+    public MySegmentsStorage getMySegmentsStorage() {
+        return mMySegmentsStorage;
+    }
+
+    @NonNull
+    public MySegmentsStorage getMyLargeSegmentsStorage() {
+        return mMyLargeSegmentsStorage;
     }
 
     @NonNull
     public SplitEventsManager getEventsManager() {
         return mEventsManager;
     }
+
+    @NonNull
+    public MySegmentsSyncTaskConfig getMySegmentsSyncTaskConfig() {
+        return mMySegmentsSyncTaskConfig;
+    }
+
+    @NonNull
+    public MySegmentsUpdateTaskConfig getMySegmentsUpdateTaskConfig() {
+        return mMySegmentsUpdateTaskConfig;
+    }
+
+    @NonNull
+    public MySegmentsUpdateTaskConfig getMyLargeSegmentsUpdateTaskConfig() {
+        return mMyLargeSegmentsUpdateTaskConfig;
+    }
+
+    @NonNull
+    public LoadMySegmentsTaskConfig getLoadMySegmentsTaskConfig() {
+        return mLoadMySegmentsTaskConfig;
+    }
+
+    public static MySegmentsTaskFactoryConfiguration get(@NonNull HttpFetcher<AllSegmentsChange> httpFetcher,
+                                                         @NonNull MySegmentsStorage mySegmentsStorage,
+                                                         @NonNull MySegmentsStorage myLargeSegmentsStorage,
+                                                         @NonNull SplitEventsManager eventsManager) {
+        return new MySegmentsTaskFactoryConfiguration(httpFetcher,
+                mySegmentsStorage,
+                myLargeSegmentsStorage,
+                eventsManager,
+                MySegmentsSyncTaskConfig.get(),
+                MySegmentsUpdateTaskConfig.getForMySegments(),
+                MySegmentsUpdateTaskConfig.getForMyLargeSegments(),
+                LoadMySegmentsTaskConfig.get());
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryImpl.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryImpl.java
index 934139cf0..93615ccd8 100644
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryImpl.java
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryImpl.java
@@ -4,7 +4,7 @@
 
 import androidx.annotation.NonNull;
 
-import java.util.List;
+import java.util.Set;
 
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
 
@@ -20,26 +20,30 @@ public MySegmentsTaskFactoryImpl(@NonNull MySegmentsTaskFactoryConfiguration con
     }
 
     @Override
-    public MySegmentsSyncTask createMySegmentsSyncTask(boolean avoidCache) {
+    public MySegmentsSyncTask createMySegmentsSyncTask(boolean avoidCache, Long targetSegmentsCn, Long targetLargeSegmentsCn) {
         return new MySegmentsSyncTask(mConfiguration.getHttpFetcher(),
-                mConfiguration.getStorage(),
+                mConfiguration.getMySegmentsStorage(),
+                mConfiguration.getMyLargeSegmentsStorage(),
                 avoidCache,
                 mConfiguration.getEventsManager(),
-                mTelemetryRuntimeProducer);
+                mTelemetryRuntimeProducer,
+                mConfiguration.getMySegmentsSyncTaskConfig(),
+                targetSegmentsCn,
+                targetLargeSegmentsCn);
     }
 
     @Override
     public LoadMySegmentsTask createLoadMySegmentsTask() {
-        return new LoadMySegmentsTask(mConfiguration.getStorage());
+        return new LoadMySegmentsTask(mConfiguration.getMySegmentsStorage(), mConfiguration.getMyLargeSegmentsStorage(), mConfiguration.getLoadMySegmentsTaskConfig());
     }
 
     @Override
-    public MySegmentsOverwriteTask createMySegmentsOverwriteTask(List<String> segments) {
-        return new MySegmentsOverwriteTask(mConfiguration.getStorage(), segments, mConfiguration.getEventsManager());
+    public MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber) {
+        return new MySegmentsUpdateTask(mConfiguration.getMySegmentsStorage(), add, segmentNames, changeNumber, mConfiguration.getEventsManager(), mTelemetryRuntimeProducer, mConfiguration.getMySegmentsUpdateTaskConfig());
     }
 
     @Override
-    public MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, String segmentName) {
-        return new MySegmentsUpdateTask(mConfiguration.getStorage(), add, segmentName, mConfiguration.getEventsManager(), mTelemetryRuntimeProducer);
+    public MySegmentsUpdateTask createMyLargeSegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber) {
+        return new MySegmentsUpdateTask(mConfiguration.getMyLargeSegmentsStorage(), add, segmentNames, changeNumber, mConfiguration.getEventsManager(), mTelemetryRuntimeProducer, mConfiguration.getMyLargeSegmentsUpdateTaskConfig());
     }
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTask.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTask.java
index 3f7cbc3a1..3f8dc1260 100644
--- a/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTask.java
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTask.java
@@ -1,10 +1,12 @@
 package io.split.android.client.service.mysegments;
 
+import static io.split.android.client.utils.Utils.checkNotNull;
+
 import androidx.annotation.NonNull;
 
-import java.util.ArrayList;
 import java.util.Set;
 
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEventsManager;
 import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.executor.SplitTask;
@@ -15,26 +17,34 @@
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
 import io.split.android.client.utils.logger.Logger;
 
-import static io.split.android.client.utils.Utils.checkNotNull;
-
 public class MySegmentsUpdateTask implements SplitTask {
 
-    private final String mSegmentName;
+    private final Set<String> mSegmentNames;
+    private final Long mChangeNumber;
     private final MySegmentsStorage mMySegmentsStorage;
     private final SplitEventsManager mEventsManager;
     private final boolean mIsAddOperation;
     private final TelemetryRuntimeProducer mTelemetryRuntimeProducer;
+    private final SplitTaskType mTaskType;
+    private final SplitInternalEvent mUpdateEvent;
+    private final UpdatesFromSSEEnum mTelemetrySSEKey;
 
     public MySegmentsUpdateTask(@NonNull MySegmentsStorage mySegmentsStorage,
                                 boolean add,
-                                @NonNull String segmentName,
+                                @NonNull Set<String> segmentName,
+                                Long changeNumber,
                                 @NonNull SplitEventsManager eventsManager,
-                                @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer) {
+                                @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
+                                @NonNull MySegmentsUpdateTaskConfig config) {
         mMySegmentsStorage = checkNotNull(mySegmentsStorage);
-        mSegmentName = checkNotNull(segmentName);
+        mSegmentNames = checkNotNull(segmentName);
+        mChangeNumber = changeNumber == null ? -1 : changeNumber;
         mIsAddOperation = add;
         mEventsManager = checkNotNull(eventsManager);
         mTelemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
+        mTaskType = config.getTaskType();
+        mUpdateEvent = config.getUpdateEvent();
+        mTelemetrySSEKey = config.getTelemetrySSEKey();
     }
 
     @Override
@@ -49,40 +59,51 @@ public SplitTaskExecutionInfo execute() {
     private SplitTaskExecutionInfo add() {
         try {
             Set<String> segments = mMySegmentsStorage.getAll();
-            if (!segments.contains(mSegmentName)) {
-                segments.add(mSegmentName);
+            boolean updateAndNotify = false;
+            for (String segment : mSegmentNames) {
+                if (!segments.contains(segment)) {
+                    updateAndNotify = true;
+                    segments.add(segment);
+                }
+            }
+
+            if (updateAndNotify) {
                 updateAndNotify(segments);
             }
-            mTelemetryRuntimeProducer.recordUpdatesFromSSE(UpdatesFromSSEEnum.MY_SEGMENTS);
+            mTelemetryRuntimeProducer.recordUpdatesFromSSE(mTelemetrySSEKey);
         } catch (Exception e) {
-            logError("Unknown error while adding segment " + mSegmentName + ": " + e.getLocalizedMessage());
-            return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_UPDATE);
+            logError("Unknown error while adding segment " + getSegmentNames() + ": " + e.getLocalizedMessage());
+            return SplitTaskExecutionInfo.error(mTaskType);
         }
-        Logger.d("My Segments have been updated. Added " + mSegmentName);
-        return SplitTaskExecutionInfo.success(SplitTaskType.MY_SEGMENTS_UPDATE);
+        Logger.d("My Segments have been updated. Added " + getSegmentNames());
+        return SplitTaskExecutionInfo.success(mTaskType);
     }
 
     public SplitTaskExecutionInfo remove() {
         try {
             Set<String> segments = mMySegmentsStorage.getAll();
-            if(segments.remove(mSegmentName)) {
+            if (segments.removeAll(mSegmentNames)) {
                 updateAndNotify(segments);
             }
-            mTelemetryRuntimeProducer.recordUpdatesFromSSE(UpdatesFromSSEEnum.MY_SEGMENTS);
+            mTelemetryRuntimeProducer.recordUpdatesFromSSE(mTelemetrySSEKey);
         } catch (Exception e) {
-            logError("Unknown error while removing segment " + mSegmentName + ": " + e.getLocalizedMessage());
-            return SplitTaskExecutionInfo.error(SplitTaskType.MY_SEGMENTS_UPDATE);
+            logError("Unknown error while removing segment " + getSegmentNames() + ": " + e.getLocalizedMessage());
+            return SplitTaskExecutionInfo.error(mTaskType);
         }
-        Logger.d("My Segments have been updated. Removed " + mSegmentName);
-        return SplitTaskExecutionInfo.success(SplitTaskType.MY_SEGMENTS_UPDATE);
+        Logger.d("My Segments have been updated. Removed " + getSegmentNames());
+        return SplitTaskExecutionInfo.success(mTaskType);
     }
 
     private void updateAndNotify(Set<String> segments) {
-        mMySegmentsStorage.set(new ArrayList<>(segments));
-        mEventsManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
+        mMySegmentsStorage.set(SegmentsChange.create(segments, mChangeNumber));
+        mEventsManager.notifyInternalEvent(mUpdateEvent);
     }
 
     private void logError(String message) {
         Logger.e("Error while executing my segments removal task: " + message);
     }
+
+    private String getSegmentNames() {
+        return String.join(",", mSegmentNames);
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfig.java b/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfig.java
new file mode 100644
index 000000000..5a3caf515
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfig.java
@@ -0,0 +1,51 @@
+package io.split.android.client.service.mysegments;
+
+import androidx.annotation.NonNull;
+
+import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.executor.SplitTaskType;
+import io.split.android.client.telemetry.model.streaming.UpdatesFromSSEEnum;
+
+public class MySegmentsUpdateTaskConfig {
+
+    private static final MySegmentsUpdateTaskConfig MY_SEGMENTS_UPDATE_TASK_CONFIG = new MySegmentsUpdateTaskConfig(SplitTaskType.MY_SEGMENTS_UPDATE,
+            SplitInternalEvent.MY_SEGMENTS_UPDATED,
+            UpdatesFromSSEEnum.MY_SEGMENTS);
+    private static final MySegmentsUpdateTaskConfig MY_LARGE_SEGMENTS_UPDATE_TASK_CONFIG = new MySegmentsUpdateTaskConfig(SplitTaskType.MY_LARGE_SEGMENTS_UPDATE,
+            SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED,
+            UpdatesFromSSEEnum.MY_LARGE_SEGMENTS);
+
+    private final SplitTaskType mTaskType;
+    private final SplitInternalEvent mUpdateEvent;
+    private final UpdatesFromSSEEnum mTelemetrySSEKey;
+
+    private MySegmentsUpdateTaskConfig(@NonNull SplitTaskType taskType,
+                                       @NonNull SplitInternalEvent updateEvent,
+                                       @NonNull UpdatesFromSSEEnum telemetrySSEKey) {
+        mTaskType = taskType;
+        mUpdateEvent = updateEvent;
+        mTelemetrySSEKey = telemetrySSEKey;
+    }
+
+    public SplitTaskType getTaskType() {
+        return mTaskType;
+    }
+
+    public SplitInternalEvent getUpdateEvent() {
+        return mUpdateEvent;
+    }
+
+    public UpdatesFromSSEEnum getTelemetrySSEKey() {
+        return mTelemetrySSEKey;
+    }
+
+    @NonNull
+    public static MySegmentsUpdateTaskConfig getForMySegments() {
+        return MY_SEGMENTS_UPDATE_TASK_CONFIG;
+    }
+
+    @NonNull
+    public static MySegmentsUpdateTaskConfig getForMyLargeSegments() {
+        return MY_LARGE_SEGMENTS_UPDATE_TASK_CONFIG;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/splits/SplitChangeProcessor.java b/src/main/java/io/split/android/client/service/splits/SplitChangeProcessor.java
index 6ca3a5c05..2c4fe4545 100644
--- a/src/main/java/io/split/android/client/service/splits/SplitChangeProcessor.java
+++ b/src/main/java/io/split/android/client/service/splits/SplitChangeProcessor.java
@@ -74,7 +74,7 @@ private ProcessedSplitChange buildProcessedSplitChange(List<Split> featureFlags,
             processStrategy.process(activeFeatureFlags, archivedFeatureFlags, featureFlag);
         }
 
-        return new ProcessedSplitChange(activeFeatureFlags, archivedFeatureFlags, changeNumber, System.currentTimeMillis() / 100);
+        return new ProcessedSplitChange(activeFeatureFlags, archivedFeatureFlags, changeNumber, System.currentTimeMillis());
     }
 
     private FeatureFlagProcessStrategy getProcessStrategy(SplitFilter splitFilter) {
diff --git a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java
index 9cf2b8500..f65adb969 100644
--- a/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java
+++ b/src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java
@@ -31,7 +31,6 @@ public class SplitsSyncHelper {
 
     private static final String SINCE_PARAM = "since";
     private static final String TILL_PARAM = "till";
-    private static final int ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES = 10;
     private static final int ON_DEMAND_FETCH_BACKOFF_MAX_WAIT = ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_WAIT;
 
     private final HttpFetcher<SplitChange> mSplitFetcher;
@@ -69,20 +68,20 @@ public SplitsSyncHelper(@NonNull HttpFetcher<SplitChange> splitFetcher,
         mFlagsSpec = flagsSpec;
     }
 
-    public SplitTaskExecutionInfo sync(long till) {
-        return sync(till, false, true, false);
+    public SplitTaskExecutionInfo sync(long till, int onDemandFetchBackoffMaxRetries) {
+        return sync(till, false, true, false, onDemandFetchBackoffMaxRetries);
     }
 
-    public SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean resetChangeNumber) {
-        return sync(till, clearBeforeUpdate, false, resetChangeNumber);
+    public SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
+        return sync(till, clearBeforeUpdate, false, resetChangeNumber, onDemandFetchBackoffMaxRetries);
     }
 
-    private SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean resetChangeNumber) {
+    private SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
         try {
-            boolean successfulSync = attemptSplitSync(till, clearBeforeUpdate, avoidCache, false, resetChangeNumber);
+            boolean successfulSync = attemptSplitSync(till, clearBeforeUpdate, avoidCache, false, resetChangeNumber, onDemandFetchBackoffMaxRetries);
 
             if (!successfulSync) {
-                attemptSplitSync(till, clearBeforeUpdate, avoidCache, true, resetChangeNumber);
+                attemptSplitSync(till, clearBeforeUpdate, avoidCache, true, resetChangeNumber, onDemandFetchBackoffMaxRetries);
             }
         } catch (HttpFetcherException e) {
             logError("Network error while fetching feature flags" + e.getLocalizedMessage());
@@ -113,10 +112,11 @@ private SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolea
      * @param clearBeforeUpdate whether to clear splits storage before updating it
      * @param avoidCache        whether to send no-cache header to api
      * @param withCdnBypass     whether to add additional query param to bypass CDN
+     * @param onDemandFetchBackoffMaxRetries max backoff retries for CDN bypass
      * @return whether sync finished successfully
      */
-    private boolean attemptSplitSync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnBypass, boolean resetChangeNumber) throws Exception {
-        int remainingAttempts = ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES;
+    private boolean attemptSplitSync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnBypass, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) throws Exception {
+        int remainingAttempts = onDemandFetchBackoffMaxRetries;
         mBackoffCounter.resetCounter();
         while (true) {
             remainingAttempts--;
@@ -145,10 +145,11 @@ private boolean attemptSplitSync(long till, boolean clearBeforeUpdate, boolean a
     private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnByPass, boolean resetChangeNumber) throws Exception {
         boolean shouldClearBeforeUpdate = clearBeforeUpdate;
 
+        long newTill = till;
         while (true) {
             long changeNumber = (resetChangeNumber) ? -1 : mSplitsStorage.getTill();
             resetChangeNumber = false;
-            if (till < changeNumber) {
+            if (newTill < changeNumber) {
                 return changeNumber;
             }
 
@@ -156,6 +157,7 @@ private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache
             updateStorage(shouldClearBeforeUpdate, splitChange);
             shouldClearBeforeUpdate = false;
 
+            newTill = splitChange.till;
             if (splitChange.till == splitChange.since) {
                 return splitChange.till;
             }
@@ -184,14 +186,14 @@ private void updateStorage(boolean clearBeforeUpdate, SplitChange splitChange) {
     }
 
     public boolean cacheHasExpired(long storedChangeNumber, long updateTimestamp, long cacheExpirationInSeconds) {
-        long elapsed = now() - updateTimestamp;
+        long elapsed = now() - TimeUnit.MILLISECONDS.toSeconds(updateTimestamp);
         return storedChangeNumber > -1
                 && updateTimestamp > 0
                 && (elapsed > cacheExpirationInSeconds);
     }
 
     private long now() {
-        return System.currentTimeMillis() / 1000;
+        return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
     }
 
     private void logError(String message) {
diff --git a/src/main/java/io/split/android/client/service/splits/SplitsSyncTask.java b/src/main/java/io/split/android/client/service/splits/SplitsSyncTask.java
index f3ed17489..1fe6d6cec 100644
--- a/src/main/java/io/split/android/client/service/splits/SplitsSyncTask.java
+++ b/src/main/java/io/split/android/client/service/splits/SplitsSyncTask.java
@@ -7,6 +7,7 @@
 
 import io.split.android.client.events.ISplitEventsManager;
 import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
@@ -27,6 +28,7 @@ public class SplitsSyncTask implements SplitTask {
     private final ISplitEventsManager mEventsManager; // Should only be null on background sync
     private final SplitsChangeChecker mChangeChecker;
     private final TelemetryRuntimeProducer mTelemetryRuntimeProducer;
+    private final int mOnDemandFetchBackoffMaxRetries;
 
     public static SplitsSyncTask build(@NonNull SplitsSyncHelper splitsSyncHelper,
                                        @NonNull SplitsStorage splitsStorage,
@@ -35,7 +37,7 @@ public static SplitsSyncTask build(@NonNull SplitsSyncHelper splitsSyncHelper,
                                        String splitsFilterQueryString,
                                        @NonNull ISplitEventsManager eventsManager,
                                        @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer) {
-        return new SplitsSyncTask(splitsSyncHelper, splitsStorage, checkCacheExpiration, cacheExpirationInSeconds, splitsFilterQueryString, telemetryRuntimeProducer, eventsManager);
+        return new SplitsSyncTask(splitsSyncHelper, splitsStorage, checkCacheExpiration, cacheExpirationInSeconds, splitsFilterQueryString, telemetryRuntimeProducer, eventsManager, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
     }
 
     public static SplitTask buildForBackground(@NonNull SplitsSyncHelper splitsSyncHelper,
@@ -44,7 +46,7 @@ public static SplitTask buildForBackground(@NonNull SplitsSyncHelper splitsSyncH
                                                     long cacheExpirationInSeconds,
                                                     String splitsFilterQueryString,
                                                     @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer) {
-        return new SplitsSyncTask(splitsSyncHelper, splitsStorage, checkCacheExpiration, cacheExpirationInSeconds, splitsFilterQueryString, telemetryRuntimeProducer, null);
+        return new SplitsSyncTask(splitsSyncHelper, splitsStorage, checkCacheExpiration, cacheExpirationInSeconds, splitsFilterQueryString, telemetryRuntimeProducer, null, 1);
     }
 
     private SplitsSyncTask(@NonNull SplitsSyncHelper splitsSyncHelper,
@@ -53,7 +55,8 @@ private SplitsSyncTask(@NonNull SplitsSyncHelper splitsSyncHelper,
                            long cacheExpirationInSeconds,
                            String splitsFilterQueryString,
                            @NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
-                           @Nullable ISplitEventsManager eventsManager) {
+                           @Nullable ISplitEventsManager eventsManager,
+                           int onDemandFetchBackoffMaxRetries) {
 
         mSplitsStorage = checkNotNull(splitsStorage);
         mSplitsSyncHelper = checkNotNull(splitsSyncHelper);
@@ -63,6 +66,7 @@ private SplitsSyncTask(@NonNull SplitsSyncHelper splitsSyncHelper,
         mEventsManager = eventsManager;
         mChangeChecker = new SplitsChangeChecker();
         mTelemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
+        mOnDemandFetchBackoffMaxRetries = onDemandFetchBackoffMaxRetries;
     }
 
     @Override
@@ -84,7 +88,7 @@ public SplitTaskExecutionInfo execute() {
         long startTime = System.currentTimeMillis();
         SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(storedChangeNumber,
                 splitsFilterHasChanged || shouldClearExpiredCache,
-                splitsFilterHasChanged);
+                splitsFilterHasChanged || shouldClearExpiredCache, mOnDemandFetchBackoffMaxRetries);
         mTelemetryRuntimeProducer.recordSyncLatency(OperationType.SPLITS, System.currentTimeMillis() - startTime);
 
         if (result.getStatus() == SplitTaskExecutionStatus.SUCCESS) {
diff --git a/src/main/java/io/split/android/client/service/splits/SplitsUpdateTask.java b/src/main/java/io/split/android/client/service/splits/SplitsUpdateTask.java
index 9b0b76313..1deca7b5c 100644
--- a/src/main/java/io/split/android/client/service/splits/SplitsUpdateTask.java
+++ b/src/main/java/io/split/android/client/service/splits/SplitsUpdateTask.java
@@ -7,6 +7,7 @@
 
 import io.split.android.client.events.ISplitEventsManager;
 import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
@@ -50,7 +51,7 @@ public SplitTaskExecutionInfo execute() {
             return SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC);
         }
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(mChangeNumber);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(mChangeNumber, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
         if (result.getStatus() == SplitTaskExecutionStatus.SUCCESS) {
             SplitInternalEvent event = SplitInternalEvent.SPLITS_FETCHED;
             if (mChangeChecker.splitsHaveChanged(storedChangeNumber, mSplitsStorage.getTill())) {
diff --git a/src/main/java/io/split/android/client/service/sseclient/SseJwtParser.java b/src/main/java/io/split/android/client/service/sseclient/SseJwtParser.java
index 5994429ce..c2ec1ec16 100644
--- a/src/main/java/io/split/android/client/service/sseclient/SseJwtParser.java
+++ b/src/main/java/io/split/android/client/service/sseclient/SseJwtParser.java
@@ -16,13 +16,13 @@
 
 public class SseJwtParser {
 
-    private final static String PUBLISHERS_CHANNEL_METADATA = "channel-metadata:publishers";
-    private final static String PUBLISHERS_CHANNEL_PREFIX = "[?occupancy=metrics.publishers]";
+    private static final String PUBLISHERS_CHANNEL_METADATA = "channel-metadata:publishers";
+    private static final String PUBLISHERS_CHANNEL_PREFIX = "[?occupancy=metrics.publishers]";
 
-    final static Type ALL_TOKEN_TYPE = new TypeToken<Map<String, Object>>() {
+    static final Type ALL_TOKEN_TYPE = new TypeToken<Map<String, Object>>() {
     }.getType();
 
-    final static Type CHANNEL_TYPE = new TypeToken<Map<String, List<String>>>() {
+    private static final Type CHANNEL_TYPE = new TypeToken<Map<String, List<String>>>() {
     }.getType();
 
     public SseJwtToken parse(String rawToken) throws InvalidJwtTokenException {
@@ -64,7 +64,7 @@ public SseJwtToken parse(String rawToken) throws InvalidJwtTokenException {
             Logger.e("Error parsing SSE authentication JWT json " + e.getLocalizedMessage());
             throw new InvalidJwtTokenException();
         } catch (Exception e) {
-            Logger.e("Unknonwn error while parsing SSE authentication JWT: " + e.getLocalizedMessage());
+            Logger.e("Unknown error while parsing SSE authentication JWT: " + e.getLocalizedMessage());
             throw new InvalidJwtTokenException();
         }
 
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/ControlNotification.java b/src/main/java/io/split/android/client/service/sseclient/notifications/ControlNotification.java
index 2f12877ea..a93d87878 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/ControlNotification.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/ControlNotification.java
@@ -1,11 +1,21 @@
 package io.split.android.client.service.sseclient.notifications;
 
+import com.google.gson.annotations.SerializedName;
+
 public class ControlNotification extends IncomingNotification {
     public enum ControlType {
-        STREAMING_RESUMED, STREAMING_DISABLED, STREAMING_PAUSED, STREAMING_RESET
+        @SerializedName("STREAMING_RESUMED")
+        STREAMING_RESUMED,
+        @SerializedName("STREAMING_DISABLED")
+        STREAMING_DISABLED,
+        @SerializedName("STREAMING_PAUSED")
+        STREAMING_PAUSED,
+        @SerializedName("STREAMING_RESET")
+        STREAMING_RESET
     }
 
     @SuppressWarnings("unused")
+    @SerializedName("controlType")
     private ControlType controlType;
 
     public ControlType getControlType() {
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/HashingAlgorithm.java b/src/main/java/io/split/android/client/service/sseclient/notifications/HashingAlgorithm.java
new file mode 100644
index 000000000..1b8049233
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/HashingAlgorithm.java
@@ -0,0 +1,12 @@
+package io.split.android.client.service.sseclient.notifications;
+
+import com.google.gson.annotations.SerializedName;
+
+public enum HashingAlgorithm {
+    @SerializedName("0")
+    NONE,
+    @SerializedName("1")
+    MURMUR3_32,
+    @SerializedName("2")
+    MURMUR3_64
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/IncomingNotificationType.java b/src/main/java/io/split/android/client/service/sseclient/notifications/IncomingNotificationType.java
index 92fe06b84..adf6a1d99 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/IncomingNotificationType.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/IncomingNotificationType.java
@@ -1,6 +1,9 @@
 package io.split.android.client.service.sseclient.notifications;
 
+import com.google.gson.annotations.SerializedName;
+
 public class IncomingNotificationType {
+    @SerializedName(value = "type")
     protected NotificationType type;
 
     public NotificationType getType() {
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/MembershipNotification.java b/src/main/java/io/split/android/client/service/sseclient/notifications/MembershipNotification.java
new file mode 100644
index 000000000..20cfea311
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/MembershipNotification.java
@@ -0,0 +1,71 @@
+package io.split.android.client.service.sseclient.notifications;
+
+import androidx.annotation.Nullable;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.Set;
+
+import io.split.android.client.common.CompressionType;
+
+public class MembershipNotification extends IncomingNotification {
+
+    @SerializedName("cn")
+    private Long changeNumber;
+    @SerializedName("n")
+    private Set<String> names;
+
+    @SerializedName("c")
+    private CompressionType compression;
+    @SerializedName("u")
+    private MySegmentUpdateStrategy updateStrategy;
+    @SerializedName("d")
+    private String data;
+
+    @SerializedName("i")
+    private Long updateIntervalMs;
+    @SerializedName("h")
+    private HashingAlgorithm hashingAlgorithm;
+    @SerializedName("s")
+    private Integer algorithmSeed;
+
+    @Nullable
+    public Long getChangeNumber() {
+        return changeNumber;
+    }
+
+    @Nullable
+    public Set<String> getNames() {
+        return names;
+    }
+
+    @Nullable
+    public CompressionType getCompression() {
+        return compression;
+    }
+
+    @Nullable
+    public MySegmentUpdateStrategy getUpdateStrategy() {
+        return updateStrategy;
+    }
+
+    @Nullable
+    public String getData() {
+        return data;
+    }
+
+    @Nullable
+    public Long getUpdateIntervalMs() {
+        return updateIntervalMs;
+    }
+
+    @Nullable
+    public HashingAlgorithm getHashingAlgorithm() {
+        return hashingAlgorithm;
+    }
+
+    @Nullable
+    public Integer getAlgorithmSeed() {
+        return algorithmSeed;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeNotification.java b/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeNotification.java
deleted file mode 100644
index 1a3cd29dc..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeNotification.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.split.android.client.service.sseclient.notifications;
-
-import java.util.List;
-
-public class MySegmentChangeNotification extends IncomingNotification {
-    private long changeNumber;
-    private boolean includesPayload;
-    private List<String> segmentList;
-
-    public long getChangeNumber() {
-        return changeNumber;
-    }
-
-    public boolean isIncludesPayload() {
-        return includesPayload;
-    }
-
-    public List<String> getSegmentList() {
-        return segmentList;
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeV2Notification.java b/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeV2Notification.java
deleted file mode 100644
index d760a6148..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentChangeV2Notification.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.split.android.client.service.sseclient.notifications;
-
-import androidx.annotation.Nullable;
-
-import com.google.gson.annotations.SerializedName;
-
-import io.split.android.client.common.CompressionType;
-
-public class MySegmentChangeV2Notification extends IncomingNotification {
-
-    private static final String FIELD_UPDATE_STRATEGY = "u";
-    private static final String FIELD_COMPRESSION = "c";
-    private static final String FIELD_DATE = "d";
-    private static final String FIELD_CHANGE_NUMBER = "changeNumber";
-    private static final String FIELD_SEGMENT_NAME = "segmentName";
-
-    @SerializedName(FIELD_CHANGE_NUMBER)
-    private Long changeNumber;
-    @SerializedName(FIELD_SEGMENT_NAME)
-    private String segmentName;
-    @SerializedName(FIELD_COMPRESSION)
-    private CompressionType compression;
-    @SerializedName(FIELD_UPDATE_STRATEGY)
-    private MySegmentUpdateStrategy updateStrategy;
-    @SerializedName(FIELD_DATE)
-    private String data;
-
-    @Nullable
-    public Long getChangeNumber() {
-        return changeNumber;
-    }
-
-    @Nullable
-    public String getSegmentName() {
-        return segmentName;
-    }
-
-    @Nullable
-    public CompressionType getCompression() {
-        return compression;
-    }
-
-    @Nullable
-    public MySegmentUpdateStrategy getUpdateStrategy() {
-        return updateStrategy;
-    }
-
-    @Nullable
-    public String getData() {
-        return data;
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoder.java b/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoder.java
deleted file mode 100644
index 541398ccb..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoder.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package io.split.android.client.service.sseclient.notifications;
-
-import android.util.Base64;
-
-import androidx.annotation.NonNull;
-
-import io.split.android.client.utils.logger.Logger;
-import io.split.android.client.utils.MurmurHash3;
-import io.split.android.client.utils.StringHelper;
-
-public class MySegmentsPayloadDecoder {
-
-    /**
-     * @param matchingKey plain matching key.
-     * @return Base64 encoding of {@param matchingKey} murmur3_32 hash.
-     */
-    @NonNull
-    public String hashUserKeyForMySegmentsV1(String matchingKey) {
-        try {
-            long murmurHash = MurmurHash3.murmurhash3_x86_32(matchingKey, 0, matchingKey.length(), 0);
-            byte[] murmurHashStringBytes = String.valueOf(murmurHash).getBytes(StringHelper.defaultCharset());
-
-            return Base64.encodeToString(murmurHashStringBytes, Base64.NO_WRAP);
-        } catch (Exception exception) {
-            Logger.e("An error occurred when encoding matching key");
-
-            return "";
-        }
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationParser.java b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationParser.java
index 97f63d2d5..3a4dbb8bc 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationParser.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationParser.java
@@ -55,14 +55,6 @@ public SplitKillNotification parseSplitKill(String jsonData) throws JsonSyntaxEx
         return Json.fromJson(jsonData, SplitKillNotification.class);
     }
 
-    public MySegmentChangeNotification parseMySegmentUpdate(String jsonData) throws JsonSyntaxException {
-        return Json.fromJson(jsonData, MySegmentChangeNotification.class);
-    }
-
-    public MySegmentChangeV2Notification parseMySegmentUpdateV2(String jsonData) throws JsonSyntaxException {
-        return Json.fromJson(jsonData, MySegmentChangeV2Notification.class);
-    }
-
     public OccupancyNotification parseOccupancy(String jsonData) throws JsonSyntaxException {
         return Json.fromJson(jsonData, OccupancyNotification.class);
     }
@@ -94,4 +86,14 @@ public String extractUserKeyHashFromChannel(String channel) {
 
         return null;
     }
+
+    @Nullable
+    public MembershipNotification parseMembershipNotification(String jsonData) {
+        try {
+            return Json.fromJson(jsonData, MembershipNotification.class);
+        } catch (Exception e) {
+            Logger.w("Failed to parse membership notification");
+            return null;
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationProcessor.java b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationProcessor.java
index f4b67bec7..6eef5c597 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationProcessor.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationProcessor.java
@@ -3,10 +3,10 @@
 import static io.split.android.client.utils.Utils.checkNotNull;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.google.gson.JsonSyntaxException;
 
-import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -14,7 +14,7 @@
 import io.split.android.client.dtos.Split;
 import io.split.android.client.service.executor.SplitTaskExecutor;
 import io.split.android.client.service.executor.SplitTaskFactory;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessor;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
 import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorRegistry;
 import io.split.android.client.utils.logger.Logger;
 
@@ -24,21 +24,18 @@ public class NotificationProcessor implements MySegmentsNotificationProcessorReg
     private final SplitTaskExecutor mSplitTaskExecutor;
     private final SplitTaskFactory mSplitTaskFactory;
     private final BlockingQueue<SplitsChangeNotification> mSplitsUpdateNotificationsQueue;
-    private final ConcurrentMap<String, MySegmentsNotificationProcessor> mMySegmentsNotificationProcessors;
-    private final MySegmentsPayloadDecoder mMySegmentsPayloadDecoder;
+    private final ConcurrentMap<String, MembershipsNotificationProcessor> mMembershipsNotificationProcessors;
 
     public NotificationProcessor(
             @NonNull SplitTaskExecutor splitTaskExecutor,
             @NonNull SplitTaskFactory splitTaskFactory,
             @NonNull NotificationParser notificationParser,
-            @NonNull BlockingQueue<SplitsChangeNotification> splitsUpdateNotificationsQueue,
-            @NonNull MySegmentsPayloadDecoder mySegmentsPayloadDecoder) {
+            @NonNull BlockingQueue<SplitsChangeNotification> splitsUpdateNotificationsQueue) {
         mSplitTaskExecutor = checkNotNull(splitTaskExecutor);
         mSplitTaskFactory = checkNotNull(splitTaskFactory);
         mNotificationParser = checkNotNull(notificationParser);
         mSplitsUpdateNotificationsQueue = checkNotNull(splitsUpdateNotificationsQueue);
-        mMySegmentsPayloadDecoder = checkNotNull(mySegmentsPayloadDecoder);
-        mMySegmentsNotificationProcessors = new ConcurrentHashMap<>();
+        mMembershipsNotificationProcessors = new ConcurrentHashMap<>();
     }
 
     public void process(IncomingNotification incomingNotification) {
@@ -51,12 +48,9 @@ public void process(IncomingNotification incomingNotification) {
                 case SPLIT_KILL:
                     processSplitKill(mNotificationParser.parseSplitKill(notificationJson));
                     break;
-                case MY_SEGMENTS_UPDATE:
-                    processMySegmentUpdate(mNotificationParser.parseMySegmentUpdate(notificationJson),
-                            mNotificationParser.extractUserKeyHashFromChannel(incomingNotification.getChannel()));
-                    break;
-                case MY_SEGMENTS_UPDATE_V2:
-                    processMySegmentUpdateV2(mNotificationParser.parseMySegmentUpdateV2(notificationJson));
+                case MEMBERSHIPS_MS_UPDATE:
+                case MEMBERSHIPS_LS_UPDATE:
+                    processMembershipsUpdate(mNotificationParser.parseMembershipNotification(notificationJson));
                     break;
                 default:
                     Logger.e("Unknown notification arrived: " + notificationJson);
@@ -71,13 +65,13 @@ public void process(IncomingNotification incomingNotification) {
     }
 
     @Override
-    public void registerMySegmentsProcessor(String matchingKey, MySegmentsNotificationProcessor processor) {
-        mMySegmentsNotificationProcessors.put(matchingKey, processor);
+    public void registerMembershipsNotificationProcessor(String matchingKey, MembershipsNotificationProcessor processor) {
+        mMembershipsNotificationProcessors.put(matchingKey, processor);
     }
 
     @Override
-    public void unregisterMySegmentsProcessor(String matchingKey) {
-        mMySegmentsNotificationProcessors.remove(matchingKey);
+    public void unregisterMembershipsProcessor(String matchingKey) {
+        mMembershipsNotificationProcessors.remove(matchingKey);
     }
 
     private void processSplitUpdate(SplitsChangeNotification notification) {
@@ -94,23 +88,9 @@ private void processSplitKill(SplitKillNotification notification) {
         mSplitsUpdateNotificationsQueue.offer(new SplitsChangeNotification(split.changeNumber));
     }
 
-    private void processMySegmentUpdate(MySegmentChangeNotification notification, String hashedUserKey) {
-        for (Map.Entry<String, MySegmentsNotificationProcessor> processor : mMySegmentsNotificationProcessors.entrySet()) {
-            String encodedProcessorKey = mMySegmentsPayloadDecoder.hashUserKeyForMySegmentsV1(processor.getKey());
-
-            if (encodedProcessorKey == null) {
-                continue;
-            }
-
-            if (encodedProcessorKey.equals(hashedUserKey)) {
-                processor.getValue().processMySegmentsUpdate(notification);
-            }
-        }
-    }
-
-    private void processMySegmentUpdateV2(MySegmentChangeV2Notification notification) {
-        for (MySegmentsNotificationProcessor processor : mMySegmentsNotificationProcessors.values()) {
-            processor.processMySegmentsUpdateV2(notification);
+    private void processMembershipsUpdate(@Nullable MembershipNotification notification) {
+        for (MembershipsNotificationProcessor processor : mMembershipsNotificationProcessors.values()) {
+            processor.process(notification);
         }
     }
 }
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationType.java b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationType.java
index 8a18b89c4..fb38d5859 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationType.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/NotificationType.java
@@ -1,5 +1,21 @@
 package io.split.android.client.service.sseclient.notifications;
 
+import com.google.gson.annotations.SerializedName;
+
 public enum NotificationType {
-    SPLIT_UPDATE, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SPLIT_KILL, CONTROL, OCCUPANCY, ERROR
+    @SerializedName("SPLIT_UPDATE")
+    SPLIT_UPDATE,
+    @SerializedName("SPLIT_KILL")
+    SPLIT_KILL,
+    @SerializedName("CONTROL")
+    CONTROL,
+    @SerializedName("OCCUPANCY")
+    OCCUPANCY,
+    @SerializedName("ERROR")
+    ERROR,
+
+    @SerializedName("MEMBERSHIPS_LS_UPDATE")
+    MEMBERSHIPS_LS_UPDATE,
+    @SerializedName("MEMBERSHIPS_MS_UPDATE")
+    MEMBERSHIPS_MS_UPDATE,
 }
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessor.java b/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessor.java
new file mode 100644
index 000000000..f0ddc93af
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessor.java
@@ -0,0 +1,10 @@
+package io.split.android.client.service.sseclient.notifications.memberships;
+
+import androidx.annotation.Nullable;
+
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
+
+public interface MembershipsNotificationProcessor {
+
+    void process(@Nullable MembershipNotification notification);
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessorImpl.java b/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessorImpl.java
new file mode 100644
index 000000000..a9eb549c3
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/memberships/MembershipsNotificationProcessorImpl.java
@@ -0,0 +1,148 @@
+package io.split.android.client.service.sseclient.notifications.memberships;
+
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+
+import io.split.android.client.common.CompressionType;
+import io.split.android.client.common.CompressionUtilProvider;
+import io.split.android.client.service.executor.SplitTaskExecutor;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
+import io.split.android.client.service.mysegments.MySegmentsUpdateTask;
+import io.split.android.client.service.sseclient.notifications.KeyList;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
+import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
+import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
+import io.split.android.client.service.sseclient.notifications.NotificationParser;
+import io.split.android.client.service.sseclient.notifications.NotificationType;
+import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorConfiguration;
+import io.split.android.client.service.sseclient.notifications.mysegments.SyncDelayCalculator;
+import io.split.android.client.utils.logger.Logger;
+
+public class MembershipsNotificationProcessorImpl implements MembershipsNotificationProcessor {
+
+    private final NotificationParser mNotificationParser;
+    private final SplitTaskExecutor mSplitTaskExecutor;
+    private final MySegmentsV2PayloadDecoder mMySegmentsPayloadDecoder;
+    private final CompressionUtilProvider mCompressionProvider;
+    private final MySegmentsNotificationProcessorConfiguration mConfiguration;
+    private final SyncDelayCalculator mSyncDelayCalculator;
+
+    public MembershipsNotificationProcessorImpl(NotificationParser notificationParser,
+                                                SplitTaskExecutor splitTaskExecutor,
+                                                MySegmentsV2PayloadDecoder mySegmentsPayloadDecoder,
+                                                CompressionUtilProvider compressionProvider,
+                                                MySegmentsNotificationProcessorConfiguration configuration,
+                                                SyncDelayCalculator syncDelayCalculator) {
+        mNotificationParser = notificationParser;
+        mSplitTaskExecutor = splitTaskExecutor;
+        mMySegmentsPayloadDecoder = mySegmentsPayloadDecoder;
+        mCompressionProvider = compressionProvider;
+        mConfiguration = configuration;
+        mSyncDelayCalculator = syncDelayCalculator;
+    }
+
+    @Override
+    public void process(@Nullable MembershipNotification notification) {
+        if (notification == null) {
+            notifyMySegmentRefreshNeeded(mConfiguration.getNotificationsQueue(), 0L, null, null);
+        } else {
+            long syncDelay = mSyncDelayCalculator.calculateSyncDelay(mConfiguration.getUserKey(),
+                    notification.getUpdateIntervalMs(),
+                    notification.getAlgorithmSeed(),
+                    notification.getUpdateStrategy(),
+                    notification.getHashingAlgorithm());
+
+            processUpdate(notification.getType(),
+                    notification.getUpdateStrategy(),
+                    notification.getData(),
+                    notification.getCompression(),
+                    notification.getNames(),
+                    notification.getChangeNumber(),
+                    mConfiguration.getNotificationsQueue(),
+                    syncDelay);
+        }
+    }
+
+    private void processUpdate(NotificationType notificationType, MySegmentUpdateStrategy updateStrategy, String data, CompressionType compression, Set<String> names, Long changeNumber, BlockingQueue<MySegmentUpdateParams> notificationsQueue, long syncDelay) {
+        try {
+            switch (updateStrategy) {
+                case UNBOUNDED_FETCH_REQUEST:
+                    Logger.d("Received Unbounded membership fetch request");
+                    notifyMySegmentRefreshNeeded(notificationsQueue, syncDelay, notificationType, changeNumber);
+                    break;
+                case BOUNDED_FETCH_REQUEST:
+                    Logger.d("Received Bounded membership fetch request");
+                    byte[] keyMap = mMySegmentsPayloadDecoder.decodeAsBytes(data,
+                            mCompressionProvider.get(compression));
+                    executeBoundedFetch(keyMap, syncDelay, notificationType, changeNumber);
+                    break;
+                case KEY_LIST:
+                    Logger.d("Received KeyList membership fetch request");
+                    updateSegments(notificationType, mMySegmentsPayloadDecoder.decodeAsString(data,
+                                    mCompressionProvider.get(compression)),
+                            names, changeNumber);
+                    break;
+                case SEGMENT_REMOVAL:
+                    Logger.d("Received membership removal request");
+                    removeSegment(notificationType, names, changeNumber);
+                    break;
+                default:
+                    notifyMySegmentRefreshNeeded(notificationsQueue, syncDelay, notificationType, changeNumber);
+                    Logger.w("Unknown membership change notification type: " + updateStrategy);
+                    break;
+            }
+        } catch (Exception e) {
+            Logger.e("Executing unbounded fetch because an error has occurred processing my "+(notificationType == NotificationType.MEMBERSHIPS_LS_UPDATE ? "large" : "")+" segment notification: " + e.getLocalizedMessage());
+            notifyMySegmentRefreshNeeded(notificationsQueue, syncDelay, notificationType, changeNumber);
+        }
+    }
+
+    private void notifyMySegmentRefreshNeeded(BlockingQueue<MySegmentUpdateParams> notificationsQueue, long syncDelay, NotificationType notificationType, Long changeNumber) {
+        Long targetSegmentsCn = (notificationType == NotificationType.MEMBERSHIPS_MS_UPDATE) ? changeNumber : null;
+        Long targetLargeSegmentsCn = (notificationType == NotificationType.MEMBERSHIPS_LS_UPDATE) ? changeNumber : null;
+
+        //noinspection ResultOfMethodCallIgnored
+        notificationsQueue.offer(new MySegmentUpdateParams(syncDelay, targetSegmentsCn, targetLargeSegmentsCn));
+    }
+
+    private void removeSegment(NotificationType notificationType, Set<String> segmentNames, Long changeNumber) {
+        // Shouldn't be null, some defensive code here
+        if (segmentNames == null) {
+            return;
+        }
+        MySegmentsUpdateTask task = (notificationType == NotificationType.MEMBERSHIPS_LS_UPDATE) ?
+                mConfiguration.getMySegmentsTaskFactory().createMyLargeSegmentsUpdateTask(false, segmentNames, changeNumber) :
+                mConfiguration.getMySegmentsTaskFactory().createMySegmentsUpdateTask(false, segmentNames, changeNumber);
+        mSplitTaskExecutor.submit(task, null);
+    }
+
+    private void executeBoundedFetch(byte[] keyMap, long syncDelay, NotificationType notificationType, Long changeNumber) {
+        int index = mMySegmentsPayloadDecoder.computeKeyIndex(mConfiguration.getHashedUserKey(), keyMap.length);
+        if (mMySegmentsPayloadDecoder.isKeyInBitmap(keyMap, index)) {
+            Logger.d("Executing Bounded membership fetch request");
+            notifyMySegmentRefreshNeeded(mConfiguration.getNotificationsQueue(), syncDelay, notificationType, changeNumber);
+        }
+    }
+
+    private void updateSegments(NotificationType notificationType, String keyListString, Set<String> segmentNames, Long changeNumber) {
+        // Shouldn't be null, some defensive code here
+        if (segmentNames == null) {
+            return;
+        }
+        KeyList keyList = mNotificationParser.parseKeyList(keyListString);
+        KeyList.Action action = mMySegmentsPayloadDecoder.getKeyListAction(keyList, mConfiguration.getHashedUserKey());
+        boolean actionIsAdd = action != KeyList.Action.REMOVE;
+
+        if (action == KeyList.Action.NONE) {
+            return;
+        }
+
+        boolean largeSegmentsUpdate = notificationType == NotificationType.MEMBERSHIPS_LS_UPDATE;
+        Logger.d("Executing KeyList my "+ (largeSegmentsUpdate ? "large " : "") +"segment fetch request: Adding = " + actionIsAdd);
+        MySegmentsUpdateTask task = largeSegmentsUpdate ? mConfiguration.getMySegmentsTaskFactory().createMyLargeSegmentsUpdateTask(actionIsAdd, segmentNames, changeNumber) :
+                mConfiguration.getMySegmentsTaskFactory().createMySegmentsUpdateTask(actionIsAdd, segmentNames, changeNumber);
+        mSplitTaskExecutor.submit(task, null);
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactory.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactory.java
new file mode 100644
index 000000000..5eb78b8eb
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactory.java
@@ -0,0 +1,8 @@
+package io.split.android.client.service.sseclient.notifications.mysegments;
+
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
+
+public interface MembershipsNotificationProcessorFactory {
+
+    MembershipsNotificationProcessor getProcessor(MySegmentsNotificationProcessorConfiguration configuration);
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactoryImpl.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactoryImpl.java
new file mode 100644
index 000000000..898ea21d9
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MembershipsNotificationProcessorFactoryImpl.java
@@ -0,0 +1,35 @@
+package io.split.android.client.service.sseclient.notifications.mysegments;
+
+import static io.split.android.client.utils.Utils.checkNotNull;
+
+import androidx.annotation.NonNull;
+
+import io.split.android.client.common.CompressionUtilProvider;
+import io.split.android.client.service.executor.SplitTaskExecutor;
+import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
+import io.split.android.client.service.sseclient.notifications.NotificationParser;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessorImpl;
+
+public class MembershipsNotificationProcessorFactoryImpl implements MembershipsNotificationProcessorFactory {
+
+    private final NotificationParser mNotificationParser;
+    private final SplitTaskExecutor mSplitTaskExecutor;
+    private final MySegmentsV2PayloadDecoder mMySegmentsPayloadDecoder;
+    private final CompressionUtilProvider mCompressionProvider;
+
+    public MembershipsNotificationProcessorFactoryImpl(@NonNull NotificationParser notificationParser,
+                                                       @NonNull SplitTaskExecutor splitTaskExecutor,
+                                                       @NonNull MySegmentsV2PayloadDecoder mySegmentsPayloadDecoder,
+                                                       @NonNull CompressionUtilProvider compressionProvider) {
+        mNotificationParser = checkNotNull(notificationParser);
+        mSplitTaskExecutor = checkNotNull(splitTaskExecutor);
+        mMySegmentsPayloadDecoder = checkNotNull(mySegmentsPayloadDecoder);
+        mCompressionProvider = checkNotNull(compressionProvider);
+    }
+
+    @Override
+    public MembershipsNotificationProcessor getProcessor(MySegmentsNotificationProcessorConfiguration configuration) {
+        return new MembershipsNotificationProcessorImpl(mNotificationParser, mSplitTaskExecutor, mMySegmentsPayloadDecoder, mCompressionProvider, configuration, new SyncDelayCalculatorImpl());
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessor.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessor.java
deleted file mode 100644
index 7a3ab01b6..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessor.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.split.android.client.service.sseclient.notifications.mysegments;
-
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
-
-public interface MySegmentsNotificationProcessor {
-
-    void processMySegmentsUpdate(MySegmentChangeNotification notification);
-
-    void processMySegmentsUpdateV2(MySegmentChangeV2Notification notification);
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorConfiguration.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorConfiguration.java
index 19af487ba..2ac0fa4a7 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorConfiguration.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorConfiguration.java
@@ -1,33 +1,43 @@
 package io.split.android.client.service.sseclient.notifications.mysegments;
 
+import static io.split.android.client.utils.Utils.checkNotNull;
+
 import androidx.annotation.NonNull;
 
 import java.math.BigInteger;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
 
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
 
 public class MySegmentsNotificationProcessorConfiguration {
 
     private final MySegmentsTaskFactory mMySegmentsTaskFactory;
-    private final BlockingQueue<MySegmentChangeNotification> mMySegmentUpdateNotificationsQueue;
+    private final BlockingQueue<MySegmentUpdateParams> mNotificationsQueue;
+    private final String mUserKey;
     private final BigInteger mHashedUserKey;
 
     public MySegmentsNotificationProcessorConfiguration(@NonNull MySegmentsTaskFactory mySegmentsTaskFactory,
-                                                        @NonNull BlockingQueue<MySegmentChangeNotification> mySegmentUpdateNotificationsQueue,
+                                                        @NonNull LinkedBlockingDeque<MySegmentUpdateParams> mySegmentUpdateNotificationsQueue,
+                                                        @NonNull String userKey,
                                                         @NonNull BigInteger hashedUserKey) {
-        mMySegmentsTaskFactory = mySegmentsTaskFactory;
-        mMySegmentUpdateNotificationsQueue = mySegmentUpdateNotificationsQueue;
-        mHashedUserKey = hashedUserKey;
+        mMySegmentsTaskFactory = checkNotNull(mySegmentsTaskFactory);
+        mNotificationsQueue = checkNotNull(mySegmentUpdateNotificationsQueue);
+        mUserKey = checkNotNull(userKey);
+        mHashedUserKey = checkNotNull(hashedUserKey);
     }
 
     public MySegmentsTaskFactory getMySegmentsTaskFactory() {
         return mMySegmentsTaskFactory;
     }
 
-    public BlockingQueue<MySegmentChangeNotification> getMySegmentUpdateNotificationsQueue() {
-        return mMySegmentUpdateNotificationsQueue;
+    public BlockingQueue<MySegmentUpdateParams> getNotificationsQueue() {
+        return mNotificationsQueue;
+    }
+
+    public String getUserKey() {
+        return mUserKey;
     }
 
     public BigInteger getHashedUserKey() {
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactory.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactory.java
deleted file mode 100644
index 44dc5c4c6..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactory.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.split.android.client.service.sseclient.notifications.mysegments;
-
-public interface MySegmentsNotificationProcessorFactory {
-
-    MySegmentsNotificationProcessor getProcessor(MySegmentsNotificationProcessorConfiguration configuration);
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactoryImpl.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactoryImpl.java
deleted file mode 100644
index 34d2e4a76..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorFactoryImpl.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.split.android.client.service.sseclient.notifications.mysegments;
-
-import static io.split.android.client.utils.Utils.checkNotNull;
-
-import androidx.annotation.NonNull;
-
-import io.split.android.client.common.CompressionUtilProvider;
-import io.split.android.client.service.executor.SplitTaskExecutor;
-import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
-import io.split.android.client.service.sseclient.notifications.NotificationParser;
-
-public class MySegmentsNotificationProcessorFactoryImpl implements MySegmentsNotificationProcessorFactory {
-
-    private final NotificationParser mNotificationParser;
-    private final SplitTaskExecutor mSplitTaskExecutor;
-    private final MySegmentsV2PayloadDecoder mMySegmentsPayloadDecoder;
-    private final CompressionUtilProvider mCompressionProvider;
-
-    public MySegmentsNotificationProcessorFactoryImpl(@NonNull NotificationParser notificationParser,
-                                                      @NonNull SplitTaskExecutor splitTaskExecutor,
-                                                      @NonNull MySegmentsV2PayloadDecoder mySegmentsPayloadDecoder,
-                                                      @NonNull CompressionUtilProvider compressionProvider) {
-        mNotificationParser = checkNotNull(notificationParser);
-        mSplitTaskExecutor = checkNotNull(splitTaskExecutor);
-        mMySegmentsPayloadDecoder = checkNotNull(mySegmentsPayloadDecoder);
-        mCompressionProvider = checkNotNull(compressionProvider);
-    }
-
-    @Override
-    public MySegmentsNotificationProcessor getProcessor(@NonNull MySegmentsNotificationProcessorConfiguration configuration) {
-        return new MySegmentsNotificationProcessorImpl(mNotificationParser,
-                mSplitTaskExecutor,
-                mMySegmentsPayloadDecoder,
-                mCompressionProvider,
-                checkNotNull(configuration));
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImpl.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImpl.java
deleted file mode 100644
index 8ee1215dc..000000000
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImpl.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package io.split.android.client.service.sseclient.notifications.mysegments;
-
-import static io.split.android.client.utils.Utils.checkNotNull;
-
-import androidx.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import io.split.android.client.common.CompressionUtilProvider;
-import io.split.android.client.service.executor.SplitTaskExecutor;
-import io.split.android.client.service.mysegments.MySegmentsOverwriteTask;
-import io.split.android.client.service.mysegments.MySegmentsUpdateTask;
-import io.split.android.client.service.sseclient.notifications.KeyList;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
-import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
-import io.split.android.client.service.sseclient.notifications.NotificationParser;
-import io.split.android.client.utils.logger.Logger;
-
-public class MySegmentsNotificationProcessorImpl implements MySegmentsNotificationProcessor {
-
-    private final NotificationParser mNotificationParser;
-    private final SplitTaskExecutor mSplitTaskExecutor;
-    private final MySegmentsV2PayloadDecoder mMySegmentsPayloadDecoder;
-    private final CompressionUtilProvider mCompressionProvider;
-    private final MySegmentsNotificationProcessorConfiguration mConfiguration;
-
-    public MySegmentsNotificationProcessorImpl(@NonNull NotificationParser notificationParser,
-                                               @NonNull SplitTaskExecutor splitTaskExecutor,
-                                               @NonNull MySegmentsV2PayloadDecoder mySegmentsPayloadDecoder,
-                                               @NonNull CompressionUtilProvider compressionProvider,
-                                               @NonNull MySegmentsNotificationProcessorConfiguration configuration) {
-        mNotificationParser = checkNotNull(notificationParser);
-        mSplitTaskExecutor = checkNotNull(splitTaskExecutor);
-        mMySegmentsPayloadDecoder = checkNotNull(mySegmentsPayloadDecoder);
-        mCompressionProvider = checkNotNull(compressionProvider);
-        mConfiguration = checkNotNull(configuration);
-    }
-
-    @Override
-    public void processMySegmentsUpdate(MySegmentChangeNotification notification) {
-        if (!notification.isIncludesPayload()) {
-            mConfiguration.getMySegmentUpdateNotificationsQueue().offer(notification);
-        } else {
-            List<String> segmentList = notification.getSegmentList() != null ? notification.getSegmentList() : new ArrayList<>();
-            MySegmentsOverwriteTask task = mConfiguration.getMySegmentsTaskFactory().createMySegmentsOverwriteTask(segmentList);
-            mSplitTaskExecutor.submit(task, null);
-        }
-    }
-
-    @Override
-    public void processMySegmentsUpdateV2(MySegmentChangeV2Notification notification) {
-        try {
-            switch (notification.getUpdateStrategy()) {
-                case UNBOUNDED_FETCH_REQUEST:
-                    Logger.d("Received Unbounded my segment fetch request");
-                    notifyMySegmentRefreshNeeded();
-                    break;
-                case BOUNDED_FETCH_REQUEST:
-                    Logger.d("Received Bounded my segment fetch request");
-                    byte[] keyMap = mMySegmentsPayloadDecoder.decodeAsBytes(notification.getData(),
-                            mCompressionProvider.get(notification.getCompression()));
-                    executeBoundedFetch(keyMap);
-                    break;
-                case KEY_LIST:
-                    Logger.d("Received KeyList my segment fetch request");
-                    updateSegments(mMySegmentsPayloadDecoder.decodeAsString(notification.getData(),
-                            mCompressionProvider.get(notification.getCompression())),
-                            notification.getSegmentName());
-                    break;
-                case SEGMENT_REMOVAL:
-                    Logger.d("Received Segment removal request");
-                    removeSegment(notification.getSegmentName());
-                    break;
-                default:
-                    Logger.i("Unknown my segment change v2 notification type: " + notification.getUpdateStrategy());
-            }
-        } catch (Exception e) {
-            Logger.e("Executing unbounded fetch because an error has occurred processing my segmentV2 notification: " + e.getLocalizedMessage());
-            notifyMySegmentRefreshNeeded();
-        }
-    }
-
-    private void notifyMySegmentRefreshNeeded() {
-        mConfiguration.getMySegmentUpdateNotificationsQueue().offer(new MySegmentChangeNotification());
-    }
-
-    private void removeSegment(String segmentName) {
-        // Shouldn't be null, some defensive code here
-        if (segmentName == null) {
-            return;
-        }
-        MySegmentsUpdateTask task = mConfiguration.getMySegmentsTaskFactory().createMySegmentsUpdateTask(false, segmentName);
-        mSplitTaskExecutor.submit(task, null);
-    }
-
-    private void executeBoundedFetch(byte[] keyMap) {
-        int index = mMySegmentsPayloadDecoder.computeKeyIndex(mConfiguration.getHashedUserKey(), keyMap.length);
-        if (mMySegmentsPayloadDecoder.isKeyInBitmap(keyMap, index)) {
-            Logger.d("Executing Unbounded my segment fetch request");
-            notifyMySegmentRefreshNeeded();
-        }
-    }
-
-    private void updateSegments(String keyListString, String segmentName) {
-        // Shouldn't be null, some defensive code here
-        if (segmentName == null) {
-            return;
-        }
-        KeyList keyList = mNotificationParser.parseKeyList(keyListString);
-        KeyList.Action action = mMySegmentsPayloadDecoder.getKeyListAction(keyList, mConfiguration.getHashedUserKey());
-        boolean actionIsAdd = action != KeyList.Action.REMOVE;
-
-        if (action == KeyList.Action.NONE) {
-            return;
-        }
-        Logger.d("Executing KeyList my segment fetch request: Adding = " + actionIsAdd);
-        MySegmentsUpdateTask task = mConfiguration.getMySegmentsTaskFactory().createMySegmentsUpdateTask(actionIsAdd, segmentName);
-        mSplitTaskExecutor.submit(task, null);
-    }
-}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorRegistry.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorRegistry.java
index d04b145a5..19d5f2759 100644
--- a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorRegistry.java
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorRegistry.java
@@ -1,8 +1,10 @@
 package io.split.android.client.service.sseclient.notifications.mysegments;
 
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
+
 public interface MySegmentsNotificationProcessorRegistry {
 
-    void registerMySegmentsProcessor(String matchingKey, MySegmentsNotificationProcessor processor);
+    void registerMembershipsNotificationProcessor(String matchingKey, MembershipsNotificationProcessor processor);
 
-    void unregisterMySegmentsProcessor(String matchingKey);
+    void unregisterMembershipsProcessor(String matchingKey);
 }
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculator.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculator.java
new file mode 100644
index 000000000..4b78f2a26
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculator.java
@@ -0,0 +1,8 @@
+package io.split.android.client.service.sseclient.notifications.mysegments;
+
+import io.split.android.client.service.sseclient.notifications.HashingAlgorithm;
+import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
+
+public interface SyncDelayCalculator {
+    long calculateSyncDelay(String key, Long updateIntervalMs, Integer algorithmSeed, MySegmentUpdateStrategy updateStrategy, HashingAlgorithm hashingAlgorithm);
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorImpl.java b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorImpl.java
new file mode 100644
index 000000000..6ba6aedc7
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorImpl.java
@@ -0,0 +1,31 @@
+package io.split.android.client.service.sseclient.notifications.mysegments;
+
+import java.util.concurrent.TimeUnit;
+
+import io.split.android.client.service.sseclient.notifications.HashingAlgorithm;
+import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
+import io.split.android.client.utils.MurmurHash3;
+
+public class SyncDelayCalculatorImpl implements SyncDelayCalculator {
+
+    public static final long DEFAULT_SYNC_INTERVAL_MS = TimeUnit.SECONDS.toMillis(60);
+
+    @Override
+    public long calculateSyncDelay(String key, Long updateIntervalMs, Integer algorithmSeed, MySegmentUpdateStrategy updateStrategy, HashingAlgorithm hashingAlgorithm) {
+        boolean fetchNotification = updateStrategy == MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST ||
+                updateStrategy == MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST;
+        if (!fetchNotification || hashingAlgorithm == HashingAlgorithm.NONE) {
+            return 0L;
+        }
+
+        if (updateIntervalMs == null || updateIntervalMs <= 0) {
+            updateIntervalMs = DEFAULT_SYNC_INTERVAL_MS;
+        }
+
+        if (algorithmSeed == null) {
+            algorithmSeed = 0;
+        }
+
+        return MurmurHash3.murmurhash3_x86_32(key, 0, key.length(), algorithmSeed) % updateIntervalMs;
+    }
+}
diff --git a/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorker.java b/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorker.java
index a56dfc208..3381e6d60 100644
--- a/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorker.java
+++ b/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorker.java
@@ -6,7 +6,7 @@
 
 import java.util.concurrent.BlockingQueue;
 
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizer;
 import io.split.android.client.utils.logger.Logger;
 
@@ -16,11 +16,11 @@
 public class MySegmentsUpdateWorker extends UpdateWorker {
 
     private final MySegmentsSynchronizer mSynchronizer;
-    private final BlockingQueue<MySegmentChangeNotification> mNotificationsQueue;
+    private final BlockingQueue<MySegmentUpdateParams> mNotificationsQueue;
 
     public MySegmentsUpdateWorker(
             @NonNull MySegmentsSynchronizer synchronizer,
-            @NonNull BlockingQueue<MySegmentChangeNotification> notificationsQueue) {
+            @NonNull BlockingQueue<MySegmentUpdateParams> notificationsQueue) {
         super();
         mSynchronizer = checkNotNull(synchronizer);
         mNotificationsQueue = checkNotNull(notificationsQueue);
@@ -29,8 +29,8 @@ public MySegmentsUpdateWorker(
     @Override
     protected void onWaitForNotificationLoop() throws InterruptedException {
         try {
-            mNotificationsQueue.take();
-            mSynchronizer.forceMySegmentsSync();
+            MySegmentUpdateParams params = mNotificationsQueue.take();
+            mSynchronizer.forceMySegmentsSync(params);
             Logger.d("A new notification to update segments has been received. " +
                     "Enqueuing polling task.");
         } catch (InterruptedException e) {
diff --git a/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorkerRegistryImpl.java b/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorkerRegistryImpl.java
index f6c5afb51..d2d03334c 100644
--- a/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorkerRegistryImpl.java
+++ b/src/main/java/io/split/android/client/service/sseclient/reactor/MySegmentsUpdateWorkerRegistryImpl.java
@@ -14,9 +14,7 @@ public class MySegmentsUpdateWorkerRegistryImpl implements MySegmentsUpdateWorke
     @Override
     public synchronized void registerMySegmentsUpdateWorker(String matchingKey, MySegmentsUpdateWorker mySegmentsUpdateWorker) {
         mMySegmentUpdateWorkers.put(matchingKey, mySegmentsUpdateWorker);
-        if (mStarted.get()) {
-            mySegmentsUpdateWorker.start();
-        }
+        startIfNeeded(mySegmentsUpdateWorker);
     }
 
     @Override
@@ -49,4 +47,10 @@ public void stop() {
             }
         }
     }
+
+    private void startIfNeeded(MySegmentsUpdateWorker mySegmentsUpdateWorker) {
+        if (mStarted.get()) {
+            mySegmentsUpdateWorker.start();
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/sseclient/sseclient/NotificationManagerKeeper.java b/src/main/java/io/split/android/client/service/sseclient/sseclient/NotificationManagerKeeper.java
index 128af8dcb..eeba8744d 100644
--- a/src/main/java/io/split/android/client/service/sseclient/sseclient/NotificationManagerKeeper.java
+++ b/src/main/java/io/split/android/client/service/sseclient/sseclient/NotificationManagerKeeper.java
@@ -79,9 +79,10 @@ public void handleControlNotification(ControlNotification notification) {
 
                 case STREAMING_RESET:
                     mBroadcasterChannel.pushMessage(new PushStatusEvent(EventType.PUSH_RESET));
-
+                    break;
                 default:
-                    Logger.e("Unknown message received" + notification.getControlType());
+                    Logger.e("Unknown message received " + notification.getControlType());
+                    break;
             }
         } catch (JsonSyntaxException e) {
             Logger.e("Could not parse control notification: "
diff --git a/src/main/java/io/split/android/client/service/sseclient/sseclient/PushNotificationManager.java b/src/main/java/io/split/android/client/service/sseclient/sseclient/PushNotificationManager.java
index 6aa7594fc..5217889b2 100644
--- a/src/main/java/io/split/android/client/service/sseclient/sseclient/PushNotificationManager.java
+++ b/src/main/java/io/split/android/client/service/sseclient/sseclient/PushNotificationManager.java
@@ -210,11 +210,11 @@ public void run() {
             recordSuccessfulSyncAndTokenRefreshes(token);
 
             long delay = authResult.getSseConnectionDelay();
+            mBroadcasterChannel.pushMessage(new DelayStatusEvent(delay));
             // Delay returns false if some error occurs
             if (delay > 0 && !delay(delay)) {
                 return;
             }
-            mBroadcasterChannel.pushMessage(new DelayStatusEvent(delay));
 
             // If host app is in bg or push manager stopped, abort the process
             if (mIsPaused.get() || mIsStopped.get()) {
diff --git a/src/main/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimer.java b/src/main/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimer.java
index 91293155f..79c14f699 100644
--- a/src/main/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimer.java
+++ b/src/main/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimer.java
@@ -5,6 +5,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionListener;
@@ -13,6 +14,7 @@
 import io.split.android.client.service.sseclient.BackoffCounter;
 import io.split.android.client.utils.logger.Logger;
 
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class RetryBackoffCounterTimer implements SplitTaskExecutionListener {
@@ -22,10 +24,11 @@ public class RetryBackoffCounterTimer implements SplitTaskExecutionListener {
     private final SplitTaskExecutor mTaskExecutor;
     private final BackoffCounter mBackoffCounter;
     private final int mRetryAttemptsLimit;
-    private final AtomicInteger mCurrentAttempts = new AtomicInteger(0);
+    private final AtomicInteger mCurrentAttempts;
+    private Long mInitialDelayInSeconds;
     private SplitTask mTask;
     private SplitTaskExecutionListener mListener;
-    private String mTaskId;
+    private volatile String mTaskId;
 
     /**
      * Creates an instance which retries tasks indefinitely, using the strategy defined by backoffCounter.
@@ -35,9 +38,7 @@ public class RetryBackoffCounterTimer implements SplitTaskExecutionListener {
      */
     public RetryBackoffCounterTimer(@NonNull SplitTaskExecutor taskExecutor,
                                     @NonNull BackoffCounter backoffCounter) {
-        mTaskExecutor = checkNotNull(taskExecutor);
-        mBackoffCounter = checkNotNull(backoffCounter);
-        mRetryAttemptsLimit = DEFAULT_MAX_ATTEMPTS;
+        this(taskExecutor, backoffCounter, DEFAULT_MAX_ATTEMPTS);
     }
 
     /**
@@ -50,20 +51,31 @@ public RetryBackoffCounterTimer(@NonNull SplitTaskExecutor taskExecutor,
     public RetryBackoffCounterTimer(@NonNull SplitTaskExecutor taskExecutor,
                                     @NonNull BackoffCounter backoffCounter,
                                     int retryAttemptsLimit) {
+        mCurrentAttempts = new AtomicInteger(0);
         mTaskExecutor = checkNotNull(taskExecutor);
         mBackoffCounter = checkNotNull(backoffCounter);
         mRetryAttemptsLimit = retryAttemptsLimit;
     }
 
     synchronized public void setTask(@NonNull SplitTask task, @Nullable SplitTaskExecutionListener listener) {
-        mTask = checkNotNull(task);
-        mListener = listener;
+        setTask(task, ServiceConstants.NO_INITIAL_DELAY, listener);
     }
 
     synchronized public void setTask(@NonNull SplitTask task) {
         setTask(task, null);
     }
 
+    synchronized public void setTask(@NonNull SplitTask task, @Nullable Long initialDelayInMillis, @Nullable SplitTaskExecutionListener listener) {
+        mTask = checkNotNull(task);
+        mListener = listener;
+        if (initialDelayInMillis != null) {
+            mInitialDelayInSeconds = TimeUnit.MILLISECONDS.toSeconds(initialDelayInMillis);
+        } else {
+            mInitialDelayInSeconds = 0L;
+        }
+        mCurrentAttempts.set(0);
+    }
+
     synchronized public void stop() {
         if (mTask == null) {
             return;
@@ -78,7 +90,7 @@ synchronized public void start() {
         }
         mBackoffCounter.resetCounter();
         mCurrentAttempts.incrementAndGet();
-        mTaskId = mTaskExecutor.schedule(mTask, 0L, this);
+        mTaskId = mTaskExecutor.schedule(mTask, mInitialDelayInSeconds, this);
     }
 
     synchronized private void schedule() {
@@ -94,15 +106,20 @@ synchronized private void schedule() {
     @Override
     public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
         mTaskId = null;
-        if (taskInfo.getStatus() == SplitTaskExecutionStatus.ERROR &&
-                (taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY) == null ||
-                        Boolean.FALSE.equals(taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY)))) {
-
-            if (mRetryAttemptsLimit == DEFAULT_MAX_ATTEMPTS || mCurrentAttempts.get() < mRetryAttemptsLimit) {
-                schedule();
+        if (taskInfo.getStatus() == SplitTaskExecutionStatus.ERROR) {
+            if (taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY) == null ||
+                    Boolean.FALSE.equals(taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY))) {
+
+                if (mRetryAttemptsLimit == DEFAULT_MAX_ATTEMPTS || mCurrentAttempts.get() < mRetryAttemptsLimit) {
+                    schedule();
+                }
+
+                return;
+            } else if (Boolean.TRUE.equals(taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY))) {
+                if (mListener != null) {
+                    mListener.taskExecuted(taskInfo);
+                }
             }
-
-            return;
         }
 
         mBackoffCounter.resetCounter();
diff --git a/src/main/java/io/split/android/client/service/sseclient/sseclient/SseHandler.java b/src/main/java/io/split/android/client/service/sseclient/sseclient/SseHandler.java
index 2f943995c..dd2f47071 100644
--- a/src/main/java/io/split/android/client/service/sseclient/sseclient/SseHandler.java
+++ b/src/main/java/io/split/android/client/service/sseclient/sseclient/SseHandler.java
@@ -1,5 +1,7 @@
 package io.split.android.client.service.sseclient.sseclient;
 
+import static io.split.android.client.utils.Utils.checkNotNull;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
@@ -22,8 +24,6 @@
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
 import io.split.android.client.utils.logger.Logger;
 
-import static io.split.android.client.utils.Utils.checkNotNull;
-
 public class SseHandler {
 
     private final PushManagerEventBroadcaster mBroadcasterChannel;
@@ -85,14 +85,14 @@ public void handleIncomingMessage(Map<String, String> values) {
                     break;
                 case SPLIT_KILL:
                 case SPLIT_UPDATE:
-                case MY_SEGMENTS_UPDATE:
-                case MY_SEGMENTS_UPDATE_V2:
+                case MEMBERSHIPS_MS_UPDATE:
+                case MEMBERSHIPS_LS_UPDATE:
                     if (mNotificationManagerKeeper.isStreamingActive()) {
                         mNotificationProcessor.process(incomingNotification);
                     }
                     break;
                 default:
-                    Logger.w("SSE Handler: Unknown notification");
+                    Logger.w("SSE Handler: Unknown notification: " + incomingNotification.getType());
             }
         }
     }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/MySegmentsChangeChecker.java b/src/main/java/io/split/android/client/service/synchronizer/MySegmentsChangeChecker.java
index e4d2fc4c8..16fb4cb73 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/MySegmentsChangeChecker.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/MySegmentsChangeChecker.java
@@ -1,15 +1,12 @@
 package io.split.android.client.service.synchronizer;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public class MySegmentsChangeChecker {
-    public boolean mySegmentsHaveChanged(List<String> oldSegments, List<String> newSegments) {
-        List<String> oldValues = new ArrayList<>(oldSegments);
-        List<String> newValues = new ArrayList<>(newSegments);
-        Collections.sort(oldValues);
-        Collections.sort(newValues);
-        return !oldValues.equals(newValues);
+    public boolean mySegmentsHaveChanged(final List<String> oldSegments, final List<String> newSegments) {
+        Collections.sort(oldSegments);
+        Collections.sort(newSegments);
+        return !oldSegments.equals(newSegments);
     }
 }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java b/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java
index 6f8e614a4..27adf8d38 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/Synchronizer.java
@@ -18,8 +18,6 @@ public interface Synchronizer extends SplitLifecycleAware {
 
     void synchronizeMySegments();
 
-    void forceMySegmentsSync();
-
     void startPeriodicFetching();
 
     void stopPeriodicFetching();
diff --git a/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java b/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java
index e0b318585..5f069bee9 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/SynchronizerImpl.java
@@ -35,7 +35,8 @@
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
 import io.split.android.client.utils.logger.Logger;
 
-public class SynchronizerImpl implements Synchronizer, SplitTaskExecutionListener, MySegmentsSynchronizerRegistry, AttributesSynchronizerRegistry {
+public class SynchronizerImpl implements Synchronizer, SplitTaskExecutionListener,
+        MySegmentsSynchronizerRegistry, AttributesSynchronizerRegistry {
 
     private final SplitTaskExecutor mTaskExecutor;
     private final SplitTaskExecutor mSingleThreadTaskExecutor;
@@ -166,11 +167,6 @@ public void synchronizeMySegments() {
         mMySegmentsSynchronizerRegistry.synchronizeMySegments();
     }
 
-    @Override
-    public void forceMySegmentsSync() {
-        mMySegmentsSynchronizerRegistry.forceMySegmentsSync();
-    }
-
     @Override
     synchronized public void startPeriodicFetching() {
         mFeatureFlagsSynchronizer.startPeriodicFetching();
diff --git a/src/main/java/io/split/android/client/service/synchronizer/WorkManagerWrapper.java b/src/main/java/io/split/android/client/service/synchronizer/WorkManagerWrapper.java
index b5cc20991..c342f1cbc 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/WorkManagerWrapper.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/WorkManagerWrapper.java
@@ -18,12 +18,14 @@
 import java.lang.ref.WeakReference;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import io.split.android.android_client.BuildConfig;
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.SplitFilter;
+import io.split.android.client.network.CertificatePin;
 import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionListener;
@@ -33,10 +35,10 @@
 import io.split.android.client.service.workmanager.EventsRecorderWorker;
 import io.split.android.client.service.workmanager.ImpressionsRecorderWorker;
 import io.split.android.client.service.workmanager.MySegmentsSyncWorker;
+import io.split.android.client.service.workmanager.UniqueKeysRecorderWorker;
 import io.split.android.client.service.workmanager.splits.SplitsSyncWorker;
 import io.split.android.client.utils.Json;
 import io.split.android.client.utils.logger.Logger;
-import io.split.android.client.service.workmanager.UniqueKeysRecorderWorker;
 
 public class WorkManagerWrapper implements MySegmentsWorkManagerWrapper {
     final private WorkManager mWorkManager;
@@ -145,11 +147,14 @@ private Data buildInputData(Data customData) {
         dataBuilder.putString(ServiceConstants.WORKER_PARAM_DATABASE_NAME, mDatabaseName);
         dataBuilder.putString(ServiceConstants.WORKER_PARAM_API_KEY, mApiKey);
         dataBuilder.putBoolean(ServiceConstants.WORKER_PARAM_ENCRYPTION_ENABLED, mSplitClientConfig.encryptionEnabled());
-        try {
-            String pinsJson = Json.toJson(mSplitClientConfig.certificatePinningConfiguration().getPins());
-            dataBuilder.putString(ServiceConstants.WORKER_PARAM_CERTIFICATE_PINS, pinsJson);
-        } catch (Exception e) {
-            Logger.e("Error converting pins to JSON for BG sync", e.getLocalizedMessage());
+        if (mSplitClientConfig.certificatePinningConfiguration() != null) {
+            try {
+                Map<String, Set<CertificatePin>> pins = mSplitClientConfig.certificatePinningConfiguration().getPins();
+                String pinsJson = Json.toJson(pins);
+                dataBuilder.putString(ServiceConstants.WORKER_PARAM_CERTIFICATE_PINS, pinsJson);
+            } catch (Exception e) {
+                Logger.e("Error converting pins to JSON for BG sync", e.getLocalizedMessage());
+            }
         }
 
         if (customData != null) {
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizer.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizer.java
index 4c8d4c17c..c656190c1 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizer.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizer.java
@@ -1,12 +1,14 @@
 package io.split.android.client.service.synchronizer.mysegments;
 
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
+
 public interface MySegmentsSynchronizer {
 
     void loadMySegmentsFromCache();
 
     void synchronizeMySegments();
 
-    void forceMySegmentsSync();
+    void forceMySegmentsSync(MySegmentUpdateParams params);
 
     void destroy();
 
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactory.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactory.java
index 31e9b63f3..240916f40 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactory.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactory.java
@@ -1,9 +1,10 @@
 package io.split.android.client.service.synchronizer.mysegments;
 
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 
 public interface MySegmentsSynchronizerFactory {
 
-    MySegmentsSynchronizer getSynchronizer(MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager splitEventsManager);
+    MySegmentsSynchronizer getSynchronizer(MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager splitEventsManager, SplitInternalEvent loadedFromStorageInternalEvent, int segmentsRefreshRate);
 }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactoryImpl.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactoryImpl.java
index 09f1aaaeb..12ec91a8d 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactoryImpl.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerFactoryImpl.java
@@ -6,6 +6,7 @@
 
 import io.split.android.client.RetryBackoffCounterTimerFactory;
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.executor.SplitTaskExecutor;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 
@@ -15,22 +16,20 @@ public class MySegmentsSynchronizerFactoryImpl implements MySegmentsSynchronizer
 
     private final RetryBackoffCounterTimerFactory mRetryBackoffCounterTimerFactory;
     private final SplitTaskExecutor mSplitTaskExecutor;
-    private final int mSegmentsRefreshRate;
 
     public MySegmentsSynchronizerFactoryImpl(@NonNull RetryBackoffCounterTimerFactory retryBackoffCounterTimerFactory,
-                                             @NonNull SplitTaskExecutor splitTaskExecutor,
-                                             int segmentsRefreshRate) {
+                                             @NonNull SplitTaskExecutor splitTaskExecutor) {
         mRetryBackoffCounterTimerFactory = checkNotNull(retryBackoffCounterTimerFactory);
         mSplitTaskExecutor = checkNotNull(splitTaskExecutor);
-        mSegmentsRefreshRate = segmentsRefreshRate;
     }
 
     @Override
-    public MySegmentsSynchronizer getSynchronizer(MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager splitEventsManager) {
+    public MySegmentsSynchronizer getSynchronizer(MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager splitEventsManager, SplitInternalEvent loadedFromStorageInternalEvent, int segmentsRefreshRate) {
         return new MySegmentsSynchronizerImpl(mRetryBackoffCounterTimerFactory.create(mSplitTaskExecutor, BACKOFF_BASE),
                 mSplitTaskExecutor,
                 splitEventsManager,
                 mySegmentsTaskFactory,
-                mSegmentsRefreshRate);
+                segmentsRefreshRate,
+                loadedFromStorageInternalEvent);
     }
 }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImpl.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImpl.java
index f743940d2..999ff04e2 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImpl.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImpl.java
@@ -12,6 +12,8 @@
 import io.split.android.client.service.executor.SplitTaskExecutionListener;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
 import io.split.android.client.service.executor.SplitTaskExecutor;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
+import io.split.android.client.service.mysegments.MySegmentsSyncTask;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 import io.split.android.client.service.sseclient.sseclient.RetryBackoffCounterTimer;
 import io.split.android.client.service.synchronizer.LoadLocalDataListener;
@@ -26,21 +28,24 @@ public class MySegmentsSynchronizerImpl implements MySegmentsSynchronizer {
     private final SplitTaskExecutionListener mMySegmentsSyncListener;
     private final AtomicBoolean mIsSynchronizing = new AtomicBoolean(true);
     private String mMySegmentsFetcherTaskId;
+    private final AtomicBoolean mIsDelayedFetchScheduled = new AtomicBoolean(false);
 
     public MySegmentsSynchronizerImpl(@NonNull RetryBackoffCounterTimer retryBackoffCounterTimer,
                                       @NonNull SplitTaskExecutor taskExecutor,
                                       @NonNull SplitEventsManager eventsManager,
                                       @NonNull MySegmentsTaskFactory mySegmentsTaskFactory,
-                                      int segmentsRefreshRate) {
+                                      int segmentsRefreshRate,
+                                      @NonNull SplitInternalEvent loadedFromStorageInternalEvent) {
         mTaskExecutor = checkNotNull(taskExecutor);
         mMySegmentsSyncRetryTimer = checkNotNull(retryBackoffCounterTimer);
         mSplitTaskFactory = checkNotNull(mySegmentsTaskFactory);
         mSegmentsRefreshRate = segmentsRefreshRate;
         mLoadLocalMySegmentsListener = new LoadLocalDataListener(
-                eventsManager, SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
+                eventsManager, loadedFromStorageInternalEvent);
         mMySegmentsSyncListener = new SplitTaskExecutionListener() {
             @Override
             public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
+                mIsDelayedFetchScheduled.set(false);
                 if (taskInfo.getStatus() == SplitTaskExecutionStatus.ERROR) {
                     if (Boolean.TRUE.equals(taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY))) {
                         mIsSynchronizing.compareAndSet(true, false);
@@ -59,15 +64,17 @@ public void loadMySegmentsFromCache() {
     @Override
     public void synchronizeMySegments() {
         if (mIsSynchronizing.get()) {
-            mMySegmentsSyncRetryTimer.setTask(mSplitTaskFactory.createMySegmentsSyncTask(false), mMySegmentsSyncListener);
+            mMySegmentsSyncRetryTimer.stop();
+            mMySegmentsSyncRetryTimer.setTask(mSplitTaskFactory.createMySegmentsSyncTask(false, null, null), mMySegmentsSyncListener);
             mMySegmentsSyncRetryTimer.start();
         }
     }
 
     @Override
-    public void forceMySegmentsSync() {
-        if (mIsSynchronizing.get()) {
-            mMySegmentsSyncRetryTimer.setTask(mSplitTaskFactory.createMySegmentsSyncTask(true), mMySegmentsSyncListener);
+    public void forceMySegmentsSync(MySegmentUpdateParams params) {
+        if (mIsSynchronizing.get() && mIsDelayedFetchScheduled.compareAndSet(false, true)) {
+            mMySegmentsSyncRetryTimer.stop();
+            mMySegmentsSyncRetryTimer.setTask(getForcedSegmentsSyncTask(params.getTargetSegmentsCn(), params.getTargetLargeSegmentsCn()), params.getSyncDelay(), mMySegmentsSyncListener);
             mMySegmentsSyncRetryTimer.start();
         }
     }
@@ -85,7 +92,7 @@ public void scheduleSegmentsSyncTask() {
             }
 
             mMySegmentsFetcherTaskId = mTaskExecutor.schedule(
-                    mSplitTaskFactory.createMySegmentsSyncTask(false),
+                    getMySegmentsSyncTask(),
                     mSegmentsRefreshRate,
                     mSegmentsRefreshRate,
                     mMySegmentsSyncListener);
@@ -105,4 +112,12 @@ public void submitMySegmentsLoadingTask() {
     private void submitMySegmentsLoadingTask(SplitTaskExecutionListener executionListener) {
         mTaskExecutor.submit(mSplitTaskFactory.createLoadMySegmentsTask(), executionListener);
     }
+
+    private MySegmentsSyncTask getMySegmentsSyncTask() {
+        return mSplitTaskFactory.createMySegmentsSyncTask(false, null, null);
+    }
+
+    private MySegmentsSyncTask getForcedSegmentsSyncTask(Long targetSegmentsCn, Long targetLargeSegmentsCn) {
+        return mSplitTaskFactory.createMySegmentsSyncTask(true, targetSegmentsCn, targetLargeSegmentsCn);
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java
index 097b95e53..5aba4ce82 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImpl.java
@@ -1,10 +1,15 @@
 package io.split.android.client.service.synchronizer.mysegments;
 
+import androidx.core.util.Consumer;
+
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public class MySegmentsSynchronizerRegistryImpl implements MySegmentsSynchronizerRegistry, MySegmentsSynchronizer {
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
+
+public class MySegmentsSynchronizerRegistryImpl implements MySegmentsSynchronizerRegistry,
+    MySegmentsSynchronizer {
 
     private final AtomicBoolean mLoadedFromCache = new AtomicBoolean(false);
     private final AtomicBoolean mSynchronizedSegments = new AtomicBoolean(false);
@@ -15,17 +20,7 @@ public class MySegmentsSynchronizerRegistryImpl implements MySegmentsSynchronize
     @Override
     public synchronized void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer) {
         mMySegmentsSynchronizers.put(userKey, mySegmentsSynchronizer);
-        if (mLoadedFromCache.get()) {
-            mySegmentsSynchronizer.loadMySegmentsFromCache();
-        }
-
-        if (mSynchronizedSegments.get()) {
-            mySegmentsSynchronizer.synchronizeMySegments();
-        }
-
-        if (mScheduledSegmentsSyncTask.get()) {
-            mySegmentsSynchronizer.scheduleSegmentsSyncTask();
-        }
+        triggerPendingActions(mySegmentsSynchronizer);
     }
 
     @Override
@@ -35,64 +30,71 @@ public synchronized void unregisterMySegmentsSynchronizer(String userKey) {
             mySegmentsSynchronizer.stopPeriodicFetching();
             mySegmentsSynchronizer.destroy();
         }
+
         mMySegmentsSynchronizers.remove(userKey);
     }
 
     @Override
     public synchronized void loadMySegmentsFromCache() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.loadMySegmentsFromCache();
-        }
+        executeForAll(MySegmentsSynchronizer::loadMySegmentsFromCache);
 
         mLoadedFromCache.set(true);
     }
 
     @Override
     public void synchronizeMySegments() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.synchronizeMySegments();
-        }
+        executeForAll(MySegmentsSynchronizer::synchronizeMySegments);
 
         mSynchronizedSegments.set(true);
     }
 
     @Override
-    public void forceMySegmentsSync() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.forceMySegmentsSync();
-        }
+    public void forceMySegmentsSync(MySegmentUpdateParams params) {
+        executeForAll(mySegmentsSynchronizer -> mySegmentsSynchronizer.forceMySegmentsSync(params));
     }
 
     @Override
     public synchronized void destroy() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.destroy();
-        }
+        executeForAll(MySegmentsSynchronizer::destroy);
     }
 
     @Override
     public synchronized void scheduleSegmentsSyncTask() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.scheduleSegmentsSyncTask();
-        }
+        executeForAll(MySegmentsSynchronizer::scheduleSegmentsSyncTask);
 
         mScheduledSegmentsSyncTask.set(true);
     }
 
     @Override
     public void submitMySegmentsLoadingTask() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.submitMySegmentsLoadingTask();
-        }
+        executeForAll(MySegmentsSynchronizer::submitMySegmentsLoadingTask);
     }
 
     @Override
     public synchronized void stopPeriodicFetching() {
-        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
-            mySegmentsSynchronizer.stopPeriodicFetching();
-        }
+        executeForAll(MySegmentsSynchronizer::stopPeriodicFetching);
 
         mScheduledSegmentsSyncTask.set(false);
         mStoppedPeriodicFetching.set(true);
     }
+
+    private void triggerPendingActions(MySegmentsSynchronizer mySegmentsSynchronizer) {
+        if (mLoadedFromCache.get()) {
+            mySegmentsSynchronizer.loadMySegmentsFromCache();
+        }
+
+        if (mSynchronizedSegments.get()) {
+            mySegmentsSynchronizer.synchronizeMySegments();
+        }
+
+        if (mScheduledSegmentsSyncTask.get()) {
+            mySegmentsSynchronizer.scheduleSegmentsSyncTask();
+        }
+    }
+
+    private void executeForAll(Consumer<MySegmentsSynchronizer> consumer) {
+        for (MySegmentsSynchronizer mySegmentsSynchronizer : mMySegmentsSynchronizers.values()) {
+            consumer.accept(mySegmentsSynchronizer);
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsWorkManagerWrapper.java b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsWorkManagerWrapper.java
index 6d1c056e2..67dee11e3 100644
--- a/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsWorkManagerWrapper.java
+++ b/src/main/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsWorkManagerWrapper.java
@@ -3,6 +3,7 @@
 import java.util.Set;
 
 public interface MySegmentsWorkManagerWrapper {
+
     void scheduleMySegmentsWork(Set<String> keys);
 
     void removeWork();
diff --git a/src/main/java/io/split/android/client/service/telemetry/TelemetryTaskFactoryImpl.java b/src/main/java/io/split/android/client/service/telemetry/TelemetryTaskFactoryImpl.java
index e28af28d9..72c1b9c32 100644
--- a/src/main/java/io/split/android/client/service/telemetry/TelemetryTaskFactoryImpl.java
+++ b/src/main/java/io/split/android/client/service/telemetry/TelemetryTaskFactoryImpl.java
@@ -29,12 +29,13 @@ public TelemetryTaskFactoryImpl(@NonNull HttpRecorder<Config> telemetryConfigRec
                                     @NonNull SplitClientConfig splitClientConfig,
                                     @NonNull SplitsStorage splitsStorage,
                                     @NonNull MySegmentsStorageContainer mySegmentsStorageContainer,
+                                    MySegmentsStorageContainer myLargeSegmentsStorageContainer,
                                     int flagSetCount,
                                     int invalidFlagSetCount) {
         mTelemetryConfigRecorder = telemetryConfigRecorder;
         mTelemetryConfigProvider = new TelemetryConfigProviderImpl(telemetryStorage, splitClientConfig, flagSetCount, invalidFlagSetCount);
         mTelemetryStatsRecorder = telemetryStatsRecorder;
-        mTelemetryStatsProvider = new TelemetryStatsProviderImpl(telemetryStorage, splitsStorage, mySegmentsStorageContainer);
+        mTelemetryStatsProvider = new TelemetryStatsProviderImpl(telemetryStorage, splitsStorage, mySegmentsStorageContainer, myLargeSegmentsStorageContainer);
         mTelemetryRuntimeProducer = telemetryStorage;
     }
 
diff --git a/src/main/java/io/split/android/client/service/workmanager/BaseSegmentsSyncWorker.java b/src/main/java/io/split/android/client/service/workmanager/BaseSegmentsSyncWorker.java
new file mode 100644
index 000000000..ce3014a62
--- /dev/null
+++ b/src/main/java/io/split/android/client/service/workmanager/BaseSegmentsSyncWorker.java
@@ -0,0 +1,68 @@
+package io.split.android.client.service.workmanager;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.work.WorkerParameters;
+
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import io.split.android.client.network.HttpClient;
+import io.split.android.client.service.ServiceConstants;
+import io.split.android.client.service.mysegments.MySegmentsBulkSyncTask;
+import io.split.android.client.service.mysegments.MySegmentsSyncTask;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.logger.Logger;
+
+abstract class BaseSegmentsSyncWorker extends SplitWorker {
+
+    BaseSegmentsSyncWorker(@NonNull Context context,
+                                @NonNull WorkerParameters workerParams) {
+
+        super(context, workerParams);
+        String[] keys =
+                workerParams.getInputData().getStringArray(ServiceConstants.WORKER_PARAM_KEY);
+        String apiKey = workerParams.getInputData().getString(ServiceConstants.WORKER_PARAM_API_KEY);
+        boolean isEncryptionEnabled = workerParams.getInputData().getBoolean(ServiceConstants.WORKER_PARAM_ENCRYPTION_ENABLED,
+                false);
+        boolean shouldRecordTelemetry = workerParams.getInputData().getBoolean(ServiceConstants.SHOULD_RECORD_TELEMETRY, false);
+        try {
+            if (keys == null) {
+                Logger.e("Error scheduling segments sync worker: Keys are null");
+                return;
+            }
+
+            mSplitTask = new MySegmentsBulkSyncTask(Collections.unmodifiableSet(getIndividualMySegmentsSyncTasks(keys,
+                    shouldRecordTelemetry,
+                    getHttpClient(),
+                    getEndPoint(),
+                    getDatabase(),
+                    apiKey,
+                    isEncryptionEnabled)));
+
+        } catch (URISyntaxException e) {
+            Logger.e("Error creating Split worker: " + e.getMessage());
+        }
+    }
+
+    private Set<MySegmentsSyncTask> getIndividualMySegmentsSyncTasks(String[] keys,
+                                                                            boolean shouldRecordTelemetry,
+                                                                            HttpClient httpClient,
+                                                                            String endPoint,
+                                                                            SplitRoomDatabase database,
+                                                                            String apiKey,
+                                                                            boolean isEncryptionEnabled) throws URISyntaxException {
+        Set<MySegmentsSyncTask> mySegmentsSyncTasks = new HashSet<>();
+        for (String key : keys) {
+            mySegmentsSyncTasks.add(
+                    getTask(shouldRecordTelemetry, httpClient, endPoint, database, apiKey, isEncryptionEnabled, key));
+        }
+
+        return mySegmentsSyncTasks;
+    }
+
+    protected abstract @NonNull MySegmentsSyncTask getTask(boolean shouldRecordTelemetry, HttpClient httpClient, String endPoint, SplitRoomDatabase database, String apiKey, boolean isEncryptionEnabled, String key) throws URISyntaxException;
+}
diff --git a/src/main/java/io/split/android/client/service/workmanager/MySegmentsSyncWorker.java b/src/main/java/io/split/android/client/service/workmanager/MySegmentsSyncWorker.java
index f3f84f02a..a55814375 100644
--- a/src/main/java/io/split/android/client/service/workmanager/MySegmentsSyncWorker.java
+++ b/src/main/java/io/split/android/client/service/workmanager/MySegmentsSyncWorker.java
@@ -6,70 +6,35 @@
 import androidx.work.WorkerParameters;
 
 import java.net.URISyntaxException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 import io.split.android.client.network.HttpClient;
-import io.split.android.client.service.ServiceConstants;
 import io.split.android.client.service.ServiceFactory;
-import io.split.android.client.service.mysegments.MySegmentsBulkSyncTask;
 import io.split.android.client.service.mysegments.MySegmentsSyncTask;
+import io.split.android.client.service.mysegments.MySegmentsSyncTaskConfig;
 import io.split.android.client.storage.db.SplitRoomDatabase;
 import io.split.android.client.storage.db.StorageFactory;
-import io.split.android.client.utils.logger.Logger;
 
-public class MySegmentsSyncWorker extends SplitWorker {
+public class MySegmentsSyncWorker extends BaseSegmentsSyncWorker {
 
     public MySegmentsSyncWorker(@NonNull Context context,
                                 @NonNull WorkerParameters workerParams) {
 
         super(context, workerParams);
-        String[] keys =
-                workerParams.getInputData().getStringArray(ServiceConstants.WORKER_PARAM_KEY);
-        String apiKey = workerParams.getInputData().getString(ServiceConstants.WORKER_PARAM_API_KEY);
-        boolean isEncryptionEnabled = workerParams.getInputData().getBoolean(ServiceConstants.WORKER_PARAM_ENCRYPTION_ENABLED,
-                false);
-        boolean shouldRecordTelemetry = workerParams.getInputData().getBoolean(ServiceConstants.SHOULD_RECORD_TELEMETRY, false);
-        try {
-            if (keys == null) {
-                Logger.e("Error scheduling segments sync worker: Keys are null");
-                return;
-            }
-
-            mSplitTask = new MySegmentsBulkSyncTask(Collections.unmodifiableSet(getIndividualMySegmentsSyncTasks(keys,
-                    shouldRecordTelemetry,
-                    getHttpClient(),
-                    getEndPoint(),
-                    getDatabase(),
-                    apiKey,
-                    isEncryptionEnabled)));
-
-        } catch (URISyntaxException e) {
-            Logger.e("Error creating Split worker: " + e.getMessage());
-        }
     }
 
-    private static Set<MySegmentsSyncTask> getIndividualMySegmentsSyncTasks(String[] keys,
-                                                                            boolean shouldRecordTelemetry,
-                                                                            HttpClient httpClient,
-                                                                            String endPoint,
-                                                                            SplitRoomDatabase database,
-                                                                            String apiKey,
-                                                                            boolean isEncryptionEnabled) throws URISyntaxException {
-        Set<MySegmentsSyncTask> mySegmentsSyncTasks = new HashSet<>();
-        for (String key : keys) {
-            mySegmentsSyncTasks.add(
-                    new MySegmentsSyncTask(
-                            ServiceFactory.getMySegmentsFetcher(httpClient,
-                                    endPoint, key),
-                            StorageFactory.getMySegmentsStorageForWorker(database, apiKey, isEncryptionEnabled).getStorageForKey(key),
-                            false,
-                            null,
-                            StorageFactory.getTelemetryStorage(shouldRecordTelemetry))
-            );
-        }
-
-        return mySegmentsSyncTasks;
+    @NonNull
+    @Override
+    protected MySegmentsSyncTask getTask(boolean shouldRecordTelemetry, HttpClient httpClient, String endPoint, SplitRoomDatabase database, String apiKey, boolean isEncryptionEnabled, String key) throws URISyntaxException {
+        return new MySegmentsSyncTask(
+                ServiceFactory.getMySegmentsFetcher(httpClient,
+                        endPoint, key),
+                StorageFactory.getMySegmentsStorageForWorker(database, apiKey, isEncryptionEnabled).getStorageForKey(key),
+                StorageFactory.getMyLargeSegmentsStorageForWorker(database, apiKey, isEncryptionEnabled).getStorageForKey(key),
+                false,
+                null,
+                StorageFactory.getTelemetryStorage(shouldRecordTelemetry),
+                MySegmentsSyncTaskConfig.get(),
+                null,
+                null);
     }
 }
diff --git a/src/main/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilder.java b/src/main/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilder.java
index 26519f20b..d1fa25c7a 100644
--- a/src/main/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilder.java
+++ b/src/main/java/io/split/android/client/service/workmanager/splits/SplitsSyncWorkerTaskBuilder.java
@@ -1,7 +1,5 @@
 package io.split.android.client.service.workmanager.splits;
 
-import androidx.annotation.NonNull;
-
 import java.net.URISyntaxException;
 
 import io.split.android.client.service.executor.SplitTask;
diff --git a/src/main/java/io/split/android/client/shared/ClientComponentsRegister.java b/src/main/java/io/split/android/client/shared/ClientComponentsRegister.java
index 935aac005..8411c009e 100644
--- a/src/main/java/io/split/android/client/shared/ClientComponentsRegister.java
+++ b/src/main/java/io/split/android/client/shared/ClientComponentsRegister.java
@@ -5,7 +5,7 @@
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 
 public interface ClientComponentsRegister {
-    void registerComponents(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager eventsManager);
+    void registerComponents(Key key, SplitEventsManager eventsManager, MySegmentsTaskFactory mySegmentsTaskFactory);
 
     void unregisterComponentsForKey(Key key);
 }
diff --git a/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java b/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java
index dfe56b4a8..de1d53414 100644
--- a/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java
+++ b/src/main/java/io/split/android/client/shared/ClientComponentsRegisterImpl.java
@@ -11,13 +11,14 @@
 import io.split.android.client.api.Key;
 import io.split.android.client.events.EventsManagerRegistry;
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.attributes.AttributeTaskFactoryImpl;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
 import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessor;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
 import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorConfiguration;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorFactory;
+import io.split.android.client.service.sseclient.notifications.mysegments.MembershipsNotificationProcessorFactory;
 import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorRegistry;
 import io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorker;
 import io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorkerRegistry;
@@ -28,13 +29,13 @@
 import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizer;
 import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizerFactory;
 import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizerRegistry;
-import io.split.android.client.storage.common.SplitStorageContainer;
 import io.split.android.client.storage.attributes.AttributesStorage;
+import io.split.android.client.storage.common.SplitStorageContainer;
 
 public class ClientComponentsRegisterImpl implements ClientComponentsRegister {
 
     private final MySegmentsSynchronizerFactory mMySegmentsSynchronizerFactory;
-    private final MySegmentsNotificationProcessorFactory mMySegmentsNotificationProcessorFactory;
+    private final MembershipsNotificationProcessorFactory mMembershipsNotificationProcessorFactory;
     private final SplitStorageContainer mStorageContainer;
     private final AttributesSynchronizerFactory mAttributesSynchronizerFactory;
     private final AttributesSynchronizerRegistry mAttributesSynchronizerRegistry;
@@ -56,7 +57,7 @@ public ClientComponentsRegisterImpl(@NonNull SplitClientConfig splitConfig,
                                         @NonNull EventsManagerRegistry eventsManagerRegistry,
                                         @Nullable SseAuthenticator sseAuthenticator,
                                         @Nullable MySegmentsNotificationProcessorRegistry mySegmentsNotificationProcessorRegistry,
-                                        @Nullable MySegmentsNotificationProcessorFactory mySegmentsNotificationProcessorFactory,
+                                        @Nullable MembershipsNotificationProcessorFactory membershipsNotificationProcessorFactory,
                                         @Nullable MySegmentsV2PayloadDecoder mySegmentsV2PayloadDecoder) {
         mSplitConfig = splitConfig;
         mMySegmentsSynchronizerFactory = checkNotNull(mySegmentsSynchronizerFactory);
@@ -70,20 +71,23 @@ public ClientComponentsRegisterImpl(@NonNull SplitClientConfig splitConfig,
         mMySegmentsNotificationProcessorRegistry = mySegmentsNotificationProcessorRegistry;
         mMySegmentsUpdateWorkerRegistry = mySegmentsUpdateWorkerRegistry;
         mSseAuthenticator = sseAuthenticator;
-        mMySegmentsNotificationProcessorFactory = mySegmentsNotificationProcessorFactory;
+        mMembershipsNotificationProcessorFactory = membershipsNotificationProcessorFactory;
         mMySegmentsV2PayloadDecoder = mySegmentsV2PayloadDecoder;
     }
 
     @Override
-    public void registerComponents(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, SplitEventsManager eventsManager) {
+    public void registerComponents(Key key, SplitEventsManager eventsManager, MySegmentsTaskFactory mySegmentsTaskFactory) {
         registerEventsManager(key, eventsManager);
-        MySegmentsSynchronizer mySegmentsSynchronizer = mMySegmentsSynchronizerFactory.getSynchronizer(mySegmentsTaskFactory, eventsManager);
+
+        MySegmentsSynchronizer mySegmentsSynchronizer = mMySegmentsSynchronizerFactory.getSynchronizer(mySegmentsTaskFactory, eventsManager, SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE, mSplitConfig.segmentsRefreshRate());
         registerMySegmentsSynchronizer(key, mySegmentsSynchronizer);
+
         registerAttributesSynchronizer(key, eventsManager);
+
         if (isSyncEnabled()) {
             registerKeyInSeeAuthenticator(key);
-            LinkedBlockingDeque<MySegmentChangeNotification> mySegmentsNotificationQueue = new LinkedBlockingDeque<>();
-            registerMySegmentsNotificationProcessor(key, mySegmentsTaskFactory, mySegmentsNotificationQueue);
+            LinkedBlockingDeque<MySegmentUpdateParams> mySegmentsNotificationQueue = new LinkedBlockingDeque<>();
+            registerMembershipsNotificationProcessor(key, mySegmentsTaskFactory, mySegmentsNotificationQueue);
             registerMySegmentsUpdateWorker(key, mySegmentsSynchronizer, mySegmentsNotificationQueue);
         }
     }
@@ -97,7 +101,7 @@ public void unregisterComponentsForKey(Key key) {
         if (isSyncEnabled()) {
             mSseAuthenticator.unregisterKey(key.matchingKey());
             mMySegmentsUpdateWorkerRegistry.unregisterMySegmentsUpdateWorker(key.matchingKey());
-            mMySegmentsNotificationProcessorRegistry.unregisterMySegmentsProcessor(key.matchingKey());
+            mMySegmentsNotificationProcessorRegistry.unregisterMembershipsProcessor(key.matchingKey());
         }
     }
 
@@ -116,7 +120,7 @@ private void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySe
                 mySegmentsSynchronizer);
     }
 
-    private void registerMySegmentsUpdateWorker(Key key, MySegmentsSynchronizer mySegmentsSynchronizer, LinkedBlockingDeque<MySegmentChangeNotification> notificationsQueue) {
+    private void registerMySegmentsUpdateWorker(Key key, MySegmentsSynchronizer mySegmentsSynchronizer, LinkedBlockingDeque<MySegmentUpdateParams> notificationsQueue) {
         mMySegmentsUpdateWorkerRegistry.registerMySegmentsUpdateWorker(key.matchingKey(),
                 new MySegmentsUpdateWorker(mySegmentsSynchronizer, notificationsQueue));
     }
@@ -129,19 +133,18 @@ private void registerKeyInSeeAuthenticator(Key key) {
         mSseAuthenticator.registerKey(key.matchingKey());
     }
 
-    private void registerMySegmentsNotificationProcessor(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, LinkedBlockingDeque<MySegmentChangeNotification> notificationsQueue) {
-        MySegmentsNotificationProcessor processor = getMySegmentsNotificationProcessor(key, mySegmentsTaskFactory, notificationsQueue);
-        mMySegmentsNotificationProcessorRegistry.registerMySegmentsProcessor(key.matchingKey(), processor);
+    private void registerMembershipsNotificationProcessor(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, LinkedBlockingDeque<MySegmentUpdateParams> notificationsQueue) {
+        MembershipsNotificationProcessor processor = getMembershipsNotificationProcessor(key, mySegmentsTaskFactory, notificationsQueue);
+        mMySegmentsNotificationProcessorRegistry.registerMembershipsNotificationProcessor(key.matchingKey(), processor);
     }
 
-    private MySegmentsNotificationProcessor getMySegmentsNotificationProcessor(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, LinkedBlockingDeque<MySegmentChangeNotification> mySegmentUpdateNotificationsQueue) {
-        return mMySegmentsNotificationProcessorFactory.getProcessor(
+    private MembershipsNotificationProcessor getMembershipsNotificationProcessor(Key key, MySegmentsTaskFactory mySegmentsTaskFactory, LinkedBlockingDeque<MySegmentUpdateParams> mySegmentUpdateNotificationsQueue) {
+        return mMembershipsNotificationProcessorFactory.getProcessor(
                 new MySegmentsNotificationProcessorConfiguration(
                         mySegmentsTaskFactory,
                         mySegmentUpdateNotificationsQueue,
-                        mMySegmentsV2PayloadDecoder.hashKey(key.matchingKey())
-                )
-        );
+                        key.matchingKey(),
+                        mMySegmentsV2PayloadDecoder.hashKey(key.matchingKey())));
     }
 
     private boolean isSyncEnabled() {
diff --git a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java
index 64535d7bb..9d50f645d 100644
--- a/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java
+++ b/src/main/java/io/split/android/client/shared/SplitClientContainerImpl.java
@@ -147,7 +147,7 @@ public void createNewClient(Key key) {
 
         SplitClient client = mSplitClientFactory.getClient(key, mySegmentsTaskFactory, eventsManager, mDefaultMatchingKey.equals(key.matchingKey()));
         trackNewClient(key, client);
-        mClientComponentsRegister.registerComponents(key, mySegmentsTaskFactory, eventsManager);
+        mClientComponentsRegister.registerComponents(key, eventsManager, mySegmentsTaskFactory);
 
         if (mConfig.syncEnabled() && mStreamingEnabled) {
             connectToStreaming();
@@ -159,14 +159,14 @@ public void createNewClient(Key key) {
         }
     }
 
+    @NonNull
     private MySegmentsTaskFactory getMySegmentsTaskFactory(Key key, SplitEventsManager eventsManager) {
         return mMySegmentsTaskFactoryProvider.getFactory(
-                new MySegmentsTaskFactoryConfiguration(
+                MySegmentsTaskFactoryConfiguration.get(
                         mSplitApiFacade.getMySegmentsFetcher(key.matchingKey()),
                         mStorageContainer.getMySegmentsStorage(key.matchingKey()),
-                        eventsManager
-                )
-        );
+                        mStorageContainer.getMyLargeSegmentsStorage(key.matchingKey()),
+                        eventsManager));
     }
 
     private void connectToStreaming() {
diff --git a/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java b/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java
index 0aa6c88f0..adb47d63a 100644
--- a/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java
+++ b/src/main/java/io/split/android/client/storage/cipher/ApplyCipherTask.java
@@ -13,8 +13,12 @@
 import io.split.android.client.storage.db.ImpressionEntity;
 import io.split.android.client.storage.db.ImpressionsCountDao;
 import io.split.android.client.storage.db.ImpressionsCountEntity;
+import io.split.android.client.storage.db.MyLargeSegmentDao;
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
 import io.split.android.client.storage.db.MySegmentDao;
 import io.split.android.client.storage.db.MySegmentEntity;
+import io.split.android.client.storage.db.SegmentDao;
+import io.split.android.client.storage.db.SegmentEntity;
 import io.split.android.client.storage.db.SplitDao;
 import io.split.android.client.storage.db.SplitEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
@@ -47,6 +51,7 @@ public SplitTaskExecutionInfo execute() {
                 public void run() {
                     updateSplits(mSplitDatabase.splitDao());
                     updateSegments(mSplitDatabase.mySegmentDao());
+                    updateLargeSegments(mSplitDatabase.myLargeSegmentDao());
                     updateImpressions(mSplitDatabase.impressionDao());
                     updateEvents(mSplitDatabase.eventDao());
                     updateImpressionsCount(mSplitDatabase.impressionsCountDao());
@@ -140,7 +145,17 @@ private void updateImpressions(ImpressionDao impressionDao) {
     private void updateSegments(MySegmentDao mySegmentDao) {
         List<MySegmentEntity> items = mySegmentDao.getAll();
 
-        for (MySegmentEntity item : items) {
+        updateSegments(mySegmentDao, items);
+    }
+
+    private void updateLargeSegments(MyLargeSegmentDao myLargeSegmentDao) {
+        List<MyLargeSegmentEntity> items = myLargeSegmentDao.getAll();
+
+        updateSegments(myLargeSegmentDao, items);
+    }
+
+    private void updateSegments(SegmentDao<? extends SegmentEntity> mySegmentDao, List<? extends SegmentEntity> items) {
+        for (SegmentEntity item : items) {
             String userKey = item.getUserKey();
             String fromUserKey = mFromCipher.decrypt(userKey);
             String fromBody = mFromCipher.decrypt(item.getSegmentList());
@@ -151,7 +166,7 @@ private void updateSegments(MySegmentDao mySegmentDao) {
             if (toUserKey != null && toBody != null) {
                 mySegmentDao.update(userKey, toUserKey, toBody);
             } else {
-                Logger.e("Error applying cipher to my segment");
+                Logger.e("Error applying cipher to my " + (item instanceof MyLargeSegmentEntity ? "large" : "") + " segment");
             }
         }
     }
diff --git a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java
index 837ff6390..af0f62f98 100644
--- a/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java
+++ b/src/main/java/io/split/android/client/storage/common/SplitStorageContainer.java
@@ -24,6 +24,7 @@ public class SplitStorageContainer {
 
     private final SplitsStorage mSplitStorage;
     private final MySegmentsStorageContainer mMySegmentsStorageContainer;
+    private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
     private final PersistentSplitsStorage mPersistentSplitsStorage;
     private final PersistentEventsStorage mPersistentEventsStorage;
     private final EventsStorage mEventsStorage;
@@ -38,6 +39,7 @@ public class SplitStorageContainer {
 
     public SplitStorageContainer(@NonNull SplitsStorage splitStorage,
                                  @NonNull MySegmentsStorageContainer mySegmentsStorageContainer,
+                                 @NonNull MySegmentsStorageContainer myLargeSegmentsStorageContainer,
                                  @NonNull PersistentSplitsStorage persistentSplitsStorage,
                                  @NonNull EventsStorage eventsStorage,
                                  @NonNull PersistentEventsStorage persistentEventsStorage,
@@ -52,6 +54,7 @@ public SplitStorageContainer(@NonNull SplitsStorage splitStorage,
 
         mSplitStorage = checkNotNull(splitStorage);
         mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer);
+        mMyLargeSegmentsStorageContainer = checkNotNull(myLargeSegmentsStorageContainer);
         mPersistentSplitsStorage = checkNotNull(persistentSplitsStorage);
         mEventsStorage = checkNotNull(eventsStorage);
         mPersistentEventsStorage = checkNotNull(persistentEventsStorage);
@@ -73,10 +76,18 @@ public MySegmentsStorageContainer getMySegmentsStorageContainer() {
         return mMySegmentsStorageContainer;
     }
 
+    public MySegmentsStorageContainer getMyLargeSegmentsStorageContainer() {
+        return mMyLargeSegmentsStorageContainer;
+    }
+
     public MySegmentsStorage getMySegmentsStorage(String matchingKey) {
         return mMySegmentsStorageContainer.getStorageForKey(matchingKey);
     }
 
+    public MySegmentsStorage getMyLargeSegmentsStorage(String matchingKey) {
+        return mMyLargeSegmentsStorageContainer.getStorageForKey(matchingKey);
+    }
+
     public PersistentSplitsStorage getPersistentSplitsStorage() {
         return mPersistentSplitsStorage;
     }
diff --git a/src/main/java/io/split/android/client/storage/db/MyLargeSegmentDao.java b/src/main/java/io/split/android/client/storage/db/MyLargeSegmentDao.java
new file mode 100644
index 000000000..c770c753a
--- /dev/null
+++ b/src/main/java/io/split/android/client/storage/db/MyLargeSegmentDao.java
@@ -0,0 +1,30 @@
+package io.split.android.client.storage.db;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import java.util.List;
+
+@Dao
+public interface MyLargeSegmentDao extends SegmentDao<MyLargeSegmentEntity> {
+
+    String TABLE_NAME = "my_large_segments";
+
+    @Override
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    void update(MyLargeSegmentEntity mySegment);
+
+    @Override
+    @Query("UPDATE " + TABLE_NAME + " SET user_key = :userKey, segment_list = :segmentList WHERE user_key = :formerUserKey")
+    void update(String formerUserKey, String userKey, String segmentList);
+
+    @Override
+    @Query("SELECT user_key, segment_list, updated_at FROM " + TABLE_NAME + " WHERE user_key = :userKey")
+    MyLargeSegmentEntity getByUserKey(String userKey);
+
+    @Override
+    @Query("SELECT user_key, segment_list, updated_at FROM " + TABLE_NAME)
+    List<MyLargeSegmentEntity> getAll();
+}
diff --git a/src/main/java/io/split/android/client/storage/db/MyLargeSegmentEntity.java b/src/main/java/io/split/android/client/storage/db/MyLargeSegmentEntity.java
new file mode 100644
index 000000000..1a7cd76fc
--- /dev/null
+++ b/src/main/java/io/split/android/client/storage/db/MyLargeSegmentEntity.java
@@ -0,0 +1,23 @@
+package io.split.android.client.storage.db;
+
+import androidx.room.Entity;
+
+@Entity(tableName = "my_large_segments")
+public class MyLargeSegmentEntity extends SegmentEntity {
+
+    public static final Creator<MyLargeSegmentEntity> CREATOR = new Creator<MyLargeSegmentEntity>() {
+        @Override
+        public MyLargeSegmentEntity createEntity(String userKey, String segmentList, long updatedAt) {
+            MyLargeSegmentEntity entity = new MyLargeSegmentEntity();
+            entity.setUserKey(userKey);
+            entity.setSegmentList(segmentList);
+            entity.setUpdatedAt(updatedAt);
+
+            return entity;
+        }
+    };
+
+    public static Creator<MyLargeSegmentEntity> creator() {
+        return CREATOR;
+    }
+}
diff --git a/src/main/java/io/split/android/client/storage/db/MySegmentDao.java b/src/main/java/io/split/android/client/storage/db/MySegmentDao.java
index a43ab3cd9..b4c6ef5d7 100644
--- a/src/main/java/io/split/android/client/storage/db/MySegmentDao.java
+++ b/src/main/java/io/split/android/client/storage/db/MySegmentDao.java
@@ -8,17 +8,23 @@
 import java.util.List;
 
 @Dao
-public interface MySegmentDao {
+public interface MySegmentDao extends SegmentDao<MySegmentEntity> {
 
+    String TABLE_NAME = "my_segments";
+
+    @Override
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     void update(MySegmentEntity mySegment);
 
-    @Query("UPDATE my_segments SET user_key = :userKey, segment_list = :segmentList WHERE user_key = :formerUserKey")
+    @Override
+    @Query("UPDATE " + TABLE_NAME + " SET user_key = :userKey, segment_list = :segmentList WHERE user_key = :formerUserKey")
     void update(String formerUserKey, String userKey, String segmentList);
 
-    @Query("SELECT user_key, segment_list, updated_at FROM my_segments WHERE user_key = :userKey")
+    @Override
+    @Query("SELECT user_key, segment_list, updated_at FROM " + TABLE_NAME + " WHERE user_key = :userKey")
     MySegmentEntity getByUserKey(String userKey);
 
-    @Query("SELECT user_key, segment_list, updated_at FROM my_segments")
+    @Override
+    @Query("SELECT user_key, segment_list, updated_at FROM " + TABLE_NAME)
     List<MySegmentEntity> getAll();
 }
diff --git a/src/main/java/io/split/android/client/storage/db/MySegmentEntity.java b/src/main/java/io/split/android/client/storage/db/MySegmentEntity.java
index 13d24581c..8c93e2dc2 100644
--- a/src/main/java/io/split/android/client/storage/db/MySegmentEntity.java
+++ b/src/main/java/io/split/android/client/storage/db/MySegmentEntity.java
@@ -1,48 +1,23 @@
 package io.split.android.client.storage.db;
 
-import androidx.annotation.NonNull;
-import androidx.room.ColumnInfo;
 import androidx.room.Entity;
-import androidx.room.PrimaryKey;
 
 @Entity(tableName = "my_segments")
-public class MySegmentEntity {
-
-    @PrimaryKey()
-    @NonNull
-    @ColumnInfo(name = "user_key")
-    private String userKey;
-
-    @NonNull
-    @ColumnInfo(name = "segment_list")
-    private String segmentList;
-
-    @ColumnInfo(name = "updated_at")
-    private long updatedAt;
-
-    @NonNull
-    public String getUserKey() {
-        return userKey;
-    }
-
-    public void setUserKey(String userKey) {
-        this.userKey = userKey;
-    }
-
-    @NonNull
-    public String getSegmentList() {
-        return segmentList;
-    }
-
-    public void setSegmentList(@NonNull String segmentList) {
-        this.segmentList = segmentList;
-    }
-
-    public long getUpdatedAt() {
-        return updatedAt;
-    }
-
-    public void setUpdatedAt(long updatedAt) {
-        this.updatedAt = updatedAt;
+public class MySegmentEntity extends SegmentEntity {
+
+    private final static Creator<MySegmentEntity> CREATOR = new Creator<MySegmentEntity>() {
+        @Override
+        public MySegmentEntity createEntity(String userKey, String segmentList, long updatedAt) {
+            MySegmentEntity entity = new MySegmentEntity();
+            entity.setUserKey(userKey);
+            entity.setSegmentList(segmentList);
+            entity.setUpdatedAt(updatedAt);
+
+            return entity;
+        }
+    };
+
+    public static Creator<MySegmentEntity> creator() {
+        return CREATOR;
     }
 }
diff --git a/src/main/java/io/split/android/client/storage/db/SegmentDao.java b/src/main/java/io/split/android/client/storage/db/SegmentDao.java
new file mode 100644
index 000000000..6f6e45a66
--- /dev/null
+++ b/src/main/java/io/split/android/client/storage/db/SegmentDao.java
@@ -0,0 +1,14 @@
+package io.split.android.client.storage.db;
+
+import java.util.List;
+
+public interface SegmentDao<T> {
+
+    void update(T mySegment);
+
+    void update(String formerUserKey, String userKey, String segmentList);
+
+    T getByUserKey(String userKey);
+
+    List<T> getAll();
+}
diff --git a/src/main/java/io/split/android/client/storage/db/SegmentEntity.java b/src/main/java/io/split/android/client/storage/db/SegmentEntity.java
new file mode 100644
index 000000000..f379ec631
--- /dev/null
+++ b/src/main/java/io/split/android/client/storage/db/SegmentEntity.java
@@ -0,0 +1,57 @@
+package io.split.android.client.storage.db;
+
+import androidx.annotation.NonNull;
+import androidx.room.ColumnInfo;
+import androidx.room.PrimaryKey;
+
+/**
+ * Base class for segment entities. This entity does not correspond to any actual SQL table
+ */
+public abstract class SegmentEntity {
+
+    @PrimaryKey()
+    @NonNull
+    @ColumnInfo(name = "user_key")
+    private String userKey;
+
+    @NonNull
+    @ColumnInfo(name = "segment_list")
+    private String segmentList;
+
+    @ColumnInfo(name = "updated_at")
+    private long updatedAt;
+
+    @NonNull
+    public String getUserKey() {
+        return userKey;
+    }
+
+    public void setUserKey(String userKey) {
+        this.userKey = userKey;
+    }
+
+    @NonNull
+    public String getSegmentList() {
+        return segmentList;
+    }
+
+    public void setSegmentList(@NonNull String segmentList) {
+        this.segmentList = segmentList;
+    }
+
+    public long getUpdatedAt() {
+        return updatedAt;
+    }
+
+    public void setUpdatedAt(long updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+
+    public static Creator<?> creator() {
+        return null;
+    }
+
+    public interface Creator<T extends SegmentEntity> {
+        T createEntity(String userKey, String segmentList, long updatedAt);
+    }
+}
diff --git a/src/main/java/io/split/android/client/storage/db/SplitRoomDatabase.java b/src/main/java/io/split/android/client/storage/db/SplitRoomDatabase.java
index 8124e5ea3..273208e52 100644
--- a/src/main/java/io/split/android/client/storage/db/SplitRoomDatabase.java
+++ b/src/main/java/io/split/android/client/storage/db/SplitRoomDatabase.java
@@ -23,14 +23,17 @@
         entities = {
                 MySegmentEntity.class, SplitEntity.class, EventEntity.class,
                 ImpressionEntity.class, GeneralInfoEntity.class, ImpressionsCountEntity.class,
-                AttributesEntity.class, UniqueKeyEntity.class, ImpressionsObserverCacheEntity.class
+                AttributesEntity.class, UniqueKeyEntity.class, ImpressionsObserverCacheEntity.class,
+                MyLargeSegmentEntity.class
         },
-        version = 5
+        version = 6
 )
 public abstract class SplitRoomDatabase extends RoomDatabase {
 
     public abstract MySegmentDao mySegmentDao();
 
+    public abstract MyLargeSegmentDao myLargeSegmentDao();
+
     public abstract SplitDao splitDao();
 
     public abstract EventDao eventDao();
diff --git a/src/main/java/io/split/android/client/storage/db/StorageFactory.java b/src/main/java/io/split/android/client/storage/db/StorageFactory.java
index ad9bf63f0..f4d6b9be1 100644
--- a/src/main/java/io/split/android/client/storage/db/StorageFactory.java
+++ b/src/main/java/io/split/android/client/storage/db/StorageFactory.java
@@ -55,6 +55,14 @@ public static MySegmentsStorageContainer getMySegmentsStorageForWorker(SplitRoom
         return getMySegmentsStorageContainer(splitRoomDatabase, SplitCipherFactory.create(apiKey, encryptionEnabled));
     }
 
+    public static MySegmentsStorageContainer getMyLargeSegmentsStorage(SplitRoomDatabase splitRoomDatabase, SplitCipher splitCipher) {
+        return getMyLargeSegmentsStorageContainer(splitRoomDatabase, splitCipher);
+    }
+
+    public static MySegmentsStorageContainer getMyLargeSegmentsStorageForWorker(SplitRoomDatabase splitRoomDatabase, String apiKey, boolean encryptionEnabled) {
+        return getMyLargeSegmentsStorageContainer(splitRoomDatabase, SplitCipherFactory.create(apiKey, encryptionEnabled));
+    }
+
     public static EventsStorage getEventsStorage(PersistentEventsStorage persistentEventsStorage,
                                                  boolean isPersistenceEnabled) {
         return new EventsStorage(persistentEventsStorage, isPersistenceEnabled);
@@ -124,7 +132,11 @@ public static TelemetryStorage getTelemetryStorage(boolean shouldRecordTelemetry
     }
 
     private static MySegmentsStorageContainer getMySegmentsStorageContainer(SplitRoomDatabase splitRoomDatabase, SplitCipher splitCipher) {
-        return new MySegmentsStorageContainerImpl(new SqLitePersistentMySegmentsStorage(splitRoomDatabase, splitCipher));
+        return new MySegmentsStorageContainerImpl(new SqLitePersistentMySegmentsStorage<>(splitCipher, splitRoomDatabase.mySegmentDao(), MySegmentEntity.creator()));
+    }
+
+    private static MySegmentsStorageContainer getMyLargeSegmentsStorageContainer(SplitRoomDatabase splitRoomDatabase, SplitCipher splitCipher) {
+        return new MySegmentsStorageContainerImpl(new SqLitePersistentMySegmentsStorage<>(splitCipher, splitRoomDatabase.myLargeSegmentDao(), MyLargeSegmentEntity.creator()));
     }
 
     private static AttributesStorageContainer getAttributesStorageContainerInstance() {
diff --git a/src/main/java/io/split/android/client/storage/mysegments/EmptyMySegmentsStorage.java b/src/main/java/io/split/android/client/storage/mysegments/EmptyMySegmentsStorage.java
index 584bad5e6..39a65bf89 100644
--- a/src/main/java/io/split/android/client/storage/mysegments/EmptyMySegmentsStorage.java
+++ b/src/main/java/io/split/android/client/storage/mysegments/EmptyMySegmentsStorage.java
@@ -1,11 +1,10 @@
 package io.split.android.client.storage.mysegments;
 
-import androidx.annotation.NonNull;
-
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
+import io.split.android.client.dtos.SegmentsChange;
+
 public class EmptyMySegmentsStorage implements MySegmentsStorage{
     @Override
     public void loadLocal() {
@@ -17,7 +16,12 @@ public Set<String> getAll() {
     }
 
     @Override
-    public void set(@NonNull List<String> mySegments) {
+    public void set(SegmentsChange segmentsChange) {
+    }
+
+    @Override
+    public long getChangeNumber() {
+        return -1;
     }
 
     @Override
diff --git a/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorage.java b/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorage.java
index 3b05da6c3..351a82d79 100644
--- a/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorage.java
+++ b/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorage.java
@@ -1,16 +1,20 @@
 package io.split.android.client.storage.mysegments;
 
-import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
-import java.util.List;
 import java.util.Set;
 
+import io.split.android.client.dtos.SegmentsChange;
+
 public interface MySegmentsStorage {
     void loadLocal();
 
     Set<String> getAll();
 
-    void set(@NonNull List<String> mySegments);
+    void set(SegmentsChange segmentsChange);
+
+    long getChangeNumber();
 
+    @VisibleForTesting
     void clear();
 }
diff --git a/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorageImpl.java b/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorageImpl.java
index 7688fdda3..4478a9a81 100644
--- a/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorageImpl.java
+++ b/src/main/java/io/split/android/client/storage/mysegments/MySegmentsStorageImpl.java
@@ -3,28 +3,38 @@
 import static io.split.android.client.utils.Utils.checkNotNull;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import io.split.android.client.dtos.Segment;
+import io.split.android.client.dtos.SegmentsChange;
 
 class MySegmentsStorageImpl implements MySegmentsStorage {
 
+    public static final int DEFAULT_CHANGE_NUMBER = -1;
     private final String mMatchingKey;
     private final PersistentMySegmentsStorage mPersistentStorage;
     private final Set<String> mInMemoryMySegments;
+    private final AtomicLong mTill;
 
     public MySegmentsStorageImpl(@NonNull String matchingKey, @NonNull PersistentMySegmentsStorage persistentStorage) {
         mPersistentStorage = checkNotNull(persistentStorage);
         mMatchingKey = checkNotNull(matchingKey);
         mInMemoryMySegments = Collections.newSetFromMap(new ConcurrentHashMap<>());
+        mTill = new AtomicLong(DEFAULT_CHANGE_NUMBER);
     }
 
     @Override
     public void loadLocal() {
-        mInMemoryMySegments.addAll(mPersistentStorage.getSnapshot(mMatchingKey));
+        SegmentsChange snapshot = mPersistentStorage.getSnapshot(mMatchingKey);
+        mInMemoryMySegments.addAll(toNames(snapshot.getSegments()));
+        mTill.set(getOrDefault(snapshot.getChangeNumber()));
     }
 
     @Override
@@ -33,18 +43,45 @@ public Set<String> getAll() {
     }
 
     @Override
-    public void set(@NonNull List<String> mySegments) {
-        if (mySegments == null) {
+    public void set(@NonNull SegmentsChange segmentsChange) {
+        if (segmentsChange == null) {
             return;
         }
         mInMemoryMySegments.clear();
-        mInMemoryMySegments.addAll(mySegments);
-        mPersistentStorage.set(mMatchingKey, mySegments);
+        mInMemoryMySegments.addAll(toNames(segmentsChange.getSegments()));
+        mTill.set(getOrDefault(segmentsChange.getChangeNumber()));
+        mPersistentStorage.set(mMatchingKey, segmentsChange);
+    }
+
+    @Override
+    public long getChangeNumber() {
+        return mTill.get();
     }
 
     @Override
+    @VisibleForTesting
     public void clear() {
         mInMemoryMySegments.clear();
-        mPersistentStorage.set(mMatchingKey, new ArrayList<>());
+        mTill.set(DEFAULT_CHANGE_NUMBER);
+        mPersistentStorage.set(mMatchingKey, SegmentsChange.createEmpty());
+    }
+
+    @NonNull
+    private static Set<String> toNames(Set<Segment> segments) {
+        if (segments == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> names = new HashSet<>();
+        for (Segment segment : segments) {
+            names.add(segment.getName());
+        }
+
+        return names;
+    }
+
+    @NonNull
+    private static Long getOrDefault(@Nullable Long changeNumber) {
+        return changeNumber == null ? DEFAULT_CHANGE_NUMBER : changeNumber;
     }
 }
diff --git a/src/main/java/io/split/android/client/storage/mysegments/PersistentMySegmentsStorage.java b/src/main/java/io/split/android/client/storage/mysegments/PersistentMySegmentsStorage.java
index 633b62d04..03ad60ae8 100644
--- a/src/main/java/io/split/android/client/storage/mysegments/PersistentMySegmentsStorage.java
+++ b/src/main/java/io/split/android/client/storage/mysegments/PersistentMySegmentsStorage.java
@@ -1,14 +1,12 @@
 package io.split.android.client.storage.mysegments;
 
-import androidx.annotation.NonNull;
-
-import java.util.List;
+import io.split.android.client.dtos.SegmentsChange;
 
 public interface PersistentMySegmentsStorage {
 
-    void set(String userKey, @NonNull List<String> mySegments);
+    void set(String userKey, SegmentsChange segmentsChange);
 
-    List<String> getSnapshot(String userKey);
+    SegmentsChange getSnapshot(String userKey);
 
     void close();
 }
diff --git a/src/main/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorage.java b/src/main/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorage.java
index 80d5c80b6..8d2da826d 100644
--- a/src/main/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorage.java
+++ b/src/main/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorage.java
@@ -1,72 +1,79 @@
 package io.split.android.client.storage.mysegments;
 
+import static io.split.android.client.dtos.SegmentsChange.createEmpty;
 import static io.split.android.client.utils.Utils.checkNotNull;
 
 import androidx.annotation.NonNull;
 
-import java.util.ArrayList;
+import com.google.gson.JsonParseException;
+
 import java.util.Arrays;
-import java.util.List;
+import java.util.HashSet;
 
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.storage.cipher.SplitCipher;
-import io.split.android.client.storage.db.MySegmentEntity;
-import io.split.android.client.storage.db.SplitRoomDatabase;
-import io.split.android.client.utils.StringHelper;
+import io.split.android.client.storage.db.SegmentDao;
+import io.split.android.client.storage.db.SegmentEntity;
+import io.split.android.client.utils.Json;
 import io.split.android.client.utils.Utils;
 import io.split.android.client.utils.logger.Logger;
 
-public class SqLitePersistentMySegmentsStorage implements PersistentMySegmentsStorage {
+public class SqLitePersistentMySegmentsStorage<T extends SegmentEntity> implements PersistentMySegmentsStorage {
 
-    private final SplitRoomDatabase mDatabase;
-    private final StringHelper mStringHelper;
+    private final SegmentDao<T> mDao;
     private final SplitCipher mSplitCipher;
+    private final SegmentEntity.Creator<T> mCreator;
 
-    public SqLitePersistentMySegmentsStorage(@NonNull SplitRoomDatabase database,
-                                             @NonNull SplitCipher splitCipher) {
-        mDatabase = checkNotNull(database);
-        mStringHelper = new StringHelper();
+    public SqLitePersistentMySegmentsStorage(@NonNull SplitCipher splitCipher, SegmentDao<T> mySegmentDao, SegmentEntity.Creator<T> creator) {
+        mDao = mySegmentDao;
         mSplitCipher = checkNotNull(splitCipher);
+        mCreator = checkNotNull(creator);
     }
 
     @Override
-    public void set(String userKey, @NonNull List<String> mySegments) {
-        if (mySegments == null) {
+    public void set(String userKey, SegmentsChange segmentsChange) {
+        if (segmentsChange == null || segmentsChange.getSegments() == null) {
             return;
         }
 
         String encryptedUserKey = mSplitCipher.encrypt(userKey);
-        String encryptedSegmentList = mSplitCipher.encrypt(mStringHelper.join(",", mySegments));
-        if (encryptedUserKey == null || encryptedSegmentList == null) {
+        String dto = Json.toJson(segmentsChange);
+        String encryptedDto = mSplitCipher.encrypt(dto);
+        if (encryptedUserKey == null || encryptedDto == null) {
             Logger.e("Error encrypting my segments");
             return;
         }
-        MySegmentEntity entity = new MySegmentEntity();
-        entity.setUserKey(encryptedUserKey);
-        entity.setSegmentList(encryptedSegmentList);
-        entity.setUpdatedAt(System.currentTimeMillis() / 1000);
-        mDatabase.mySegmentDao().update(entity);
+        T entity = mCreator.createEntity(encryptedUserKey, encryptedDto, System.currentTimeMillis() / 1000);
+        mDao.update(entity);
     }
 
     @Override
-    public List<String> getSnapshot(String userKey) {
+    public SegmentsChange getSnapshot(String userKey) {
         String encryptedUserKey = mSplitCipher.encrypt(userKey);
-        return getMySegmentsFromEntity(mDatabase.mySegmentDao().getByUserKey(encryptedUserKey));
+        return getMySegmentsFromEntity(mDao.getByUserKey(encryptedUserKey));
     }
 
     @Override
     public void close() {
     }
 
-    private List<String> getMySegmentsFromEntity(MySegmentEntity entity) {
+    private SegmentsChange getMySegmentsFromEntity(SegmentEntity entity) {
         if (entity == null || Utils.isNullOrEmpty(entity.getSegmentList())) {
-            return new ArrayList<>();
+            return createEmpty();
+        }
+
+        String storedJson = mSplitCipher.decrypt(entity.getSegmentList());
+        if (storedJson == null) {
+            return createEmpty();
         }
 
-        String segmentList = mSplitCipher.decrypt(entity.getSegmentList());
-        if (segmentList == null) {
-            return new ArrayList<>();
+        try {
+            return Json.fromJson(storedJson, SegmentsChange.class);
+        } catch (JsonParseException | NullPointerException ex) {
+            Logger.v("Parsing of segments DTO failed, returning as legacy");
+            String[] segments = storedJson.split(",");
+
+            return SegmentsChange.create(new HashSet<>(Arrays.asList(segments)), null);
         }
-        String[] segments = segmentList.split(",");
-        return Arrays.asList(segments);
     }
 }
diff --git a/src/main/java/io/split/android/client/telemetry/model/Config.java b/src/main/java/io/split/android/client/telemetry/model/Config.java
index bf6849271..5c24ab0cd 100644
--- a/src/main/java/io/split/android/client/telemetry/model/Config.java
+++ b/src/main/java/io/split/android/client/telemetry/model/Config.java
@@ -66,6 +66,12 @@ public class Config {
     @SerializedName("fsI")
     private int flagSetsInvalid;
 
+    @SerializedName("lsE")
+    private boolean largeSegmentsEnabled;
+
+    @SerializedName("wls")
+    private boolean waitForLargeSegments;
+
     public int getOperationMode() {
         return operationMode;
     }
@@ -217,4 +223,20 @@ public int getFlagSetsInvalid() {
     public void setFlagSetsInvalid(int flagSetsInvalid) {
         this.flagSetsInvalid = flagSetsInvalid;
     }
+
+    public boolean largeSegmentsEnabled() {
+        return largeSegmentsEnabled;
+    }
+
+    public void setLargeSegmentsEnabled(boolean largeSegmentsEnabled) {
+        this.largeSegmentsEnabled = largeSegmentsEnabled;
+    }
+
+    public boolean getWaitForLargeSegments() {
+        return waitForLargeSegments;
+    }
+
+    public void setWaitForLargeSegments(boolean waitForLargeSegments) {
+        this.waitForLargeSegments = waitForLargeSegments;
+    }
 }
diff --git a/src/main/java/io/split/android/client/telemetry/model/HttpErrors.java b/src/main/java/io/split/android/client/telemetry/model/HttpErrors.java
index 75352efaa..da32619ca 100644
--- a/src/main/java/io/split/android/client/telemetry/model/HttpErrors.java
+++ b/src/main/java/io/split/android/client/telemetry/model/HttpErrors.java
@@ -12,6 +12,9 @@ public class HttpErrors {
     @SerializedName("ms")
     private Map<Long, Long> mySegmentSyncErrs;
 
+    @SerializedName("mls")
+    private Map<Long, Long> myLargeSegmentsSyncErrs;
+
     @SerializedName("im")
     private Map<Long, Long> impressionSyncErrs;
 
@@ -43,6 +46,14 @@ public void setMySegmentSyncErrs(Map<Long, Long> mySegmentSyncErrs) {
         this.mySegmentSyncErrs = mySegmentSyncErrs;
     }
 
+    public Map<Long, Long> getMyLargeSegmentsSyncErrs() {
+        return myLargeSegmentsSyncErrs;
+    }
+
+    public void setMyLargeSegmentsSyncErrs(Map<Long, Long> myLargeSegmentsSyncErrs) {
+        this.myLargeSegmentsSyncErrs = myLargeSegmentsSyncErrs;
+    }
+
     public Map<Long, Long> getImpressionSyncErrs() {
         return impressionSyncErrs;
     }
diff --git a/src/main/java/io/split/android/client/telemetry/model/HttpLatencies.java b/src/main/java/io/split/android/client/telemetry/model/HttpLatencies.java
index 22f80c6c2..d58e647e5 100644
--- a/src/main/java/io/split/android/client/telemetry/model/HttpLatencies.java
+++ b/src/main/java/io/split/android/client/telemetry/model/HttpLatencies.java
@@ -12,6 +12,9 @@ public class HttpLatencies {
     @SerializedName("ms")
     private List<Long> mySegments;
 
+    @SerializedName("mls")
+    private List<Long> myLargeSegments;
+
     @SerializedName("im")
     private List<Long> impressions;
 
@@ -43,6 +46,14 @@ public void setMySegments(List<Long> mySegments) {
         this.mySegments = mySegments;
     }
 
+    public List<Long> getMyLargeSegments() {
+        return myLargeSegments;
+    }
+
+    public void setMyLargeSegments(List<Long> myLargeSegments) {
+        this.myLargeSegments = myLargeSegments;
+    }
+
     public List<Long> getImpressions() {
         return impressions;
     }
diff --git a/src/main/java/io/split/android/client/telemetry/model/LastSync.java b/src/main/java/io/split/android/client/telemetry/model/LastSync.java
index 0f4dc5df3..e0e65653b 100644
--- a/src/main/java/io/split/android/client/telemetry/model/LastSync.java
+++ b/src/main/java/io/split/android/client/telemetry/model/LastSync.java
@@ -5,25 +5,28 @@
 public class LastSync {
 
     @SerializedName("sp")
-    private long lastSplitSync;
+    private Long lastSplitSync;
 
     @SerializedName("ms")
-    private long lastMySegmentSync;
+    private Long lastMySegmentSync;
+
+    @SerializedName("mls")
+    private Long lastMyLargeSegmentSync;
 
     @SerializedName("im")
-    private long lastImpressionSync;
+    private Long lastImpressionSync;
 
     @SerializedName("ic")
-    private long lastImpressionCountSync;
+    private Long lastImpressionCountSync;
 
     @SerializedName("ev")
-    private long lastEventSync;
+    private Long lastEventSync;
 
     @SerializedName("te")
-    private long lastTelemetrySync;
+    private Long lastTelemetrySync;
 
     @SerializedName("to")
-    private long lastTokenRefresh;
+    private Long lastTokenRefresh;
 
     public long getLastSplitSync() {
         return lastSplitSync;
@@ -41,6 +44,14 @@ public void setLastMySegmentSync(long lastMySegmentSync) {
         this.lastMySegmentSync = lastMySegmentSync;
     }
 
+    public long getLastMyLargeSegmentSync() {
+        return lastMyLargeSegmentSync;
+    }
+
+    public void setLastMyLargeSegmentSync(long lastMyLargeSegmentSync) {
+        this.lastMyLargeSegmentSync = lastMyLargeSegmentSync;
+    }
+
     public long getLastImpressionSync() {
         return lastImpressionSync;
     }
diff --git a/src/main/java/io/split/android/client/telemetry/model/OperationType.java b/src/main/java/io/split/android/client/telemetry/model/OperationType.java
index edc8df96a..abd691e31 100644
--- a/src/main/java/io/split/android/client/telemetry/model/OperationType.java
+++ b/src/main/java/io/split/android/client/telemetry/model/OperationType.java
@@ -7,5 +7,6 @@ public enum OperationType {
     EVENTS,
     TELEMETRY,
     TOKEN,
-    MY_SEGMENT
+    MY_SEGMENT,
+    MY_LARGE_SEGMENT,
 }
diff --git a/src/main/java/io/split/android/client/telemetry/model/RefreshRates.java b/src/main/java/io/split/android/client/telemetry/model/RefreshRates.java
index 6588f2f3b..b5492a828 100644
--- a/src/main/java/io/split/android/client/telemetry/model/RefreshRates.java
+++ b/src/main/java/io/split/android/client/telemetry/model/RefreshRates.java
@@ -10,6 +10,9 @@ public class RefreshRates {
     @SerializedName("ms")
     private long mySegments;
 
+    @SerializedName("mls")
+    private long myLargeSegments;
+
     @SerializedName("im")
     private long impressions;
 
@@ -35,6 +38,14 @@ public void setMySegments(long mySegments) {
         this.mySegments = mySegments;
     }
 
+    public long getMyLargeSegments() {
+        return myLargeSegments;
+    }
+
+    public void setMyLargeSegments(long myLargeSegments) {
+        this.myLargeSegments = myLargeSegments;
+    }
+
     public long getImpressions() {
         return impressions;
     }
diff --git a/src/main/java/io/split/android/client/telemetry/model/Stats.java b/src/main/java/io/split/android/client/telemetry/model/Stats.java
index 7b1a169e4..06438619a 100644
--- a/src/main/java/io/split/android/client/telemetry/model/Stats.java
+++ b/src/main/java/io/split/android/client/telemetry/model/Stats.java
@@ -46,6 +46,9 @@ public class Stats {
     @SerializedName("seC")
     private long segmentCount;
 
+    @SerializedName("lsC")
+    private long largeSegmentCount;
+
     @SerializedName("skC")
     private final long segmentKeyCount = 1;
 
@@ -115,6 +118,10 @@ public void setSegmentCount(long segmentCount) {
         this.segmentCount = segmentCount;
     }
 
+    public void setLargeSegmentCount(long largeSegmentCount) {
+        this.largeSegmentCount = largeSegmentCount;
+    }
+
     public void setSessionLengthMs(long sessionLengthMs) {
         this.sessionLengthMs = sessionLengthMs;
     }
@@ -207,6 +214,11 @@ public long getSegmentCount() {
         return segmentCount;
     }
 
+    @VisibleForTesting
+    public long getLargeSegmentCount() {
+        return largeSegmentCount;
+    }
+
     @VisibleForTesting
     public long getSessionLengthMs() {
         return sessionLengthMs;
diff --git a/src/main/java/io/split/android/client/telemetry/model/UpdatesFromSSE.java b/src/main/java/io/split/android/client/telemetry/model/UpdatesFromSSE.java
index 48b30c9a5..24504a0c9 100644
--- a/src/main/java/io/split/android/client/telemetry/model/UpdatesFromSSE.java
+++ b/src/main/java/io/split/android/client/telemetry/model/UpdatesFromSSE.java
@@ -10,9 +10,13 @@ public class UpdatesFromSSE {
     @SerializedName("ms")
     private long mMySegments;
 
-    public UpdatesFromSSE(long splits, long mySegments) {
+    @SerializedName("mls")
+    private long mMyLargeSegments;
+
+    public UpdatesFromSSE(long splits, long mySegments, long myLargeSegments) {
         mSplits = splits;
         mMySegments = mySegments;
+        mMyLargeSegments = myLargeSegments;
     }
 
     public long getSplits() {
@@ -22,4 +26,8 @@ public long getSplits() {
     public long getMySegments() {
         return mMySegments;
     }
+
+    public long getMyLargeSegments() {
+        return mMyLargeSegments;
+    }
 }
diff --git a/src/main/java/io/split/android/client/telemetry/model/streaming/UpdatesFromSSEEnum.java b/src/main/java/io/split/android/client/telemetry/model/streaming/UpdatesFromSSEEnum.java
index 1e4a1acaf..add69333d 100644
--- a/src/main/java/io/split/android/client/telemetry/model/streaming/UpdatesFromSSEEnum.java
+++ b/src/main/java/io/split/android/client/telemetry/model/streaming/UpdatesFromSSEEnum.java
@@ -4,4 +4,5 @@ public enum UpdatesFromSSEEnum {
 
     SPLITS,
     MY_SEGMENTS,
+    MY_LARGE_SEGMENTS,
 }
diff --git a/src/main/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorage.java b/src/main/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorage.java
index 2c824efa2..6ee8507a6 100644
--- a/src/main/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorage.java
+++ b/src/main/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorage.java
@@ -181,6 +181,7 @@ public LastSync getLastSynchronization() {
         lastSync.setLastEventSync(lastSynchronizationData.get(OperationType.EVENTS).get());
         lastSync.setLastSplitSync(lastSynchronizationData.get(OperationType.SPLITS).get());
         lastSync.setLastMySegmentSync(lastSynchronizationData.get(OperationType.MY_SEGMENT).get());
+        lastSync.setLastMyLargeSegmentSync(lastSynchronizationData.get(OperationType.MY_LARGE_SEGMENT).get());
         lastSync.setLastTelemetrySync(lastSynchronizationData.get(OperationType.TELEMETRY).get());
         lastSync.setLastImpressionSync(lastSynchronizationData.get(OperationType.IMPRESSIONS).get());
         lastSync.setLastImpressionCountSync(lastSynchronizationData.get(OperationType.IMPRESSIONS_COUNT).get());
@@ -199,6 +200,7 @@ public HttpErrors popHttpErrors() {
         errors.setImpressionSyncErrs(httpErrors.get(OperationType.IMPRESSIONS));
         errors.setSplitSyncErrs(httpErrors.get(OperationType.SPLITS));
         errors.setMySegmentSyncErrs(httpErrors.get(OperationType.MY_SEGMENT));
+        errors.setMyLargeSegmentsSyncErrs(httpErrors.get(OperationType.MY_LARGE_SEGMENT));
         errors.setTokenGetErrs(httpErrors.get(OperationType.TOKEN));
 
         initializeHttpErrors();
@@ -215,6 +217,7 @@ public HttpLatencies popHttpLatencies() {
             latencies.setEvents(popLatencies(OperationType.EVENTS));
             latencies.setSplits(popLatencies(OperationType.SPLITS));
             latencies.setMySegments(popLatencies(OperationType.MY_SEGMENT));
+            latencies.setMyLargeSegments(popLatencies(OperationType.MY_LARGE_SEGMENT));
             latencies.setToken(popLatencies(OperationType.TOKEN));
             latencies.setImpressions(popLatencies(OperationType.IMPRESSIONS));
             latencies.setImpressionsCount(popLatencies(OperationType.IMPRESSIONS_COUNT));
@@ -263,6 +266,7 @@ public UpdatesFromSSE popUpdatesFromSSE() {
         synchronized (updatesFromSSELock) {
             long sCount = 0L;
             long mCount = 0L;
+            long lCount = 0L;
 
             AtomicLong splits = updatesFromSSE.get(UpdatesFromSSEEnum.SPLITS);
             if (splits != null) {
@@ -274,7 +278,12 @@ public UpdatesFromSSE popUpdatesFromSSE() {
                 mCount = mySegments.getAndSet(0L);
             }
 
-            return new UpdatesFromSSE(sCount, mCount);
+            AtomicLong myLargeSegments = updatesFromSSE.get(UpdatesFromSSEEnum.MY_LARGE_SEGMENTS);
+            if (myLargeSegments != null) {
+                lCount = myLargeSegments.getAndSet(0L);
+            }
+
+            return new UpdatesFromSSE(sCount, mCount, lCount);
         }
     }
 
@@ -421,6 +430,7 @@ private void initializeLastSynchronizationData() {
         lastSynchronizationData.put(OperationType.TELEMETRY, new AtomicLong());
         lastSynchronizationData.put(OperationType.EVENTS, new AtomicLong());
         lastSynchronizationData.put(OperationType.MY_SEGMENT, new AtomicLong());
+        lastSynchronizationData.put(OperationType.MY_LARGE_SEGMENT, new AtomicLong());
         lastSynchronizationData.put(OperationType.SPLITS, new AtomicLong());
         lastSynchronizationData.put(OperationType.TOKEN, new AtomicLong());
     }
@@ -430,6 +440,7 @@ private void initializeHttpErrors() {
         httpErrors.put(OperationType.SPLITS, new ConcurrentHashMap<>());
         httpErrors.put(OperationType.TELEMETRY, new ConcurrentHashMap<>());
         httpErrors.put(OperationType.MY_SEGMENT, new ConcurrentHashMap<>());
+        httpErrors.put(OperationType.MY_LARGE_SEGMENT, new ConcurrentHashMap<>());
         httpErrors.put(OperationType.IMPRESSIONS_COUNT, new ConcurrentHashMap<>());
         httpErrors.put(OperationType.IMPRESSIONS, new ConcurrentHashMap<>());
         httpErrors.put(OperationType.TOKEN, new ConcurrentHashMap<>());
@@ -441,6 +452,7 @@ private void initializeHttpLatencies() {
         httpLatencies.put(OperationType.TELEMETRY, new BinarySearchLatencyTracker());
         httpLatencies.put(OperationType.IMPRESSIONS_COUNT, new BinarySearchLatencyTracker());
         httpLatencies.put(OperationType.MY_SEGMENT, new BinarySearchLatencyTracker());
+        httpLatencies.put(OperationType.MY_LARGE_SEGMENT, new BinarySearchLatencyTracker());
         httpLatencies.put(OperationType.SPLITS, new BinarySearchLatencyTracker());
         httpLatencies.put(OperationType.TOKEN, new BinarySearchLatencyTracker());
     }
@@ -453,6 +465,7 @@ private void initializePushCounters() {
     private void initializeUpdatesFromSSE() {
         updatesFromSSE.put(UpdatesFromSSEEnum.SPLITS, new AtomicLong());
         updatesFromSSE.put(UpdatesFromSSEEnum.MY_SEGMENTS, new AtomicLong());
+        updatesFromSSE.put(UpdatesFromSSEEnum.MY_LARGE_SEGMENTS, new AtomicLong());
     }
 
     private List<Long> popLatencies(OperationType operationType) {
diff --git a/src/main/java/io/split/android/client/telemetry/storage/TelemetryConfigProviderImpl.java b/src/main/java/io/split/android/client/telemetry/storage/TelemetryConfigProviderImpl.java
index 595cb0b17..839d88f0d 100644
--- a/src/main/java/io/split/android/client/telemetry/storage/TelemetryConfigProviderImpl.java
+++ b/src/main/java/io/split/android/client/telemetry/storage/TelemetryConfigProviderImpl.java
@@ -8,6 +8,8 @@
 import static io.split.android.client.ServiceEndpoints.EndpointValidator.streamingEndpointIsOverridden;
 import static io.split.android.client.ServiceEndpoints.EndpointValidator.telemetryEndpointIsOverridden;
 
+import android.os.Build;
+
 import androidx.annotation.NonNull;
 
 import io.split.android.client.SplitClientConfig;
@@ -15,6 +17,7 @@
 import io.split.android.client.telemetry.model.Config;
 import io.split.android.client.telemetry.model.RefreshRates;
 import io.split.android.client.telemetry.model.UrlOverrides;
+import io.split.android.client.utils.logger.Logger;
 
 public class TelemetryConfigProviderImpl implements TelemetryConfigProvider {
 
@@ -36,6 +39,7 @@ public TelemetryConfigProviderImpl(@NonNull TelemetryStorageConsumer telemetryCo
     @Override
     public Config getConfigTelemetry() {
         Config config = new Config();
+        addDefaultTags(mSplitClientConfig);
         config.setStreamingEnabled(mSplitClientConfig.streamingEnabled());
         config.setRefreshRates(buildRefreshRates(mSplitClientConfig));
         config.setTags(mTelemetryConsumer.popTags());
@@ -84,4 +88,20 @@ private UrlOverrides buildUrlOverrides(SplitClientConfig splitClientConfig) {
 
         return urlOverrides;
     }
+
+    private void addDefaultTags(SplitClientConfig mSplitClientConfig) {
+        try {
+            TelemetryRuntimeProducer producer = (TelemetryRuntimeProducer) mTelemetryConsumer;
+            int sdkInt = Build.VERSION.SDK_INT;
+            if (sdkInt > 0) {
+                producer.addTag("av:" + sdkInt);
+            }
+
+            if (mSplitClientConfig.synchronizeInBackground()) {
+                producer.addTag("bgr:" + mSplitClientConfig.backgroundSyncPeriod());
+            }
+        } catch (ClassCastException ex) {
+            Logger.d("Telemetry storage is not a producer");
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImpl.java b/src/main/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImpl.java
index 35783b09a..a7f2a2d1b 100644
--- a/src/main/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImpl.java
+++ b/src/main/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImpl.java
@@ -2,6 +2,8 @@
 
 import static io.split.android.client.utils.Utils.checkNotNull;
 
+import android.os.Build;
+
 import androidx.annotation.NonNull;
 
 import io.split.android.client.storage.mysegments.MySegmentsStorageContainer;
@@ -9,21 +11,25 @@
 import io.split.android.client.telemetry.model.EventsDataRecordsEnum;
 import io.split.android.client.telemetry.model.ImpressionsDataType;
 import io.split.android.client.telemetry.model.Stats;
+import io.split.android.client.utils.logger.Logger;
 
 public class TelemetryStatsProviderImpl implements TelemetryStatsProvider {
 
     private final TelemetryStorageConsumer mTelemetryStorageConsumer;
     private final SplitsStorage mSplitsStorage;
     private final MySegmentsStorageContainer mMySegmentsStorageContainer;
+    private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
     private volatile Stats pendingStats = null;
     private final Object mLock = new Object();
 
     public TelemetryStatsProviderImpl(@NonNull TelemetryStorageConsumer telemetryStorageConsumer,
                                       @NonNull SplitsStorage splitsStorage,
-                                      @NonNull MySegmentsStorageContainer mySegmentsStorage) {
+                                      @NonNull MySegmentsStorageContainer mySegmentsStorage,
+                                      @NonNull MySegmentsStorageContainer myLargeSegmentsStorage) {
         mTelemetryStorageConsumer = checkNotNull(telemetryStorageConsumer);
         mSplitsStorage = checkNotNull(splitsStorage);
         mMySegmentsStorageContainer = checkNotNull(mySegmentsStorage);
+        mMyLargeSegmentsStorageContainer = myLargeSegmentsStorage;
     }
 
     @Override
@@ -46,12 +52,16 @@ public void clearStats() {
 
     private Stats buildStats() {
         Stats stats = new Stats();
+        addDefaultTags();
 
         stats.setStreamingEvents(mTelemetryStorageConsumer.popStreamingEvents());
         stats.setSplitCount(mSplitsStorage.getAll().size());
         stats.setTags(mTelemetryStorageConsumer.popTags());
         stats.setMethodLatencies(mTelemetryStorageConsumer.popLatencies());
         stats.setSegmentCount(mMySegmentsStorageContainer.getUniqueAmount());
+        if (mMyLargeSegmentsStorageContainer != null) {
+            stats.setLargeSegmentCount(mMyLargeSegmentsStorageContainer.getUniqueAmount());
+        }
         stats.setSessionLengthMs(mTelemetryStorageConsumer.getSessionLength());
         stats.setLastSynchronizations(mTelemetryStorageConsumer.getLastSynchronization());
         stats.setImpressionsDropped(mTelemetryStorageConsumer.getImpressionsStats(ImpressionsDataType.IMPRESSIONS_DROPPED));
@@ -68,4 +78,16 @@ private Stats buildStats() {
 
         return stats;
     }
+
+    private void addDefaultTags() {
+        try {
+            TelemetryRuntimeProducer producer = (TelemetryRuntimeProducer) mTelemetryStorageConsumer;
+            int sdkInt = Build.VERSION.SDK_INT;
+            if (sdkInt > 0) {
+                producer.addTag("av:" + sdkInt);
+            }
+        } catch (ClassCastException ex) {
+            Logger.d("Telemetry storage is not a producer");
+        }
+    }
 }
diff --git a/src/main/java/io/split/android/client/utils/StringHelper.java b/src/main/java/io/split/android/client/utils/StringHelper.java
index c63e00d6c..5fbfc39f7 100644
--- a/src/main/java/io/split/android/client/utils/StringHelper.java
+++ b/src/main/java/io/split/android/client/utils/StringHelper.java
@@ -50,4 +50,5 @@ public String join(String connector, Iterable<String> values) {
 
         return string.toString();
     }
+
 }
diff --git a/src/main/java/io/split/android/client/utils/Utils.java b/src/main/java/io/split/android/client/utils/Utils.java
index 415cb0170..8341d776c 100644
--- a/src/main/java/io/split/android/client/utils/Utils.java
+++ b/src/main/java/io/split/android/client/utils/Utils.java
@@ -1,5 +1,6 @@
 package io.split.android.client.utils;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import java.util.ArrayList;
@@ -105,4 +106,9 @@ public static String repeat(String str, int count) {
         }
         return builder.toString();
     }
+
+    @NonNull
+    public static <T> T getOrDefault(@Nullable T value, @NonNull T defaultValue) {
+        return value != null ? value : defaultValue;
+    }
 }
diff --git a/src/main/java/io/split/android/engine/experiments/SplitParser.java b/src/main/java/io/split/android/engine/experiments/SplitParser.java
index 598473a6d..2763ecca7 100644
--- a/src/main/java/io/split/android/engine/experiments/SplitParser.java
+++ b/src/main/java/io/split/android/engine/experiments/SplitParser.java
@@ -49,20 +49,24 @@ public class SplitParser {
     public static final int CONDITIONS_UPPER_LIMIT = 50;
 
     private final MySegmentsStorageContainer mMySegmentsStorageContainer;
+    private final MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
     private final DefaultConditionsProvider mDefaultConditionsProvider;
 
-    public SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer) {
-        this(mySegmentsStorageContainer, new DefaultConditionsProvider());
+    public SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, @Nullable MySegmentsStorageContainer myLargeSegmentsStorageContainer) {
+        this(mySegmentsStorageContainer, myLargeSegmentsStorageContainer, new DefaultConditionsProvider());
     }
 
     @VisibleForTesting
-    static SplitParser get(MySegmentsStorageContainer mySegmentsStorageContainer) {
-        return new SplitParser(mySegmentsStorageContainer);
+    static SplitParser get(MySegmentsStorageContainer mySegmentsStorageContainer, MySegmentsStorageContainer myLargeSegmentsStorageContainer) {
+        return new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer);
     }
 
     @VisibleForTesting
-    SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer, @NonNull DefaultConditionsProvider defaultConditionsProvider) {
+    SplitParser(@NonNull MySegmentsStorageContainer mySegmentsStorageContainer,
+                @Nullable MySegmentsStorageContainer myLargeSegmentsStorageContainer,
+                @NonNull DefaultConditionsProvider defaultConditionsProvider) {
         mMySegmentsStorageContainer = checkNotNull(mySegmentsStorageContainer);
+        mMyLargeSegmentsStorageContainer = myLargeSegmentsStorageContainer;
         mDefaultConditionsProvider = checkNotNull(defaultConditionsProvider);
     }
 
@@ -154,6 +158,10 @@ private AttributeMatcher toMatcher(Matcher matcher, String matchingKey) throws U
                 checkNotNull(matcher.userDefinedSegmentMatcherData);
                 delegate = new MySegmentsMatcher((matchingKey != null) ? mMySegmentsStorageContainer.getStorageForKey(matchingKey) : new EmptyMySegmentsStorage(), matcher.userDefinedSegmentMatcherData.segmentName);
                 break;
+            case IN_LARGE_SEGMENT:
+                checkNotNull(matcher.userDefinedLargeSegmentMatcherData);
+                delegate = new MySegmentsMatcher((matchingKey != null) ? mMyLargeSegmentsStorageContainer.getStorageForKey(matchingKey) : new EmptyMySegmentsStorage(), matcher.userDefinedLargeSegmentMatcherData.largeSegmentName);
+                break;
             case WHITELIST:
                 checkNotNull(matcher.whitelistMatcherData);
                 delegate = new WhitelistMatcher(matcher.whitelistMatcherData.whitelist);
diff --git a/src/sharedTest/java/helper/TestingData.java b/src/sharedTest/java/helper/TestingData.java
index 3ba5476fd..1a8da42df 100644
--- a/src/sharedTest/java/helper/TestingData.java
+++ b/src/sharedTest/java/helper/TestingData.java
@@ -1,26 +1,26 @@
 package helper;
 
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
 import io.split.android.client.utils.Json;
 
 public class TestingData {
 
     public final static String UNBOUNDED_NOTIFICATION = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 0," +
             "\\\"c\\\": 0," +
             "\\\"d\\\": \\\"\\\"," +
-            "\\\"segmentName\\\": \\\"pepe\\\"," +
-            "\\\"changeNumber\\\": 28" +
+            "\\\"n\\\": [\\\"pepe\\\"]," +
+            "\\\"cn\\\": 28" +
             "}";
 
     public final static String SEGMENT_REMOVAL_NOTIFICATION = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 3," +
             "\\\"c\\\": 0," +
             "\\\"d\\\": \\\"\\\"," +
-            "\\\"segmentName\\\": \\\"segment1\\\"," +
-            "\\\"changeNumber\\\": 28" +
+            "\\\"n\\\": [\\\"segment1\\\"]," +
+            "\\\"cn\\\": 28" +
             "}";
 
     /**
@@ -39,28 +39,28 @@ public class TestingData {
       8492584437244343049 11382796718859679607 11383137936375052427 17699699514337596928 17001541343685934583 8355202062888946034]
      */
     public final static String BOUNDED_NOTIFICATION_GZIP = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 1," +
             "\"d\": \"H4sIAAAAAAAA/2IYBfgAx0A7YBTgB4wD7YABAAID7QC6g5EYy8MEMA20A+gMFAbaAYMZDPXqlGWgHTAKRsEoGAWjgCzQQFjJkKqiiPAPAQAIAAD//5L7VQwAEAAA\"" +
             "}";
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_GZIP = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAA/2IYBfgAx0A7YBTgB4wD7YABAAID7QC6g5EYy8MEMA20A+gMFAbaAYMZDPXqlGWgHTAKRsEoGAWjgCzQQFjJkKqiiPAPAQAIAAD//5L7VQwAEAAA\\\"" +
             "}";
 
     public final static String BOUNDED_NOTIFICATION_ZLIB = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 2," +
             "\"d\": \"eJxiGAX4AMdAO2AU4AeMA+2AAQACA+0AuoORGMvDBDANtAPoDBQG2gGDGQz16pRloB0wCkbBKBgFo4As0EBYyZCqoojwDwEACAAA//+W/QFR\"" +
             "}";
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_ZLIB = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 2," +
             "\\\"d\\\": \\\"eJxiGAX4AMdAO2AU4AeMA+2AAQACA+0AuoORGMvDBDANtAPoDBQG2gGDGQz16pRloB0wCkbBKBgFo4As0EBYyZCqoojwDwEACAAA//+W/QFR\\\"" +
@@ -68,7 +68,7 @@ public class TestingData {
 
 
     public final static String ESCAPED_BOUNDED_NOTIFICATION_MALFORMED = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
             "\\\"u\\\": 1," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAAg5EYy8MEMA20A+//5L7VQwAEAAA\\\"" +
@@ -80,49 +80,49 @@ public class TestingData {
      * = a: [key1, key2] , r: [key3, key4]
      */
     public final static String KEY_LIST_NOTIFICATION_GZIP = "{" +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 2," +
             "\"c\": 1," +
             "\"d\": \"H4sIAAAAAAAA/wTAsRHDUAgD0F2ofwEIkPAqPhdZIW0uu/v97GPXHU004ULuMGrYR6XUbIjlXULPPse+dt1yhJibBODjrTmj3GJ4emduuDDP/w0AAP//18WLsl0AAAA=\"" +
             "}";
 
     public final static String ESCAPED_KEY_LIST_NOTIFICATION_GZIP = "{" +
-            "\\\"type\\\": \\\"MY_SEGMENTS_UPDATE_V2\\\"," +
-            "\\\"segmentName\\\": \\\"new_segment_added\\\"," +
+            "\\\"type\\\": \\\"MEMBERSHIPS_MS_UPDATE\\\"," +
+            "\\\"n\\\": [\\\"new_segment_added\\\"]," +
             "\\\"u\\\": 2," +
             "\\\"c\\\": 1," +
             "\\\"d\\\": \\\"H4sIAAAAAAAA/wTAsRHDUAgD0F2ofwEIkPAqPhdZIW0uu/v97GPXHU004ULuMGrYR6XUbIjlXULPPse+dt1yhJibBODjrTmj3GJ4emduuDDP/w0AAP//18WLsl0AAAA=\\\"" +
             "}";
 
 public final static String BOUNDED_NOTIFICATION_ZLIB_2 = "{" +
-            "\"changeNumber\": 1629754722111, " +
-            "\"type\": \"MY_SEGMENTS_UPDATE_V2\"," +
+            "\"cn\": 1629754722111, " +
+            "\"type\": \"MEMBERSHIPS_MS_UPDATE\"," +
             "\"u\": 1," +
             "\"c\": 2," +
             "\"d\": \"eJzMVk3OhDAIVdNFl9/22zVzEo8yR5mjT6LGsRTKg2LiW8yPUnjQB+2kIwM2ThTIKtVU1oknFcRzufz+YGYM/phnHW8sdPvs9EzXW2I+HFzhNyTNgCD/PpW9xpGiHD0Bw1U5HLSS644FbGZgoPovmjpmX5wAzhIxJyN7IAnFQWX1htj+LUl6ZQRV3umMqYG1LCrOJGLPV8+IidBQZFt6sOUA6CqsX5iEFY2gqufs2mfqRtsVWytRnO+iYMN7xIBqJhDqAydV+HidkGOGEJYvk4fhe/8iIukphG/XfFcfVxnMVcALCOF77qL/EU7ODepxlLST6qxFLYRdOyW8EBY4BqVjObnm3V5ZMkZIKf++8+hM7zM1Kd3aFqVZeSHzDQAA//+QUQ3a\"" +
             "}";
 //    c: 2
-//    changeNumber: 1629754722111
+//    cn: 1629754722111
 //    d: "eJzMVk3OhDAIVdNFl9/22zVzEo8yR5mjT6LGsRTKg2LiW8yPUnjQB+2kIwM2ThTIKtVU1oknFcRzufz+YGYM/phnHW8sdPvs9EzXW2I+HFzhNyTNgCD/PpW9xpGiHD0Bw1U5HLSS644FbGZgoPovmjpmX5wAzhIxJyN7IAnFQWX1htj+LUl6ZQRV3umMqYG1LCrOJGLPV8+IidBQZFt6sOUA6CqsX5iEFY2gqufs2mfqRtsVWytRnO+iYMN7xIBqJhDqAydV+HidkGOGEJYvk4fhe/8iIukphG/XfFcfVxnMVcALCOF77qL/EU7ODepxlLST6qxFLYRdOyW8EBY4BqVjObnm3V5ZMkZIKf++8+hM7zM1Kd3aFqVZeSHzDQAA//+QUQ3a"
-//    segmentName: ""
-//    type: "MY_SEGMENTS_UPDATE_V2"
+//    n: ""
+//    type: "MEMBERSHIPS_MS_UPDATE"
 //    u: 1
 
     public final static String DECOMPRESSED_KEY_LIST_PAYLOAD_GZIP = "{\"a\":[1573573083296714675,8482869187405483569],\"r\":[8031872927333060586,6829471020522910836]}";
 
     public static String encodedKeyListPayloadGzip() {
-        return (Json.fromJson(KEY_LIST_NOTIFICATION_GZIP, MySegmentChangeV2Notification.class)).getData();
+        return (Json.fromJson(KEY_LIST_NOTIFICATION_GZIP, MembershipNotification.class)).getData();
     }
 
     public static String encodedBoundedPayloadZlib() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB, MySegmentChangeV2Notification.class)).getData();
+        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB, MembershipNotification.class)).getData();
     }
 
     public static String encodedBoundedPayloadZlib2() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB_2, MySegmentChangeV2Notification.class)).getData();
+        return (Json.fromJson(BOUNDED_NOTIFICATION_ZLIB_2, MembershipNotification.class)).getData();
     }
 
     public static String encodedBoundedPayloadGzip() {
-        return (Json.fromJson(BOUNDED_NOTIFICATION_GZIP, MySegmentChangeV2Notification.class)).getData();
+        return (Json.fromJson(BOUNDED_NOTIFICATION_GZIP, MembershipNotification.class)).getData();
     }
 }
diff --git a/src/test/java/io/split/android/client/MySegmentsUriBuildersTest.java b/src/test/java/io/split/android/client/MySegmentsUriBuildersTest.java
new file mode 100644
index 000000000..ab0237d42
--- /dev/null
+++ b/src/test/java/io/split/android/client/MySegmentsUriBuildersTest.java
@@ -0,0 +1,25 @@
+package io.split.android.client;
+
+import static org.mockito.Mockito.mockStatic;
+
+import org.junit.Test;
+import org.mockito.MockedStatic;
+
+import java.net.URISyntaxException;
+
+import io.split.android.client.network.SdkTargetPath;
+
+public class MySegmentsUriBuildersTest {
+
+    @Test
+    public void mySegmentsUriBuilderUsesSdkTargetPath() throws URISyntaxException {
+        try (MockedStatic<SdkTargetPath> mockedStatic = mockStatic(SdkTargetPath.class)) {
+
+            SplitFactoryHelper.MySegmentsUriBuilder builder = new SplitFactoryHelper.MySegmentsUriBuilder(
+                    "https://sdk.split.io/api/");
+            builder.build("some_key");
+
+            mockedStatic.verify(() -> SdkTargetPath.mySegments("https://sdk.split.io/api/", "some_key"));
+        }
+    }
+}
diff --git a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java
index f5edff592..7b8076a18 100644
--- a/src/test/java/io/split/android/client/SplitClientImplBaseTest.java
+++ b/src/test/java/io/split/android/client/SplitClientImplBaseTest.java
@@ -30,6 +30,8 @@ public abstract class SplitClientImplBaseTest {
     @Mock
     protected MySegmentsStorageContainer mySegmentsStorageContainer;
     @Mock
+    protected MySegmentsStorageContainer myLargeSegmentsStorageContainer;
+    @Mock
     protected MySegmentsStorage mySegmentsStorage;
     @Mock
     protected ImpressionListener impressionListener;
@@ -56,7 +58,7 @@ public void setUp() {
                 container,
                 clientContainer,
                 new Key("test_key"),
-                new SplitParser(mySegmentsStorageContainer),
+                new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer),
                 impressionListener,
                 splitClientConfig,
                 new SplitEventsManager(splitClientConfig, new SplitTaskExecutorStub()),
diff --git a/src/test/java/io/split/android/client/SplitManagerImplTest.java b/src/test/java/io/split/android/client/SplitManagerImplTest.java
index c2b903e58..07adcfc84 100644
--- a/src/test/java/io/split/android/client/SplitManagerImplTest.java
+++ b/src/test/java/io/split/android/client/SplitManagerImplTest.java
@@ -45,6 +45,8 @@ public class SplitManagerImplTest {
     @Mock
     MySegmentsStorageContainer mMySegmentsStorageContainer;
     @Mock
+    MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
+    @Mock
     SplitManager mSplitManager;
 
     @Before
@@ -52,7 +54,7 @@ public void setup() {
         MockitoAnnotations.openMocks(this);
         SplitValidator validator = new SplitValidatorImpl();
         when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage);
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer);
         mSplitManager = new SplitManagerImpl(mSplitsStorage, validator, parser);
     }
 
diff --git a/src/test/java/io/split/android/client/TreatmentManagerTest.java b/src/test/java/io/split/android/client/TreatmentManagerTest.java
index 371e92a36..a13f3e287 100644
--- a/src/test/java/io/split/android/client/TreatmentManagerTest.java
+++ b/src/test/java/io/split/android/client/TreatmentManagerTest.java
@@ -70,12 +70,13 @@ public void loadSplitsFromFile() {
         if (evaluator == null) {
             FileHelper fileHelper = new FileHelper();
             MySegmentsStorageContainer mySegmentsStorageContainer = mock(MySegmentsStorageContainer.class);
+            MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class);
             MySegmentsStorage mySegmentsStorage = mock(MySegmentsStorage.class);
             SplitsStorage splitsStorage = mock(SplitsStorage.class);
 
             Set<String> mySegments = new HashSet(Arrays.asList("s1", "s2", "test_copy"));
             List<Split> splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json");
-            SplitParser splitParser = new SplitParser(mySegmentsStorageContainer);
+            SplitParser splitParser = new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer);
 
             Map<String, Split> splitsMap = splitsMap(splits);
             when(splitsStorage.getAll()).thenReturn(splitsMap);
diff --git a/src/test/java/io/split/android/client/events/EventsManagerTest.java b/src/test/java/io/split/android/client/events/EventsManagerTest.java
index 8a530296a..c9cca29c6 100644
--- a/src/test/java/io/split/android/client/events/EventsManagerTest.java
+++ b/src/test/java/io/split/android/client/events/EventsManagerTest.java
@@ -1,11 +1,13 @@
 package io.split.android.client.events;
 
-import org.junit.Assert;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
 
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -13,17 +15,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import helper.TestingHelper;
 import io.split.android.client.SplitClient;
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.events.executors.SplitEventExecutorResources;
 import io.split.android.fake.SplitTaskExecutorStub;
 
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
-
 public class EventsManagerTest {
 
     @Mock
@@ -46,33 +42,16 @@ public void eventOnReady() {
 
         eventManager.notifyInternalEvent(SplitInternalEvent.SPLITS_UPDATED);
         eventManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
-
+        eventManager.notifyInternalEvent(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED);
 
         boolean shouldStop = false;
         long maxExecutionTime = System.currentTimeMillis() + 10000;
-        long intervalExecutionTime = 1000;
-
-        while(!shouldStop) {
-            try {
-                Thread.currentThread().sleep(intervalExecutionTime);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-                Assert.fail();
-            }
-
-            maxExecutionTime -= intervalExecutionTime;
+        long intervalExecutionTime = 200;
 
-            if (System.currentTimeMillis() > maxExecutionTime) {
-                shouldStop = true;
-            }
-
-            if (eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY)) {
-                shouldStop = true;
-            }
-        }
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY);
 
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY), is(equalTo(true)));
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT), is(equalTo(false)));
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY));
+        assertFalse(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT));
     }
 
     @Test
@@ -82,29 +61,12 @@ public void eventOnReadyTimedOut() {
 
         boolean shouldStop = false;
         long maxExecutionTime = System.currentTimeMillis() + 10000;
-        long intervalExecutionTime = 1000;
+        long intervalExecutionTime = 200;
 
-        while(!shouldStop) {
-            try {
-                Thread.currentThread().sleep(intervalExecutionTime);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-                Assert.fail();
-            }
-
-            maxExecutionTime -= intervalExecutionTime;
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY_TIMED_OUT);
 
-            if (System.currentTimeMillis() > maxExecutionTime) {
-                shouldStop = true;
-            }
-
-            if (eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT)) {
-                shouldStop = true;
-            }
-        }
-
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY), is(equalTo(false)));
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT), is(equalTo(true)));
+        assertFalse(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY));
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT));
     }
 
     @Test
@@ -114,59 +76,26 @@ public void eventOnReadyAndOnReadyTimedOut() {
 
         boolean shouldStop = false;
         long maxExecutionTime = System.currentTimeMillis() + 10000;
-        long intervalExecutionTime = 1000;
+        long intervalExecutionTime = 200;
 
-        while(!shouldStop) {
-            try {
-                Thread.currentThread().sleep(intervalExecutionTime);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-                Assert.fail();
-            }
-
-            maxExecutionTime -= intervalExecutionTime;
-
-            if (System.currentTimeMillis() > maxExecutionTime) {
-                shouldStop = true;
-            }
-
-            if (eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT)) {
-                shouldStop = true;
-            }
-        }
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY_TIMED_OUT);
 
         //At this line timeout has been reached
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT), is(equalTo(true)));
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT));
 
         //But if after timeout event, the Splits and MySegments are ready, SDK_READY should be triggered
         eventManager.notifyInternalEvent(SplitInternalEvent.SPLITS_UPDATED);
         eventManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
+        eventManager.notifyInternalEvent(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED);
 
         shouldStop = false;
         maxExecutionTime = System.currentTimeMillis() + 10000;
-        intervalExecutionTime = 1000;
-
-        while(!shouldStop) {
-            try {
-                Thread.currentThread().sleep(intervalExecutionTime);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-                Assert.fail();
-            }
-
-            maxExecutionTime -= intervalExecutionTime;
-
-            if (System.currentTimeMillis() > maxExecutionTime) {
-                shouldStop = true;
-            }
+        intervalExecutionTime = 200;
 
-            if (eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY)) {
-                shouldStop = true;
-            }
-        }
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY);
 
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY), is(equalTo(true)));
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT), is(equalTo(true)));
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY));
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT));
     }
 
     @Test
@@ -176,7 +105,7 @@ public void eventOnReadyFromCacheSplitsFirst() {
         eventList.add(SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.ATTRIBUTES_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE);
-        eventOnReadyFromCache(eventList);
+        eventOnReadyFromCache(eventList, SplitClientConfig.builder().build());
     }
 
     @Test
@@ -186,7 +115,7 @@ public void eventOnReadyFromCacheMySegmentsFirst() {
         eventList.add(SplitInternalEvent.SPLITS_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.ATTRIBUTES_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE);
-        eventOnReadyFromCache(eventList);
+        eventOnReadyFromCache(eventList, SplitClientConfig.builder().build());
     }
 
     @Test
@@ -196,7 +125,7 @@ public void eventOnReadyFromCacheAttributesFirst() {
         eventList.add(SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.SPLITS_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE);
-        eventOnReadyFromCache(eventList);
+        eventOnReadyFromCache(eventList, SplitClientConfig.builder().build());
     }
 
     @Test
@@ -206,25 +135,114 @@ public void eventEncryptionMigrationDoneFirst() {
         eventList.add(SplitInternalEvent.ATTRIBUTES_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
         eventList.add(SplitInternalEvent.SPLITS_LOADED_FROM_STORAGE);
-        eventOnReadyFromCache(eventList);
+        eventOnReadyFromCache(eventList, SplitClientConfig.builder().build());
+    }
+
+    @Test
+    public void eventOnReadyFromCacheMyLargeSegmentsFirst() {
+        List<SplitInternalEvent> eventList = new ArrayList<>();
+        eventList.add(SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
+        eventList.add(SplitInternalEvent.SPLITS_LOADED_FROM_STORAGE);
+        eventList.add(SplitInternalEvent.ATTRIBUTES_LOADED_FROM_STORAGE);
+        eventList.add(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE);
+        eventOnReadyFromCache(eventList, SplitClientConfig.builder()
+                .build());
+    }
+
+    @Test
+    public void sdkUpdateWithFeatureFlags() throws InterruptedException {
+        sdkUpdateTest(SplitInternalEvent.SPLITS_UPDATED, false);
     }
 
-    public void eventOnReadyFromCache(List<SplitInternalEvent> eventList) {
+    @Test
+    public void sdkUpdateWithMySegments() throws InterruptedException {
+        sdkUpdateTest(SplitInternalEvent.MY_SEGMENTS_UPDATED, false);
+    }
+
+    @Test
+    public void sdkUpdateWithLargeSegmentsAndConfigEnabledEmitsEvent() throws InterruptedException {
+        sdkUpdateTest(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED, false);
+    }
+
+    @Test
+    public void sdkUpdateWithLargeSegmentsAndConfigEnabledAndWaitForLargeSegmentsFalseEmitsEvent() throws InterruptedException {
+        sdkUpdateTest(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED, false);
+    }
+
+    @Test
+    public void sdkReadyWithSplitsAndUpdatedLargeSegments() {
 
         SplitClientConfig cfg = SplitClientConfig.builder().build();
         SplitEventsManager eventManager = new SplitEventsManager(cfg, new SplitTaskExecutorStub());
 
-        for(SplitInternalEvent event : eventList) {
+        eventManager.notifyInternalEvent(SplitInternalEvent.SPLITS_UPDATED);
+        eventManager.notifyInternalEvent(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED);
+
+        boolean shouldStop = false;
+        long maxExecutionTime = System.currentTimeMillis() + 10000;
+        long intervalExecutionTime = 200;
+
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY);
+
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY));
+        assertFalse(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_TIMED_OUT));
+    }
+
+    private static void sdkUpdateTest(SplitInternalEvent eventToCheck, boolean negate) throws InterruptedException {
+        SplitEventsManager eventManager = new SplitEventsManager(SplitClientConfig.builder()
+                .build(), new SplitTaskExecutorStub());
+
+        CountDownLatch updateLatch = new CountDownLatch(1);
+        CountDownLatch readyLatch = new CountDownLatch(1);
+        eventManager.register(SplitEvent.SDK_UPDATE, new SplitEventTask() {
+            @Override
+            public void onPostExecutionView(SplitClient client) {
+                updateLatch.countDown();
+            }
+        });
+        eventManager.register(SplitEvent.SDK_READY, new SplitEventTask() {
+            @Override
+            public void onPostExecutionView(SplitClient client) {
+                readyLatch.countDown();
+            }
+        });
+
+        eventManager.notifyInternalEvent(SplitInternalEvent.SPLITS_FETCHED);
+        eventManager.notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_FETCHED);
+        boolean readyAwait = readyLatch.await(3, TimeUnit.SECONDS);
+
+        eventManager.notifyInternalEvent(eventToCheck);
+        boolean updateAwait = updateLatch.await(3, TimeUnit.SECONDS);
+
+        assertTrue(readyAwait);
+        if (!negate) {
+            assertTrue(updateAwait);
+        } else {
+            assertFalse(updateAwait);
+        }
+    }
+
+    private void eventOnReadyFromCache(List<SplitInternalEvent> eventList, SplitClientConfig config) {
+
+        SplitEventsManager eventManager = new SplitEventsManager(config, new SplitTaskExecutorStub());
+
+        for (SplitInternalEvent event : eventList) {
             eventManager.notifyInternalEvent(event);
         }
 
         boolean shouldStop = false;
         long maxExecutionTime = System.currentTimeMillis() + 10000;
-        long intervalExecutionTime = 1000;
+        long intervalExecutionTime = 200;
+
+        execute(shouldStop, intervalExecutionTime, maxExecutionTime, eventManager, SplitEvent.SDK_READY_FROM_CACHE);
 
-        while(!shouldStop) {
+        assertTrue(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_FROM_CACHE));
+    }
+
+    private static void execute(boolean shouldStop, long intervalExecutionTime, long maxExecutionTime, SplitEventsManager eventManager, SplitEvent event) {
+        while (!shouldStop) {
             try {
-                Thread.currentThread().sleep(intervalExecutionTime);
+                Thread.sleep(intervalExecutionTime);
             } catch (InterruptedException e) {
                 e.printStackTrace();
                 Assert.fail();
@@ -236,11 +254,9 @@ public void eventOnReadyFromCache(List<SplitInternalEvent> eventList) {
                 shouldStop = true;
             }
 
-            if (eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_FROM_CACHE)) {
+            if (eventManager.eventAlreadyTriggered(event)) {
                 shouldStop = true;
             }
         }
-
-        assertThat(eventManager.eventAlreadyTriggered(SplitEvent.SDK_READY_FROM_CACHE), is(equalTo(true)));
     }
 }
diff --git a/src/test/java/io/split/android/client/network/HttpClientTest.java b/src/test/java/io/split/android/client/network/HttpClientTest.java
index c655b92a3..b036ec5b8 100644
--- a/src/test/java/io/split/android/client/network/HttpClientTest.java
+++ b/src/test/java/io/split/android/client/network/HttpClientTest.java
@@ -30,12 +30,11 @@
 import java.net.URL;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import io.split.android.client.dtos.AllSegmentsChange;
 import io.split.android.client.dtos.Event;
-import io.split.android.client.dtos.MySegment;
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.dtos.TestImpressions;
 import io.split.android.client.utils.Json;
@@ -143,13 +142,11 @@ public void severalRequest() throws Exception {
         Assert.assertEquals("this is split test", dummyResp.getData());
 
         // Assert my segments
-        List<MySegment> mySegments = parseMySegments(mySegmentsResp.getData());
+        List<String> mySegments = parseMySegments(mySegmentsResp.getData()).getSegmentsChange().getNames();
         Assert.assertEquals(200, mySegmentsResp.getHttpStatus());
         Assert.assertEquals(2, mySegments.size());
-        Assert.assertEquals("id1", mySegments.get(0).id);
-        Assert.assertEquals("groupa", mySegments.get(0).name);
-        Assert.assertEquals("id2", mySegments.get(1).id);
-        Assert.assertEquals("groupb", mySegments.get(1).name);
+        Assert.assertTrue(mySegments.contains("groupa"));
+        Assert.assertTrue(mySegments.contains("groupb"));
 
         // Assert split changes
         SplitChange splitChange = Json.fromJson(splitChangeResp.getData(), SplitChange.class);
@@ -157,7 +154,7 @@ public void severalRequest() throws Exception {
         assertTrue(splitChangeResp.isSuccess());
         Assert.assertEquals(-1, splitChange.since);
         Assert.assertEquals(1506703262916L, splitChange.till);
-        Assert.assertEquals(30, splitChange.splits.size());
+        Assert.assertEquals(31, splitChange.splits.size());
 
         // Assert empty response
         Assert.assertEquals(200, emptyResp.getHttpStatus());
@@ -426,7 +423,7 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
 
                     case "/test2/":
 
-                        return new MockResponse().setResponseCode(200).setBody("{\"mySegments\":[{\"id\":\"id1\", \"name\":\"groupa\"}, {\"id\":\"id2\", \"name\":\"groupb\"}]}");
+                        return new MockResponse().setResponseCode(200).setBody("{\"ms\":{\"k\":[{\"n\":\"groupa\"},{\"n\":\"groupb\"}],\"cn\":999999},\"ls\":{\"k\":[],\"cn\":999999}}");
                     case "/test3/":
                         return new MockResponse().setResponseCode(200).setBody(splitChangesResponse);
 
@@ -462,12 +459,8 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio
                 .build();
     }
 
-    private List<MySegment> parseMySegments(String json) {
-        Type mapType = new TypeToken<Map<String, List<MySegment>>>() {
-        }.getType();
-
-        Map<String, List<MySegment>> mySegmentsMap = Json.fromJson(json, mapType);
-        return mySegmentsMap.get("mySegments");
+    private AllSegmentsChange parseMySegments(String json) {
+        return Json.fromJson(json, AllSegmentsChange.class);
     }
 
     private List<Event> parseTrackEvents(String json) {
diff --git a/src/test/java/io/split/android/client/network/SdkTargetPathTest.java b/src/test/java/io/split/android/client/network/SdkTargetPathTest.java
index 8c44403a3..8f8c34657 100644
--- a/src/test/java/io/split/android/client/network/SdkTargetPathTest.java
+++ b/src/test/java/io/split/android/client/network/SdkTargetPathTest.java
@@ -13,21 +13,21 @@ public class SdkTargetPathTest {
     public void userKeyWithSpaces() throws URISyntaxException {
         URI uri = SdkTargetPath.mySegments("https://split.io", "CABM, CCIB Marketing");
 
-        assertEquals("/mySegments/CABM,%20CCIB%20Marketing", uri.getRawPath());
+        assertEquals(SdkTargetPath.MEMBERSHIPS+"/CABM,%20CCIB%20Marketing", uri.getRawPath());
     }
 
     @Test
     public void userKeyWithSlash() throws URISyntaxException {
         URI uri = SdkTargetPath.mySegments("https://split.io", "user/key");
 
-        assertEquals("/mySegments/user%2Fkey", uri.getRawPath());
+        assertEquals(SdkTargetPath.MEMBERSHIPS+"/user%2Fkey", uri.getRawPath());
     }
 
     @Test
     public void userKeyWithSpecialChars() throws URISyntaxException {
         URI uri = SdkTargetPath.mySegments("https://split.io", "grüneStraße");
 
-        assertEquals("/mySegments/gr%C3%BCneStra%C3%9Fe", uri.getRawPath());
+        assertEquals(SdkTargetPath.MEMBERSHIPS+"/gr%C3%BCneStra%C3%9Fe", uri.getRawPath());
     }
 
     @Test
diff --git a/src/test/java/io/split/android/client/service/HttpFetcherTest.java b/src/test/java/io/split/android/client/service/HttpFetcherTest.java
index 4b1259a3b..36c85ffe0 100644
--- a/src/test/java/io/split/android/client/service/HttpFetcherTest.java
+++ b/src/test/java/io/split/android/client/service/HttpFetcherTest.java
@@ -18,13 +18,14 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.network.HttpClient;
 import io.split.android.client.network.HttpException;
@@ -38,21 +39,21 @@
 import io.split.android.client.service.http.HttpFetcherException;
 import io.split.android.client.service.http.HttpFetcherImpl;
 import io.split.android.client.service.http.HttpResponseParser;
-import io.split.android.client.service.mysegments.MySegmentsResponseParser;
+import io.split.android.client.service.mysegments.AllSegmentsResponseParser;
 import io.split.android.client.service.splits.SplitChangeResponseParser;
 
 public class HttpFetcherTest {
 
     private final static String TEST_URL = "http://testurl.com";
     private final static String SPLIT_CHANGES_TEST_URL = TEST_URL + SdkTargetPath.SPLIT_CHANGES;
-    private final static String MY_SEGMENTS_TEST_URL = TEST_URL + SdkTargetPath.MY_SEGMENTS;
+    private final static String MY_SEGMENTS_TEST_URL = TEST_URL + SdkTargetPath.MEMBERSHIPS;
 
-    HttpClient mClientMock;
-    URI mUrl;
-    URI mSplitChangesUrl;
-    URI mMySegmentsUrl;
-    HttpResponseParser<SplitChange> mSplitChangeResponseParser = new SplitChangeResponseParser();
-    HttpResponseParser<List<MySegment>> mMySegmentsResponseParser = new MySegmentsResponseParser();
+    private HttpClient mClientMock;
+    private URI mUrl;
+    private URI mSplitChangesUrl;
+    private URI mMySegmentsUrl;
+    private final HttpResponseParser<SplitChange> mSplitChangeResponseParser = new SplitChangeResponseParser();
+    private final HttpResponseParser<AllSegmentsChange> mMySegmentsResponseParser = new AllSegmentsResponseParser();
 
     @Before
     public void setup() throws URISyntaxException {
@@ -193,15 +194,16 @@ public void testSuccessfulMySegmentsFetch() throws URISyntaxException, HttpExcep
         when(request.execute()).thenReturn(response);
         when(mClientMock.request(mMySegmentsUrl, HttpMethod.GET, null, null)).thenReturn(request);
 
-        HttpFetcher<List<MySegment>> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
-        List<MySegment> mySegments = null;
+        HttpFetcher<AllSegmentsChange> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
+        List<String> mySegments = null;
         try {
-            mySegments = fetcher.execute(new HashMap<>(), null);
+            SegmentsChange segmentsChange = fetcher.execute(new HashMap<>(), null).getSegmentsChange();
+            mySegments = segmentsChange.getNames();
         } catch (HttpFetcherException e) {
             exceptionWasThrown = true;
         }
 
-        Set<String> mySegmentsSet = mySegments.stream().map(mySegment -> mySegment.name).collect(Collectors.toSet());
+        Set<String> mySegmentsSet = new HashSet<>(mySegments);
 
         Assert.assertFalse(exceptionWasThrown);
         Assert.assertEquals(2, mySegments.size());
@@ -222,10 +224,10 @@ public void tesNonEmptyHeadersMySegmentsFetch() throws URISyntaxException, HttpE
         when(request.execute()).thenReturn(response);
         when(mClientMock.request(mMySegmentsUrl, HttpMethod.GET, null, headers)).thenReturn(request);
 
-        HttpFetcher<List<MySegment>> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
+        HttpFetcher<AllSegmentsChange> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
 
         try {
-            List<MySegment> mySegments = fetcher.execute(new HashMap<>(), headers);
+            List<String> mySegments = fetcher.execute(new HashMap<>(), headers).getSegmentsChange().getNames();
         } catch (HttpFetcherException e) {
             exceptionWasThrown = true;
         }
@@ -246,10 +248,10 @@ public void testHandleParserExceptionFetch() throws URISyntaxException, HttpExce
         when(mClientMock.request(mMySegmentsUrl, HttpMethod.GET)).thenReturn(request);
 
 
-        HttpFetcher<List<MySegment>> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
-        List<MySegment> mySegments = null;
+        HttpFetcher<AllSegmentsChange> fetcher = new HttpFetcherImpl<>(mClientMock, mMySegmentsUrl, mMySegmentsResponseParser);
+        List<String> mySegments = null;
         try {
-            mySegments = fetcher.execute(new HashMap<>(), null);
+            mySegments = fetcher.execute(new HashMap<>(), null).getSegmentsChange().getNames();
         } catch (HttpFetcherException e) {
             exceptionWasThrown = true;
         }
@@ -281,7 +283,7 @@ public void paramOrderWithoutTillIsCorrect() throws HttpException, HttpFetcherEx
         params.put("since", "-1");
         params.put("sets", "flag_set1,flagset2");
 
-            fetcher.execute(params, null);
+        fetcher.execute(params, null);
 
         verifyQuery("s=1.1&since=-1&sets=flag_set1,flagset2");
     }
@@ -365,6 +367,6 @@ private String dummySplitChangeResponse() {
     }
 
     private String dummyMySegmentsResponse() {
-        return "{\"mySegments\":[{ \"id\":\"id1\", \"name\":\"segment1\"}, { \"id\":\"id2\", \"name\":\"segment2\"}]}";
+        return "{\"ms\":{\"k\":[{\"n\":\"segment1\"},{\"n\":\"segment2\"}],\"cn\":99999},\"ls\":{\"k\":[{\"n\":\"large-segment1\"},{\"n\":\"large-segment2\"},{\"n\":\"large-segment3\"}],\"cn\":9999999999999}}";
     }
 }
diff --git a/src/test/java/io/split/android/client/service/MySegmentsOverwriteTaskTest.java b/src/test/java/io/split/android/client/service/MySegmentsOverwriteTaskTest.java
deleted file mode 100644
index 4e799857e..000000000
--- a/src/test/java/io/split/android/client/service/MySegmentsOverwriteTaskTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package io.split.android.client.service;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import io.split.android.client.events.SplitEventsManager;
-import io.split.android.client.service.executor.SplitTaskExecutionInfo;
-import io.split.android.client.service.executor.SplitTaskExecutionStatus;
-import io.split.android.client.service.executor.SplitTaskType;
-import io.split.android.client.service.http.HttpFetcherException;
-import io.split.android.client.service.mysegments.MySegmentsOverwriteTask;
-import io.split.android.client.storage.mysegments.MySegmentsStorage;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class MySegmentsOverwriteTaskTest {
-
-    @Mock
-    MySegmentsStorage mySegmentsStorage;
-
-    @Mock
-    SplitEventsManager mEventsManager;
-
-    MySegmentsOverwriteTask mTask;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void correctExecution() throws HttpFetcherException {
-        List<String> segments = dummySegments();
-        mTask = new MySegmentsOverwriteTask(mySegmentsStorage, segments, mEventsManager);
-        SplitTaskExecutionInfo result = mTask.execute();
-
-        verify(mySegmentsStorage, times(1)).set(any());
-
-        Assert.assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus());
-        Assert.assertEquals(SplitTaskType.MY_SEGMENTS_OVERWRITE, result.getTaskType());
-    }
-
-    @Test
-    public void storageException() {
-        List<String> segments = dummySegments();
-        mTask = new MySegmentsOverwriteTask(mySegmentsStorage, segments, mEventsManager);
-        doThrow(NullPointerException.class).when(mySegmentsStorage).set(segments);
-
-        SplitTaskExecutionInfo result = mTask.execute();
-
-        Assert.assertEquals(SplitTaskExecutionStatus.ERROR, result.getStatus());
-    }
-
-    @Test
-    public void nullParameter() {
-
-        mTask = new MySegmentsOverwriteTask(mySegmentsStorage, null, mEventsManager);
-
-        SplitTaskExecutionInfo result = mTask.execute();
-
-        Assert.assertEquals(SplitTaskExecutionStatus.ERROR, result.getStatus());
-    }
-
-    @After
-    public void tearDown() {
-        reset(mySegmentsStorage);
-    }
-
-    private List<String> dummySegments() {
-        List<String> segments = new ArrayList<>();
-        for (int i = 0; i < 3; i++) {
-            segments.add("segment" + i);
-        }
-        return segments;
-    }
-}
diff --git a/src/test/java/io/split/android/client/service/MySegmentsSyncTaskTest.java b/src/test/java/io/split/android/client/service/MySegmentsSyncTaskTest.java
index cce259185..696c844f0 100644
--- a/src/test/java/io/split/android/client/service/MySegmentsSyncTaskTest.java
+++ b/src/test/java/io/split/android/client/service/MySegmentsSyncTaskTest.java
@@ -5,11 +5,14 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.longThat;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import androidx.annotation.NonNull;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -17,21 +20,27 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
-import io.split.android.client.dtos.MySegment;
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.network.SplitHttpHeadersBuilder;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
 import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.service.http.HttpFetcherException;
 import io.split.android.client.service.mysegments.MySegmentsSyncTask;
+import io.split.android.client.service.mysegments.MySegmentsSyncTaskConfig;
+import io.split.android.client.service.sseclient.BackoffCounter;
+import io.split.android.client.service.synchronizer.MySegmentsChangeChecker;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 import io.split.android.client.telemetry.model.OperationType;
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
@@ -41,15 +50,19 @@ public class MySegmentsSyncTaskTest {
     Map<String, Object> noParams = Collections.emptyMap();
 
     @Mock
-    HttpFetcher<List<MySegment>> mMySegmentsFetcher;
+    HttpFetcher<AllSegmentsChange> mMySegmentsFetcher;
     @Mock
     MySegmentsStorage mySegmentsStorage;
     @Mock
+    MySegmentsStorage myLargeSegmentsStorage;
+    @Mock
     SplitEventsManager mEventsManager;
     @Mock
     TelemetryRuntimeProducer mTelemetryRuntimeProducer;
+    @Mock
+    MySegmentsChangeChecker mMySegmentsChangeChecker;
 
-    List<MySegment> mMySegments = null;
+    AllSegmentsChange mMySegments = null;
 
     MySegmentsSyncTask mTask;
     private AutoCloseable mAutoCloseable;
@@ -57,7 +70,8 @@ public class MySegmentsSyncTaskTest {
     @Before
     public void setup() {
         mAutoCloseable = MockitoAnnotations.openMocks(this);
-        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, false, mEventsManager, mTelemetryRuntimeProducer);
+        when(mMySegmentsChangeChecker.mySegmentsHaveChanged(any(), any())).thenReturn(true);
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null);
         loadMySegments();
     }
 
@@ -72,7 +86,7 @@ public void tearDown() {
 
     @Test
     public void correctExecution() throws HttpFetcherException {
-        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
 
         mTask.execute();
 
@@ -88,8 +102,8 @@ public void correctExecution() throws HttpFetcherException {
     public void correctExecutionNoCache() throws HttpFetcherException {
         Map<String, String> headers = new HashMap<>();
         headers.put(SplitHttpHeadersBuilder.CACHE_CONTROL_HEADER, SplitHttpHeadersBuilder.CACHE_CONTROL_NO_CACHE);
-        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, true, null, mTelemetryRuntimeProducer);
-        when(mMySegmentsFetcher.execute(noParams, headers)).thenReturn(mMySegments);
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, true, null, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null);
+        when(mMySegmentsFetcher.execute(noParams, headers)).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
 
         mTask.execute();
 
@@ -111,7 +125,7 @@ public void fetcherExceptionRetryOff() throws HttpFetcherException {
 
     @Test
     public void fetcherOtherExceptionRetryOn() throws HttpFetcherException {
-        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, false, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null);
         when(mMySegmentsFetcher.execute(noParams, null)).thenThrow(IllegalStateException.class);
 
         mTask.execute();
@@ -122,7 +136,7 @@ public void fetcherOtherExceptionRetryOn() throws HttpFetcherException {
 
     @Test
     public void storageException() throws HttpFetcherException {
-        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
         doThrow(NullPointerException.class).when(mySegmentsStorage).set(any());
 
         mTask.execute();
@@ -142,7 +156,7 @@ public void errorIsTrackedInTelemetry() throws HttpFetcherException {
 
     @Test
     public void latencyIsTrackedInTelemetry() throws HttpFetcherException {
-        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
 
         mTask.execute();
 
@@ -151,7 +165,7 @@ public void latencyIsTrackedInTelemetry() throws HttpFetcherException {
 
     @Test
     public void successIsTrackedInTelemetry() throws HttpFetcherException {
-        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
 
         mTask.execute();
 
@@ -180,7 +194,7 @@ public void nullStatusCodeReturnsNullDoNotRetry() throws HttpFetcherException {
 
     @Test
     public void successfulCallToExecuteReturnsNullDoNotRetry() throws HttpFetcherException {
-        when(mMySegmentsFetcher.execute(any(), any())).thenReturn(mMySegments);
+        when(mMySegmentsFetcher.execute(any(), any())).thenAnswer((Answer<AllSegmentsChange>) invocation -> mMySegments);
 
         SplitTaskExecutionInfo result = mTask.execute();
 
@@ -188,15 +202,198 @@ public void successfulCallToExecuteReturnsNullDoNotRetry() throws HttpFetcherExc
         Assert.assertEquals(result.getStatus(), SplitTaskExecutionStatus.SUCCESS);
     }
 
+    @Test
+    public void addTillParameterToRequestWhenResponseCnDoesNotMatchTargetAndRetryLimitIsReached() throws HttpFetcherException {
+        long targetLargeSegmentsChangeNumber = 4L;
+        BackoffCounter backoffCounter = mock(BackoffCounter.class);
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, targetLargeSegmentsChangeNumber,
+                backoffCounter, 2);
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(1L));
+        when(mMySegmentsFetcher.execute(Collections.singletonMap("till", 4L), null))
+                .thenReturn(createChange(4L));
+
+        SplitTaskExecutionInfo result = mTask.execute();
+
+        verify(backoffCounter).resetCounter();
+        verify(mMySegmentsFetcher, times(2)).execute(noParams, null);
+        verify(mMySegmentsFetcher, times(1)).execute(Collections.singletonMap("till", 4L), null);
+        verify(backoffCounter, times(2)).getNextRetryTime();
+    }
+
+    @Test
+    public void fetchedEventIsEmittedWhenNoChangesInSegments() throws HttpFetcherException {
+        when(mMySegmentsChangeChecker.mySegmentsHaveChanged(any(), any())).thenReturn(false);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mEventsManager).notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_FETCHED);
+    }
+
+    @Test
+    public void updatedEventIsEmittedWhenChangesInSegments() throws HttpFetcherException {
+        when(mMySegmentsChangeChecker.mySegmentsHaveChanged(any(), any())).thenReturn(true);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(mMySegments);
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mEventsManager).notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
+    }
+
+    @Test
+    public void largeSegmentsUpdatedEventIsEmittedWhenChangesInLargeSegmentsAndNotInSegments() throws HttpFetcherException {
+        when(mMySegmentsChangeChecker.mySegmentsHaveChanged(any(), any())).thenReturn(false);
+        when(mMySegmentsChangeChecker.mySegmentsHaveChanged(Collections.emptyList(), Collections.singletonList("largesegment0"))).thenReturn(true);
+        when(mMySegmentsFetcher.execute(noParams, null)).thenReturn(createChange(1L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mEventsManager).notifyInternalEvent(SplitInternalEvent.MY_LARGE_SEGMENTS_UPDATED);
+    }
+
+    @Test
+    public void largeSegmentsTargetIsUsedForCdnBypassWhenSegmentsChangeNumberIsNotSet() throws HttpFetcherException {
+        long targetLargeSegmentsChangeNumber = 4L;
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(null, 1L));
+        when(mMySegmentsFetcher.execute(Collections.singletonMap("till", 4L), null))
+                .thenReturn(createChange(null, 4L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, targetLargeSegmentsChangeNumber, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, times(2)).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+        verify(mMySegmentsFetcher).execute(Collections.singletonMap("till", 4L), null);
+    }
+
+    @Test
+    public void segmentsTargetIsUsedForCdnBypassWhenLargeSegmentsChangeNumberIsNotSet() throws HttpFetcherException {
+        long targetSegmentsChangeNumber = 5L;
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(2L, null));
+        when(mMySegmentsFetcher.execute(Collections.singletonMap("till", 5L), null))
+                .thenReturn(createChange(5L, null));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), targetSegmentsChangeNumber, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, times(2)).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+        verify(mMySegmentsFetcher).execute(Collections.singletonMap("till", 5L), null);
+    }
+
+    @Test
+    public void largeSegmentsTargetIsUsedForCdnBypassWhenSegmentsChangeNumberIsSmaller() throws HttpFetcherException {
+        long targetLargeSegmentsChangeNumber = 4L;
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(2L, 1L));
+        when(mMySegmentsFetcher.execute(Collections.singletonMap("till", 4L), null))
+                .thenReturn(createChange(2L, 4L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, targetLargeSegmentsChangeNumber, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, times(2)).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+        verify(mMySegmentsFetcher).execute(Collections.singletonMap("till", 4L), null);
+    }
+
+    @Test
+    public void segmentsTargetIsUsedForCdnBypassWhenLargeSegmentsChangeNumberIsSmaller() throws HttpFetcherException {
+        long targetSegmentsChangeNumber = 5L;
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(2L, 1L));
+        when(mMySegmentsFetcher.execute(Collections.singletonMap("till", 5L), null))
+                .thenReturn(createChange(5L, 1L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), targetSegmentsChangeNumber, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, times(2)).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+        verify(mMySegmentsFetcher).execute(Collections.singletonMap("till", 5L), null);
+    }
+
+    @Test
+    public void noFetchWhenSegmentsChangeNumberInStorageIsNewerThanTarget() throws HttpFetcherException {
+        long targetSegmentsChangeNumber = 5L;
+        when(mySegmentsStorage.getChangeNumber()).thenReturn(6L);
+        when(myLargeSegmentsStorage.getChangeNumber()).thenReturn(5L);
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), targetSegmentsChangeNumber, null, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, never()).execute(any(), any());
+    }
+
+    @Test
+    public void noFetchWhenLargeSegmentsChangeNumberIsNewerThanTarget() throws HttpFetcherException {
+        long targetLargeSegmentsChangeNumber = 4L;
+        when(mySegmentsStorage.getChangeNumber()).thenReturn(3L);
+        when(myLargeSegmentsStorage.getChangeNumber()).thenReturn(5L);
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), null, targetLargeSegmentsChangeNumber, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher, never()).execute(any(), any());
+    }
+
+    @Test
+    public void fetchWhenSegmentsChangeNumberInStorageIsNewerThanTargetAndLargeSegmentsChangeNumberIsOlder() throws HttpFetcherException {
+        long targetSegmentsChangeNumber = 5L;
+        long targetLargeSegmentsChangeNumber = 4L;
+        when(mySegmentsStorage.getChangeNumber()).thenReturn(6L);
+        when(myLargeSegmentsStorage.getChangeNumber()).thenReturn(3L);
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(6L, 4L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), targetSegmentsChangeNumber, targetLargeSegmentsChangeNumber, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+    }
+
+    @Test
+    public void fetchIsPerformedWhenLargeSegmentsChangeNumberInStorageIsNewerThanTargetAndSegmentsChangeNumberIsOlder() throws HttpFetcherException {
+        long targetSegmentsChangeNumber = 5L;
+        long targetLargeSegmentsChangeNumber = 4L;
+        when(mySegmentsStorage.getChangeNumber()).thenReturn(3L);
+        when(myLargeSegmentsStorage.getChangeNumber()).thenReturn(6L);
+        when(mMySegmentsFetcher.execute(noParams, null))
+                .thenReturn(createChange(5L, 6L));
+
+        mTask = new MySegmentsSyncTask(mMySegmentsFetcher, mySegmentsStorage, myLargeSegmentsStorage, false, mEventsManager, mMySegmentsChangeChecker, mTelemetryRuntimeProducer, MySegmentsSyncTaskConfig.get(), targetSegmentsChangeNumber, targetLargeSegmentsChangeNumber, mock(BackoffCounter.class), 1);
+        mTask.execute();
+
+        verify(mMySegmentsFetcher).execute(any(), any());
+        verify(mMySegmentsFetcher).execute(noParams, null);
+    }
+
+    @NonNull
+    private static AllSegmentsChange createChange(Long msChangeNumber, Long lsChangeNumber) {
+        return AllSegmentsChange.create(
+                SegmentsChange.create(new HashSet<>(Collections.singletonList("segment0")), msChangeNumber),
+                SegmentsChange.create(new HashSet<>(Collections.singletonList("largesegment0")), lsChangeNumber));
+    }
+
+    @NonNull
+    private static AllSegmentsChange createChange(long lsChangeNumber) {
+        return createChange(null, lsChangeNumber);
+    }
+
     private void loadMySegments() {
         if (mMySegments == null) {
-            mMySegments = new ArrayList<>();
+            Set<String> segments = new HashSet<>();
             for (int i = 0; i < 5; i++) {
-                MySegment s = new MySegment();
-                s.id = "id_" + i;
-                s.name = "segment_" + i;
-                mMySegments.add(s);
+                segments.add("segment_" + i);
             }
+            mMySegments = AllSegmentsChange.create(SegmentsChange.create(segments, null), SegmentsChange.createEmpty());
         }
     }
 }
diff --git a/src/test/java/io/split/android/client/service/MySegmentsUpdateTaskTest.java b/src/test/java/io/split/android/client/service/MySegmentsUpdateTaskTest.java
index 9acb9614a..3e4972765 100644
--- a/src/test/java/io/split/android/client/service/MySegmentsUpdateTaskTest.java
+++ b/src/test/java/io/split/android/client/service/MySegmentsUpdateTaskTest.java
@@ -1,5 +1,14 @@
 package io.split.android.client.service;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -8,31 +17,25 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionStatus;
 import io.split.android.client.service.executor.SplitTaskType;
 import io.split.android.client.service.http.HttpFetcherException;
 import io.split.android.client.service.mysegments.MySegmentsUpdateTask;
+import io.split.android.client.service.mysegments.MySegmentsUpdateTaskConfig;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 import io.split.android.client.telemetry.model.streaming.UpdatesFromSSEEnum;
 import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
 public class MySegmentsUpdateTaskTest {
 
     @Mock
@@ -61,15 +64,15 @@ public void setup() {
 
     @Test
     public void correctExecution() throws HttpFetcherException {
-        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, mSegmentToRemove, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, Collections.singleton(mSegmentToRemove), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
 
-        ArgumentCaptor<List<String>> segmentsCaptor = ArgumentCaptor.forClass(List.class);
+        ArgumentCaptor<SegmentsChange> segmentsCaptor = ArgumentCaptor.forClass(SegmentsChange.class);
 
         SplitTaskExecutionInfo result = mTask.execute();
 
         verify(mySegmentsStorage, times(1)).set(segmentsCaptor.capture());
-        Assert.assertTrue(isSegmentRemoved(segmentsCaptor.getValue(), mSegmentToRemove));
-        Assert.assertFalse(isSegmentRemoved(segmentsCaptor.getValue(), mCustomerSegment));
+        Assert.assertTrue(isSegmentRemoved(segmentsCaptor.getValue().getNames(), mSegmentToRemove));
+        Assert.assertFalse(isSegmentRemoved(segmentsCaptor.getValue().getNames(), mCustomerSegment));
         Assert.assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus());
         Assert.assertEquals(SplitTaskType.MY_SEGMENTS_UPDATE, result.getTaskType());
     }
@@ -77,7 +80,7 @@ public void correctExecution() throws HttpFetcherException {
     @Test
     public void correctExecutionToEraseNotInSegments() throws HttpFetcherException {
         String otherSegment = "OtherSegment";
-        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, otherSegment, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, Collections.singleton(otherSegment), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
         ArgumentCaptor<List<String>> segmentsCaptor = ArgumentCaptor.forClass(List.class);
 
         SplitTaskExecutionInfo result = mTask.execute();
@@ -90,7 +93,7 @@ public void correctExecutionToEraseNotInSegments() throws HttpFetcherException {
     @Test
     public void storageException() {
 
-        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, mSegmentToRemove, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, Collections.singleton(mSegmentToRemove), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
         doThrow(NullPointerException.class).when(mySegmentsStorage).set(any());
 
         SplitTaskExecutionInfo result = mTask.execute();
@@ -101,7 +104,7 @@ public void storageException() {
 
     @Test
     public void successfulAddOperationIsRecordedInTelemetry() {
-        mTask = new MySegmentsUpdateTask(mySegmentsStorage, true, mSegmentToRemove, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, true, Collections.singleton(mSegmentToRemove), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
 
         mTask.execute();
 
@@ -110,13 +113,90 @@ public void successfulAddOperationIsRecordedInTelemetry() {
 
     @Test
     public void successfulRemoveOperationIsRecordedInTelemetry() {
-        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, mSegmentToRemove, mEventsManager, mTelemetryRuntimeProducer);
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, Collections.singleton(mSegmentToRemove), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
 
         mTask.execute();
 
         verify(mTelemetryRuntimeProducer).recordUpdatesFromSSE(UpdatesFromSSEEnum.MY_SEGMENTS);
     }
 
+    @Test
+    public void addOperationWithSegmentsAlreadyInStorage() {
+        Set<String> oldSegments = new HashSet<>();
+        oldSegments.add(mCustomerSegment);
+        oldSegments.add(mSegmentToRemove);
+
+        when(mySegmentsStorage.getAll()).thenReturn(oldSegments);
+
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, true, oldSegments, 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
+
+        SplitTaskExecutionInfo result = mTask.execute();
+
+        verify(mySegmentsStorage, never()).set(any());
+        verify(mEventsManager, never()).notifyInternalEvent(any());
+    }
+
+    @Test
+    public void addOperationWithOneSegmentAlreadyInStorage() {
+        Set<String> oldSegments = new HashSet<>();
+        oldSegments.add(mCustomerSegment);
+
+        when(mySegmentsStorage.getAll()).thenReturn(oldSegments);
+
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, true, new HashSet<>(Arrays.asList(mCustomerSegment, mSegmentToRemove)), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
+
+        ArgumentCaptor<SegmentsChange> segmentsCaptor = ArgumentCaptor.forClass(SegmentsChange.class);
+
+        SplitTaskExecutionInfo result = mTask.execute();
+
+        verify(mySegmentsStorage, times(1)).set(segmentsCaptor.capture());
+        Assert.assertTrue(segmentsCaptor.getValue().getNames().contains(mSegmentToRemove));
+        Assert.assertTrue(segmentsCaptor.getValue().getNames().contains(mCustomerSegment));
+        Assert.assertEquals(2, segmentsCaptor.getValue().getNames().size());
+        Assert.assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus());
+        Assert.assertEquals(SplitTaskType.MY_SEGMENTS_UPDATE, result.getTaskType());
+    }
+
+    @Test
+    public void removeOperationRemovesOnlyNotifiedSegments() {
+        Set<String> oldSegments = new HashSet<>();
+        oldSegments.add(mCustomerSegment);
+        oldSegments.add(mSegmentToRemove);
+        oldSegments.add("extra_segment");
+
+        when(mySegmentsStorage.getAll()).thenReturn(oldSegments);
+
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, new HashSet<>(Arrays.asList(mSegmentToRemove, "extra_segment")), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
+
+        ArgumentCaptor<SegmentsChange> segmentsCaptor = ArgumentCaptor.forClass(SegmentsChange.class);
+
+        SplitTaskExecutionInfo result = mTask.execute();
+
+        verify(mySegmentsStorage, times(1)).set(segmentsCaptor.capture());
+        SegmentsChange captorValue = segmentsCaptor.getValue();
+        Assert.assertFalse(captorValue.getNames().contains(mSegmentToRemove));
+        Assert.assertFalse(captorValue.getNames().contains("extra_segment"));
+        Assert.assertTrue(captorValue.getNames().contains(mCustomerSegment));
+        Assert.assertEquals(1, captorValue.getNames().size());
+        Assert.assertEquals(SplitTaskExecutionStatus.SUCCESS, result.getStatus());
+        verify(mEventsManager).notifyInternalEvent(SplitInternalEvent.MY_SEGMENTS_UPDATED);
+    }
+
+    @Test
+    public void removeOperationDoesNotNotifyWhenNothingWasRemoved() {
+        Set<String> oldSegments = new HashSet<>();
+        oldSegments.add(mCustomerSegment);
+        oldSegments.add(mSegmentToRemove);
+
+        when(mySegmentsStorage.getAll()).thenReturn(oldSegments);
+
+        mTask = new MySegmentsUpdateTask(mySegmentsStorage, false, Collections.singleton("extra_segment"), 25L, mEventsManager, mTelemetryRuntimeProducer, MySegmentsUpdateTaskConfig.getForMySegments());
+
+        SplitTaskExecutionInfo result = mTask.execute();
+
+        verify(mEventsManager, never()).notifyInternalEvent(any());
+    }
+
     @After
     public void tearDown() {
         reset(mySegmentsStorage);
diff --git a/src/test/java/io/split/android/client/service/SplitSyncTaskTest.java b/src/test/java/io/split/android/client/service/SplitSyncTaskTest.java
index 7692180d7..9c354f744 100644
--- a/src/test/java/io/split/android/client/service/SplitSyncTaskTest.java
+++ b/src/test/java/io/split/android/client/service/SplitSyncTaskTest.java
@@ -55,7 +55,7 @@ public void setup() {
         mSplitsSyncHelper = mock(SplitsSyncHelper.class);
         mEventsManager = mock(SplitEventsManager.class);
 
-        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLIT_KILL));
+        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLIT_KILL));
 
         loadSplitChanges();
     }
@@ -74,7 +74,7 @@ public void correctExecution() throws HttpFetcherException {
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper, times(1)).sync(-1, false, false);
+        verify(mSplitsSyncHelper, times(1)).sync(-1, false, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
     }
 
     @Test
@@ -106,7 +106,7 @@ public void cleanOldCacheEnabled() throws HttpFetcherException {
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper, times(1)).sync(100L, true, false);
+        verify(mSplitsSyncHelper, times(1)).sync(100L, true, true, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
     }
 
     @Test
@@ -127,7 +127,7 @@ public void cleanSplitsWhenQueryStringHasChanged() throws HttpFetcherException {
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper, times(1)).sync(-1, true, true);
+        verify(mSplitsSyncHelper, times(1)).sync(-1, true, true, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
         verify(mSplitsStorage, times(1)).updateSplitsFilterQueryString(otherQs);
     }
 
@@ -145,7 +145,7 @@ public void noClearSplitsWhenQueryStringHasNotChanged() throws HttpFetcherExcept
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper, times(1)).sync(100L, false, false);
+        verify(mSplitsSyncHelper, times(1)).sync(100L, false, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
         verify(mSplitsStorage, never()).updateSplitsFilterQueryString(anyString());
     }
 
@@ -160,7 +160,7 @@ public void splitUpdatedNotified() throws HttpFetcherException {
         when(mSplitsStorage.getTill()).thenReturn(-1L).thenReturn(100L);
         when(mSplitsStorage.getUpdateTimestamp()).thenReturn(0L);
         when(mSplitsStorage.getSplitsFilterQueryString()).thenReturn(mQueryString);
-        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
+        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
 
         mTask.execute();
 
@@ -178,7 +178,7 @@ public void splitFetchdNotified() throws HttpFetcherException {
         when(mSplitsStorage.getTill()).thenReturn(100L);
         when(mSplitsStorage.getUpdateTimestamp()).thenReturn(0L);
         when(mSplitsStorage.getSplitsFilterQueryString()).thenReturn(mQueryString);
-        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
+        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
 
         mTask.execute();
 
@@ -192,7 +192,7 @@ public void syncIsTrackedInTelemetry() {
         when(mSplitsStorage.getTill()).thenReturn(100L);
         when(mSplitsStorage.getUpdateTimestamp()).thenReturn(0L);
         when(mSplitsStorage.getSplitsFilterQueryString()).thenReturn(mQueryString);
-        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
+        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC));
 
         mTask.execute();
 
diff --git a/src/test/java/io/split/android/client/service/SplitTaskExecutorTest.java b/src/test/java/io/split/android/client/service/SplitTaskExecutorTest.java
index 90157d367..341150e1c 100644
--- a/src/test/java/io/split/android/client/service/SplitTaskExecutorTest.java
+++ b/src/test/java/io/split/android/client/service/SplitTaskExecutorTest.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskBatchItem;
@@ -130,7 +131,7 @@ public void simpleScheduled() throws InterruptedException {
         latch.await(10, TimeUnit.SECONDS);
 
         Assert.assertTrue(task.taskHasBeenCalled);
-        Assert.assertEquals(4, task.callCount);
+        Assert.assertEquals(4, task.callCount.get());
     }
 
     @Test
@@ -152,7 +153,7 @@ public void multipleScheduled() throws InterruptedException {
         for (int i = 0; i < taskCount; i++) {
             Assert.assertTrue(taskList.get(i).taskHasBeenCalled);
         }
-        Assert.assertTrue(4 < taskList.get(0).callCount);
+        Assert.assertTrue(4 < taskList.get(0).callCount.get());
     }
 
     @Test
@@ -192,8 +193,8 @@ public void pauseScheduled() throws InterruptedException {
         mTaskExecutor.schedule(
                 task1, 6L, 20, mock(SplitTaskExecutionListener.class));
         latch.await(5, TimeUnit.SECONDS);
-        int countBeforePause = task.callCount;
-        int count1BeforePause = task1.callCount;
+        int countBeforePause = task.callCount.get();
+        int count1BeforePause = task1.callCount.get();
 
         mTaskExecutor.pause();
 
@@ -201,13 +202,13 @@ public void pauseScheduled() throws InterruptedException {
         // then resumes task executor
         // and wait for task 1 latch
         sleep(3);
-        int countAfterPause = task.callCount;
-        int count1AfterPause = task1.callCount;
+        int countAfterPause = task.callCount.get();
+        int count1AfterPause = task1.callCount.get();
 
         mTaskExecutor.resume();
         latch1.await(5, TimeUnit.SECONDS);
 
-        int count1AfterResume = task1.callCount;
+        int count1AfterResume = task1.callCount.get();
 
         Assert.assertEquals(3, countBeforePause);
         Assert.assertEquals(0, count1BeforePause);
@@ -250,7 +251,7 @@ public void exceptionInScheduled() throws InterruptedException {
         latch.await(10, TimeUnit.SECONDS);
 
         Assert.assertTrue(task.taskHasBeenCalled);
-        Assert.assertEquals(4, task.callCount);
+        Assert.assertEquals(4, task.callCount.get());
     }
 
     @Test
@@ -278,7 +279,7 @@ public void stopStartedTask() throws InterruptedException {
 
         assertTrue(task.taskHasBeenCalled);
         assertTrue(testListener.taskExecutedCalled);
-        assertEquals(2, task.callCount);
+        assertEquals(2, task.callCount.get());
     }
 
     @Test
@@ -293,7 +294,7 @@ public void scheduleDelayedTask() throws InterruptedException {
         listenerLatch.await(2L, TimeUnit.SECONDS);
         assertTrue(task.taskHasBeenCalled);
         assertTrue(testListener.taskExecutedCalled);
-        assertEquals(1, task.callCount);
+        assertEquals(1, task.callCount.get());
     }
 
     @Test
@@ -314,7 +315,7 @@ public void schedulingAfterShutdown() throws InterruptedException {
 
         assertTrue(task.taskHasBeenCalled);
         assertTrue(testListener.taskExecutedCalled);
-        assertEquals(2, task.callCount);
+        assertEquals(2, task.callCount.get());
         assertFalse(newTask.taskHasBeenCalled);
     }
 
@@ -338,13 +339,13 @@ static class TestTask implements SplitTask {
         }
 
         public boolean shouldThrowException = false;
-        public int callCount = 0;
+        public AtomicInteger callCount = new AtomicInteger(0);
         public boolean taskHasBeenCalled = false;
 
         @NonNull
         @Override
         public SplitTaskExecutionInfo execute() {
-            callCount++;
+            callCount.incrementAndGet();
             taskHasBeenCalled = true;
             latch.countDown();
             if (shouldThrowException) {
diff --git a/src/test/java/io/split/android/client/service/SplitUpdateTaskTest.java b/src/test/java/io/split/android/client/service/SplitUpdateTaskTest.java
index 84e47742b..134db2981 100644
--- a/src/test/java/io/split/android/client/service/SplitUpdateTaskTest.java
+++ b/src/test/java/io/split/android/client/service/SplitUpdateTaskTest.java
@@ -3,33 +3,23 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.Mockito;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.events.SplitEventsManager;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskType;
-import io.split.android.client.service.http.HttpFetcher;
 import io.split.android.client.service.http.HttpFetcherException;
-import io.split.android.client.service.splits.SplitChangeProcessor;
 import io.split.android.client.service.splits.SplitsSyncHelper;
-import io.split.android.client.service.splits.SplitsSyncTask;
 import io.split.android.client.service.splits.SplitsUpdateTask;
-import io.split.android.client.storage.splits.ProcessedSplitChange;
 import io.split.android.client.storage.splits.SplitsStorage;
 import io.split.android.helpers.FileHelper;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doThrow;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -50,8 +40,8 @@ public void setup() {
         mSplitsSyncHelper = Mockito.mock(SplitsSyncHelper.class);
         mEventsManager = Mockito.mock(SplitEventsManager.class);
         mTask = new SplitsUpdateTask(mSplitsSyncHelper, mSplitsStorage, mChangeNumber, mEventsManager);
-        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK));
-        when(mSplitsSyncHelper.sync(anyLong())).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK));
+        when(mSplitsSyncHelper.sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK));
+        when(mSplitsSyncHelper.sync(anyLong(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES))).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK));
         loadSplitChanges();
     }
 
@@ -61,7 +51,7 @@ public void correctExecution() throws HttpFetcherException {
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper).sync(mChangeNumber);
+        verify(mSplitsSyncHelper).sync(mChangeNumber, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
     }
 
     @Test
@@ -70,7 +60,7 @@ public void storedChangeNumBigger() throws HttpFetcherException {
 
         mTask.execute();
 
-        verify(mSplitsSyncHelper, never()).sync(anyLong(), anyBoolean(), anyBoolean());
+        verify(mSplitsSyncHelper, never()).sync(anyLong(), anyBoolean(), anyBoolean(), eq(ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES));
     }
 
     @After
diff --git a/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java b/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java
index b99575481..1b2d9e104 100644
--- a/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java
+++ b/src/test/java/io/split/android/client/service/SplitsSyncHelperTest.java
@@ -94,7 +94,7 @@ public void correctSyncExecution() throws HttpFetcherException {
         when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange);
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, false, false);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, false, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null);
         verify(mSplitsStorage, times(1)).update(any());
@@ -116,7 +116,7 @@ public void correctSyncExecutionNoCache() throws HttpFetcherException {
         when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange);
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher, times(1)).execute(mDefaultParams, headers);
         verify(mSplitsStorage, times(1)).update(any());
@@ -131,7 +131,7 @@ public void fetcherSyncException() throws HttpFetcherException {
                 .thenThrow(HttpFetcherException.class);
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null);
         verify(mSplitsStorage, never()).update(any());
@@ -146,7 +146,7 @@ public void storageException() throws HttpFetcherException {
         doThrow(NullPointerException.class).when(mSplitsStorage).update(any(ProcessedSplitChange.class));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null);
         verify(mSplitsStorage, times(1)).update(any());
@@ -164,7 +164,7 @@ public void shouldClearStorageAfterFetch() throws HttpFetcherException {
         when(mSplitsFetcher.execute(mSecondFetchParams, null)).thenReturn(secondSplitChange);
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher, times(1)).execute(mDefaultParams, null);
         verify(mSplitsStorage, times(1)).update(any());
@@ -194,7 +194,7 @@ public void cacheNotExpired() throws HttpFetcherException {
         // only when cache expired
 
         long cacheExpInSeconds = 10000;
-        long updateTimestamp = System.currentTimeMillis() / 1000 - cacheExpInSeconds + 1000;
+        long updateTimestamp = System.currentTimeMillis() - cacheExpInSeconds * 1000 + 1000;
         boolean expired = mSplitsSyncHelper.cacheHasExpired(100, updateTimestamp, cacheExpInSeconds);
 
         Assert.assertFalse(expired);
@@ -220,7 +220,7 @@ public void errorIsRecordedInTelemetry() throws HttpFetcherException {
                 .thenThrow(new HttpFetcherException("error", "error", 500));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        mSplitsSyncHelper.sync(-1, true, false);
+        mSplitsSyncHelper.sync(-1, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mTelemetryRuntimeProducer).recordSyncError(OperationType.SPLITS, 500);
     }
@@ -240,7 +240,7 @@ public void performSplitsFetchUntilSinceEqualsTill() throws HttpFetcherException
         when(mSplitsFetcher.execute(eq(secondParams), any())).thenReturn(secondSplitChange);
         when(mSplitsFetcher.execute(eq(thirdParams), any())).thenReturn(thirdSplitChange);
 
-        mSplitsSyncHelper.sync(3);
+        mSplitsSyncHelper.sync(3, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsStorage, times(3)).getTill();
         verify(mSplitsFetcher).execute(eq(firstParams), any());
@@ -260,7 +260,7 @@ public void performSplitFetchUntilStoredChangeNumberIsGreaterThanRequested() thr
         when(mSplitsFetcher.execute(eq(firstParams), any())).thenReturn(firstSplitChange);
         when(mSplitsFetcher.execute(eq(secondParams), any())).thenReturn(secondSplitChange);
 
-        mSplitsSyncHelper.sync(3);
+        mSplitsSyncHelper.sync(3, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsStorage, times(3)).getTill();
         verify(mSplitsFetcher).execute(eq(firstParams), any());
@@ -271,7 +271,7 @@ public void performSplitFetchUntilStoredChangeNumberIsGreaterThanRequested() thr
     public void syncWithClearBeforeUpdateOnlyClearsStorageOnce() {
         when(mSplitsStorage.getTill()).thenReturn(-1L, 2L, 4L);
 
-        mSplitsSyncHelper.sync(3, true, false);
+        mSplitsSyncHelper.sync(3, true, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsStorage).clear();
     }
@@ -280,7 +280,7 @@ public void syncWithClearBeforeUpdateOnlyClearsStorageOnce() {
     public void syncWithoutClearBeforeUpdateDoesNotClearStorage() {
         when(mSplitsStorage.getTill()).thenReturn(-1L, 2L, 4L);
 
-        mSplitsSyncHelper.sync(3, false, false);
+        mSplitsSyncHelper.sync(3, false, false, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsStorage, never()).clear();
     }
@@ -304,7 +304,7 @@ public void cdnIsBypassedWhenNeeded() throws HttpFetcherException {
                 getSplitChange(4, 4)
         );
 
-        mSplitsSyncHelper.sync(4);
+        mSplitsSyncHelper.sync(4, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         Map<String, String> headers = new HashMap<>();
         headers.put("Cache-Control", "no-cache");
@@ -332,7 +332,7 @@ public void backoffIsAppliedWhenRetryingSplits() throws HttpFetcherException {
                 getSplitChange(4, 4)
         );
 
-        mSplitsSyncHelper.sync(4);
+        mSplitsSyncHelper.sync(4, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mBackoffCounter).resetCounter();
         verify(mBackoffCounter, times(2)).getNextRetryTime();
@@ -340,7 +340,7 @@ public void backoffIsAppliedWhenRetryingSplits() throws HttpFetcherException {
 
     @Test
     public void replaceTillWhenFilterHasChanged() throws HttpFetcherException {
-        mSplitsSyncHelper.sync(14829471, true, true);
+        mSplitsSyncHelper.sync(14829471, true, true, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         Map<String, Object> params = new HashMap<>();
         params.put("s", "1.1");
@@ -355,7 +355,7 @@ public void returnTaskInfoToDoNotRetryWhenHttpFetcherExceptionStatusCodeIs414()
                 .thenThrow(new HttpFetcherException("error", "error", 414));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         assertEquals(true, result.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY));
     }
@@ -366,7 +366,7 @@ public void doNotRetryFlagIsNullWhenFetcherExceptionStatusCodeIsNot414() throws
                 .thenThrow(new HttpFetcherException("error", "error", 500));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         assertNull(result.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY));
     }
@@ -377,7 +377,7 @@ public void returnTaskInfoToDoNotRetryWhenHttpFetcherExceptionStatusCodeIs9009()
                 .thenThrow(new HttpFetcherException("error", "error", 9009));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         assertEquals(true, result.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY));
     }
@@ -388,14 +388,14 @@ public void doNotRetryFlagIsNullWhenFetcherExceptionStatusCodeIsNot9009() throws
                 .thenThrow(new HttpFetcherException("error", "error", 500));
         when(mSplitsStorage.getTill()).thenReturn(-1L);
 
-        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1);
+        SplitTaskExecutionInfo result = mSplitsSyncHelper.sync(-1, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         assertNull(result.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY));
     }
 
     @Test
     public void defaultQueryParamOrderIsCorrect() throws HttpFetcherException {
-        mSplitsSyncHelper.sync(100);
+        mSplitsSyncHelper.sync(100, ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_RETRIES);
 
         verify(mSplitsFetcher).execute(argThat(new ArgumentMatcher<Map<String, Object>>() {
             @Override
diff --git a/src/test/java/io/split/android/client/service/SynchronizerTest.java b/src/test/java/io/split/android/client/service/SynchronizerTest.java
index 6a932d6a9..c0c32eabb 100644
--- a/src/test/java/io/split/android/client/service/SynchronizerTest.java
+++ b/src/test/java/io/split/android/client/service/SynchronizerTest.java
@@ -41,7 +41,6 @@
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.dtos.Event;
 import io.split.android.client.dtos.KeyImpression;
-import io.split.android.client.dtos.MySegment;
 import io.split.android.client.dtos.SplitChange;
 import io.split.android.client.events.SplitEventsManager;
 import io.split.android.client.impressions.Impression;
@@ -163,7 +162,7 @@ public void setup(SplitClientConfig splitClientConfig, ImpressionManagerConfig.M
         mTaskExecutor = taskExecutor;
         mSingleThreadedTaskExecutor = spy(new SplitTaskExecutorStub());
         HttpFetcher<SplitChange> splitsFetcher = Mockito.mock(HttpFetcher.class);
-        HttpFetcher<List<MySegment>> mySegmentsFetcher = Mockito.mock(HttpFetcher.class);
+        HttpFetcher mySegmentsFetcher = Mockito.mock(HttpFetcher.class);
         HttpRecorder<List<Event>> eventsRecorder = Mockito.mock(HttpRecorder.class);
         HttpRecorder<List<KeyImpression>> impressionsRecorder = Mockito.mock(HttpRecorder.class);
 
@@ -182,7 +181,7 @@ public void setup(SplitClientConfig splitClientConfig, ImpressionManagerConfig.M
         when(mSplitStorageContainer.getImpressionsStorage()).thenReturn(mImpressionsStorage);
 
         when(mTaskFactory.createSplitsSyncTask(anyBoolean())).thenReturn(Mockito.mock(SplitsSyncTask.class));
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(anyBoolean())).thenReturn(Mockito.mock(MySegmentsSyncTask.class));
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(anyBoolean(), anyLong(), anyLong())).thenReturn(Mockito.mock(MySegmentsSyncTask.class));
         when(mTaskFactory.createImpressionsRecorderTask()).thenReturn(Mockito.mock(ImpressionsRecorderTask.class));
         when(mTaskFactory.createEventsRecorderTask()).thenReturn(Mockito.mock(EventsRecorderTask.class));
         when(mTaskFactory.createLoadSplitsTask()).thenReturn(Mockito.mock(LoadSplitsTask.class));
@@ -247,6 +246,20 @@ public void splitExecutorSchedule() {
         mSynchronizer.unregisterMySegmentsSynchronizer("userKey");
     }
 
+    @Test
+    public void startPeriodicRecordingSchedulesLargeSegmentsSyncTask() {
+        SplitClientConfig config = SplitClientConfig.builder()
+                .eventsQueueSize(10)
+                .userConsent(UserConsent.GRANTED)
+                .impressionsQueueSize(3)
+                .build();
+        setup(config);
+
+        mSynchronizer.startPeriodicFetching();
+
+        verify(mMySegmentsSynchronizerRegistry).scheduleSegmentsSyncTask();
+    }
+
     @Test
     public void workManagerSchedule() throws InterruptedException {
         SplitClientConfig config = SplitClientConfig.builder()
@@ -515,7 +528,7 @@ public void loadLocalData() {
         setup(config);
 
         List<SplitTaskExecutionInfo> list = new ArrayList<>();
-        list.add(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SYGMENTS));
+        list.add(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SEGMENTS));
         list.add(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_SPLITS));
         list.add(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_ATTRIBUTES));
         SplitTaskExecutor executor = new SplitTaskExecutorSub(list);
@@ -528,7 +541,7 @@ public void loadLocalData() {
                 mFeatureFlagsSynchronizer, mSplitStorageContainer.getEventsStorage());
 
         LoadMySegmentsTask loadMySegmentsTask = mock(LoadMySegmentsTask.class);
-        when(loadMySegmentsTask.execute()).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SYGMENTS));
+        when(loadMySegmentsTask.execute()).thenReturn(SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_MY_SEGMENTS));
         when(mMySegmentsTaskFactory.createLoadMySegmentsTask()).thenReturn(loadMySegmentsTask);
 
         ((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer("", mMySegmentsSynchronizer);
@@ -608,15 +621,6 @@ public void synchronizeMySegmentsDelegatesToRegistry() {
         verify(mMySegmentsSynchronizerRegistry).synchronizeMySegments();
     }
 
-    @Test
-    public void forceMySegmentsSyncDelegatesToRegistry() {
-        setup(SplitClientConfig.builder().synchronizeInBackground(false).build());
-
-        mSynchronizer.forceMySegmentsSync();
-
-        verify(mMySegmentsSynchronizerRegistry).forceMySegmentsSync();
-    }
-
     @Test
     public void destroyDelegatesToRegisteredSyncs() {
         setup(SplitClientConfig.builder().synchronizeInBackground(false).build());
@@ -685,8 +689,8 @@ public void beingNotifiedOfSplitsSyncTaskTriggersSplitsLoad() {
     }
 
     @Test
-    public void beginNotifiedOfMySegmentsSyncTriggersMySegmentsLoad() {
-        setup(SplitClientConfig.builder().persistentAttributesEnabled(false).build());
+    public void beingNotifiedOfMySegmentsSyncTriggersMySegmentsLoad() {
+        setup(SplitClientConfig.builder().build());
 
         mSynchronizer.taskExecuted(SplitTaskExecutionInfo.success(SplitTaskType.MY_SEGMENTS_SYNC));
 
@@ -767,6 +771,24 @@ public void reschedulingEventsTaskCancelsPreviousWhenCallingSequentially() {
         verify(mTaskExecutor, times(1)).stopTask("task-id");
     }
 
+    @Test
+    public void registerMySegmentsSynchronizerDelegatesToRegistry() {
+        setup(SplitClientConfig.builder().synchronizeInBackground(false).build());
+
+        mSynchronizer.registerMySegmentsSynchronizer("userKey", mMySegmentsSynchronizer);
+
+        verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer("userKey", mMySegmentsSynchronizer);
+    }
+
+    @Test
+    public void synchronizeSplitsDelegatesToFeatureFlagsSynchronizer() {
+        setup(SplitClientConfig.builder().synchronizeInBackground(false).build());
+
+        mSynchronizer.synchronizeSplits();
+
+        verify(mFeatureFlagsSynchronizer).synchronize();
+    }
+
     @After
     public void tearDown() {
     }
diff --git a/src/test/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImplTest.java b/src/test/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImplTest.java
new file mode 100644
index 000000000..5ccc083bd
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/http/mysegments/MySegmentsFetcherFactoryImplTest.java
@@ -0,0 +1,40 @@
+package io.split.android.client.service.http.mysegments;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.network.HttpClient;
+import io.split.android.client.service.http.HttpFetcher;
+import io.split.android.client.service.http.HttpResponseParser;
+
+public class MySegmentsFetcherFactoryImplTest {
+
+    @Test
+    public void getFetcherCallsUriBuilderImplementation() throws URISyntaxException {
+        MySegmentsFetcherFactory.UriBuilder uriBuilder = mock(MySegmentsFetcherFactory.UriBuilder.class);
+        when(uriBuilder.build("matchingKey")).thenReturn(mock(URI.class));
+        HttpFetcher<AllSegmentsChange> fetcher = new MySegmentsFetcherFactoryImpl(mock(HttpClient.class),
+                "endpoint",
+                mock(HttpResponseParser.class),
+                uriBuilder).getFetcher("matchingKey");
+
+        verify(uriBuilder).build("matchingKey");
+    }
+
+    @Test
+    public void getFetcherDoesNotCrashWhenUriBuilderFails() throws URISyntaxException {
+        MySegmentsFetcherFactory.UriBuilder uriBuilder = mock(MySegmentsFetcherFactory.UriBuilder.class);
+        when(uriBuilder.build("matchingKey")).thenThrow(new URISyntaxException("test", "test"));
+        HttpFetcher<AllSegmentsChange> fetcher = new MySegmentsFetcherFactoryImpl(mock(HttpClient.class),
+                "endpoint",
+                mock(HttpResponseParser.class),
+                uriBuilder).getFetcher("matchingKey");
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/mysegments/AllSegmentsResponseParserTest.java b/src/test/java/io/split/android/client/service/mysegments/AllSegmentsResponseParserTest.java
new file mode 100644
index 000000000..dd3067e48
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/mysegments/AllSegmentsResponseParserTest.java
@@ -0,0 +1,38 @@
+package io.split.android.client.service.mysegments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.service.http.HttpResponseParserException;
+
+public class AllSegmentsResponseParserTest {
+
+    private AllSegmentsResponseParser mParser;
+
+    @Before
+    public void setUp() {
+        mParser = new AllSegmentsResponseParser();
+    }
+
+    @Test
+    public void parse() throws HttpResponseParserException {
+        String response = "{\"ms\":{\"k\":[{\"n\":\"segment1\"},{\"n\":\"segment2\"}], \"cn\":null},\"ls\":{\"k\":[{\"n\":\"large-segment1\"},{\"n\":\"large-segment2\"},{\"n\":\"large-segment3\"}], \"cn\":2000}}";
+
+        AllSegmentsChange parsed = mParser.parse(response);
+
+        assertTrue(parsed.getSegmentsChange().getNames().contains("segment1"));
+        assertTrue(parsed.getSegmentsChange().getNames().contains("segment2"));
+        assertTrue(parsed.getLargeSegmentsChange().getNames().contains("large-segment1"));
+        assertTrue(parsed.getLargeSegmentsChange().getNames().contains("large-segment2"));
+        assertTrue(parsed.getLargeSegmentsChange().getNames().contains("large-segment3"));
+        assertEquals(2, parsed.getSegmentsChange().getSegments().size());
+        assertEquals(3, parsed.getLargeSegmentsChange().getSegments().size());
+        assertNull(parsed.getSegmentsChange().getChangeNumber());
+        assertEquals(Long.valueOf(2000), parsed.getLargeSegmentsChange().getChangeNumber());
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfigTest.java b/src/test/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfigTest.java
new file mode 100644
index 000000000..9973189e6
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/mysegments/LoadMySegmentsTaskConfigTest.java
@@ -0,0 +1,17 @@
+package io.split.android.client.service.mysegments;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import io.split.android.client.service.executor.SplitTaskType;
+
+public class LoadMySegmentsTaskConfigTest {
+
+    @Test
+    public void getForMySegments() {
+        LoadMySegmentsTaskConfig config = LoadMySegmentsTaskConfig.get();
+
+        assertEquals(config.getTaskType(), SplitTaskType.LOAD_LOCAL_MY_SEGMENTS);
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfigTest.java b/src/test/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfigTest.java
new file mode 100644
index 000000000..07f4fa6a0
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/mysegments/MySegmentsSyncTaskConfigTest.java
@@ -0,0 +1,22 @@
+package io.split.android.client.service.mysegments;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.executor.SplitTaskType;
+import io.split.android.client.telemetry.model.OperationType;
+
+public class MySegmentsSyncTaskConfigTest {
+
+    @Test
+    public void getForMySegments() {
+        MySegmentsSyncTaskConfig config = MySegmentsSyncTaskConfig.get();
+
+        assertEquals(config.getTaskType(), SplitTaskType.MY_SEGMENTS_SYNC);
+        assertEquals(config.getUpdateEvent(), SplitInternalEvent.MY_SEGMENTS_UPDATED);
+        assertEquals(config.getFetchedEvent(), SplitInternalEvent.MY_SEGMENTS_FETCHED);
+        assertEquals(config.getTelemetryOperationType(), OperationType.MY_SEGMENT);
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfigurationTest.java b/src/test/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfigurationTest.java
new file mode 100644
index 000000000..765714214
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/mysegments/MySegmentsTaskFactoryConfigurationTest.java
@@ -0,0 +1,45 @@
+package io.split.android.client.service.mysegments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import io.split.android.client.dtos.AllSegmentsChange;
+import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.service.http.HttpFetcher;
+import io.split.android.client.storage.mysegments.MySegmentsStorage;
+
+public class MySegmentsTaskFactoryConfigurationTest {
+
+    private HttpFetcher<AllSegmentsChange> mHttpFetcher;
+    private MySegmentsStorage mMySegmentsStorage;
+    private MySegmentsStorage mMyLargeSegmentsStorage;
+    private SplitEventsManager mEventsManager;
+
+    @Before
+    public void setUp() {
+        mHttpFetcher = mock(HttpFetcher.class);
+        mMySegmentsStorage = mock(MySegmentsStorage.class);
+        mMyLargeSegmentsStorage = mock(MySegmentsStorage.class);
+        mEventsManager = mock(SplitEventsManager.class);
+    }
+
+    @Test
+    public void getForMySegments() {
+        MySegmentsTaskFactoryConfiguration config = MySegmentsTaskFactoryConfiguration.get(
+                mHttpFetcher,
+                mMySegmentsStorage,
+                mMyLargeSegmentsStorage,
+                mEventsManager);
+
+        assertSame(mHttpFetcher, config.getHttpFetcher());
+        assertSame(mMySegmentsStorage, config.getMySegmentsStorage());
+        assertSame(mEventsManager, config.getEventsManager());
+        assertEquals(MySegmentsSyncTaskConfig.get(), config.getMySegmentsSyncTaskConfig());
+        assertEquals(MySegmentsUpdateTaskConfig.getForMySegments(), config.getMySegmentsUpdateTaskConfig());
+        assertEquals(LoadMySegmentsTaskConfig.get(), config.getLoadMySegmentsTaskConfig());
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfigTest.java b/src/test/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfigTest.java
new file mode 100644
index 000000000..ea7f26181
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/mysegments/MySegmentsUpdateTaskConfigTest.java
@@ -0,0 +1,21 @@
+package io.split.android.client.service.mysegments;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import io.split.android.client.events.SplitInternalEvent;
+import io.split.android.client.service.executor.SplitTaskType;
+import io.split.android.client.telemetry.model.streaming.UpdatesFromSSEEnum;
+
+public class MySegmentsUpdateTaskConfigTest {
+
+    @Test
+    public void getForMySegments() {
+        MySegmentsUpdateTaskConfig config = MySegmentsUpdateTaskConfig.getForMySegments();
+
+        assertEquals(config.getTaskType(), SplitTaskType.MY_SEGMENTS_UPDATE);
+        assertEquals(config.getUpdateEvent(), SplitInternalEvent.MY_SEGMENTS_UPDATED);
+        assertEquals(config.getTelemetrySSEKey(), UpdatesFromSSEEnum.MY_SEGMENTS);
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/sseclient/MySegmentsUpdateWorkerTest.java b/src/test/java/io/split/android/client/service/sseclient/MySegmentsUpdateWorkerTest.java
index a3547bad5..f1cd8ac0c 100644
--- a/src/test/java/io/split/android/client/service/sseclient/MySegmentsUpdateWorkerTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/MySegmentsUpdateWorkerTest.java
@@ -1,5 +1,6 @@
 package io.split.android.client.service.sseclient;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -12,7 +13,7 @@
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorker;
 import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizer;
 
@@ -23,7 +24,7 @@ public class MySegmentsUpdateWorkerTest {
     @Mock
     MySegmentsSynchronizer mSynchronizer;
 
-    BlockingQueue<MySegmentChangeNotification> mNotificationQueue;
+    BlockingQueue<MySegmentUpdateParams> mNotificationQueue;
 
     @Before
     public void setup() {
@@ -36,20 +37,27 @@ public void setup() {
     @Test
     public void mySegmentsUpdateReceived() throws InterruptedException {
 
-        mNotificationQueue.offer(new MySegmentChangeNotification());
-        mNotificationQueue.offer(new MySegmentChangeNotification());
-        mNotificationQueue.offer(new MySegmentChangeNotification());
-        mNotificationQueue.offer(new MySegmentChangeNotification());
-
-        Thread.sleep(1000);
-
-        verify(mSynchronizer, times(4)).forceMySegmentsSync();
+        MySegmentUpdateParams params = mock(MySegmentUpdateParams.class);
+        MySegmentUpdateParams params2 = mock(MySegmentUpdateParams.class);
+        MySegmentUpdateParams params3 = mock(MySegmentUpdateParams.class);
+        MySegmentUpdateParams params4 = mock(MySegmentUpdateParams.class);
+        mNotificationQueue.offer(params);
+        mNotificationQueue.offer(params2);
+        mNotificationQueue.offer(params3);
+        mNotificationQueue.offer(params4);
+
+        Thread.sleep(200);
+
+        verify(mSynchronizer, times(1)).forceMySegmentsSync(params);
+        verify(mSynchronizer, times(1)).forceMySegmentsSync(params2);
+        verify(mSynchronizer, times(1)).forceMySegmentsSync(params3);
+        verify(mSynchronizer, times(1)).forceMySegmentsSync(params4);
     }
 
     @Test
     public void stopped() throws InterruptedException {
         mWorker.stop();
-        mNotificationQueue.offer(new MySegmentChangeNotification());
+        mNotificationQueue.offer(mock(MySegmentUpdateParams.class));
 
         Thread.sleep(1000);
 
diff --git a/src/test/java/io/split/android/client/service/sseclient/NotificationParserTest.java b/src/test/java/io/split/android/client/service/sseclient/NotificationParserTest.java
index 9a80a13aa..5e6adb906 100644
--- a/src/test/java/io/split/android/client/service/sseclient/NotificationParserTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/NotificationParserTest.java
@@ -1,15 +1,22 @@
 package io.split.android.client.service.sseclient;
 
+import static org.junit.Assert.assertEquals;
+import static io.split.android.client.service.sseclient.notifications.NotificationType.MEMBERSHIPS_LS_UPDATE;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import io.split.android.client.common.CompressionType;
 import io.split.android.client.service.sseclient.notifications.ControlNotification;
+import io.split.android.client.service.sseclient.notifications.HashingAlgorithm;
 import io.split.android.client.service.sseclient.notifications.IncomingNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
+import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
 import io.split.android.client.service.sseclient.notifications.NotificationParser;
 import io.split.android.client.service.sseclient.notifications.NotificationType;
 import io.split.android.client.service.sseclient.notifications.OccupancyNotification;
@@ -25,17 +32,13 @@ public class NotificationParserTest {
             "{\"id\":\"VSEQrcq9D8:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:MjU4MzkwNDA2NA==\",\"timestamp\":1584554772719,\"encoding\":\"json\",\"channel\":\"MzM5Njc0ODcyNg==_MTExMzgwNjgx_splits\",\n" +
                     "\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1584554772108}\"}";
 
-    private final static String MY_SEGMENT_UDATE_NOTIFICATION = "{\"id\":\"x2dE2TEiJL:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:OTc5Nzc4NDYz\",\"timestamp\":1584647533288,\"encoding\":\"json\",\"channel\":\"MzM5Njc0ODcyNg==_MTExMzgwNjgx_MTcwNTI2MTM0Mg==_mySegments\",\"data\":\"{\\\"type\\\":\\\"MY_SEGMENTS_UPDATE\\\",\\\"changeNumber\\\":1584647532812,\\\"includesPayload\\\":false}\"}";
-
     private final static String SPLIT_KILL_NOTIFICATION = "{\"id\":\"-OT-rGuSwz:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:NDIxNjU0NTUyNw==\",\"timestamp\":1584647606489,\"encoding\":\"json\",\"channel\":\"MzM5Njc0ODcyNg==_MTExMzgwNjgx_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_KILL\\\",\\\"changeNumber\\\":1584647606125,\\\"defaultTreatment\\\":\\\"off\\\",\\\"splitName\\\":\\\"dep_split\\\"}\"}";
 
-    private final static String MY_SEGMENT_UDATE_INLINE_NOTIFICATION = "{\"id\":\"x2dE2TEiJL:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:OTc5Nzc4NDYz\",\"timestamp\":1584647533288,\"encoding\":\"json\",\"channel\":\"MzM5Njc0ODcyNg==_MTExMzgwNjgx_MTcwNTI2MTM0Mg==_mySegments\",\"data\":\"{\\\"type\\\":\\\"MY_SEGMENTS_UPDATE\\\",\\\"changeNumber\\\":1584647532812,\\\"includesPayload\\\":true,\\\"segmentList\\\":[\\\"segment1\\\", \\\"segment2\\\"]}\"}";
-
     private final static String OCCUPANCY = "{\"id\":\"x2dE2TEiJL:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:OTc5Nzc4NDYz\",\"timestamp\":1584647533288,\"encoding\":\"json\",\"channel\":\"control_pri\",\"data\":\"{\\\"metrics\\\": {\\\"publishers\\\":1}}\"}";
 
     private final static String CONTROL = "{\"id\":\"x2dE2TEiJL:0:0\",\"clientId\":\"NDEzMTY5Mzg0MA==:OTc5Nzc4NDYz\",\"timestamp\":1584647533288,\"encoding\":\"json\",\"channel\":\"control_pri\",\"data\":\"{\\\"type\\\":\\\"CONTROL\\\",\\\"controlType\\\":\\\"STREAMING_RESUMED\\\"}\"}";
 
-    private final static String ERROR = "{\"id\":\"null\",\"name\":\"error\",\"comment\":\"[no comments]\",\"data\":\"{\\\"message\\\":\\\"Invalid token; capability must be a string\\\",\\\"code\\\":40144,\\\"statusCode\\\":400,\\\"href\\\":\\\"https://help.ably.io/error/40144\\\"}\"}";
+    private static final String MY_LARGE_SEGMENTS_UPDATE = "{\"id\": \"diSrQttrC9:0:0\",\"clientId\": \"pri:MjcyNDE2NDUxMA==\",\"timestamp\": 1702507131100,\"encoding\": \"json\",\"channel\": \"NzM2MDI5Mzc0_MTc1MTYwODQxMQ==_memberships\",\"data\": \"{\\\"type\\\":\\\"MEMBERSHIPS_LS_UPDATE\\\",\\\"cn\\\":1702507130121,\\\"n\\\":[\\\"android_test\\\"],\\\"c\\\":2,\\\"u\\\":2,\\\"d\\\":\\\"eJwEwLsRwzAMA9BdWKsg+IFBraJTkRXS5rK7388+tg+KdC8+jq4eBBQLFcUnO8FAAC36gndOSEyFqJFP32Vf2+f+3wAAAP//hUQQ9A==\\\",\\\"i\\\":100,\\\"h\\\":1,\\\"s\\\":325}\"}";
 
     @Before
     public void setup() {
@@ -47,8 +50,8 @@ public void processSplitUpdate() {
         IncomingNotification incoming = mParser.parseIncoming(SPLIT_UPDATE_NOTIFICATION);
         SplitsChangeNotification splitUpdate = mParser.parseSplitUpdate(incoming.getJsonData());
 
-        Assert.assertEquals(NotificationType.SPLIT_UPDATE, incoming.getType());
-        Assert.assertEquals(1584554772108L, splitUpdate.getChangeNumber());
+        assertEquals(NotificationType.SPLIT_UPDATE, incoming.getType());
+        assertEquals(1584554772108L, splitUpdate.getChangeNumber());
     }
 
     @Test
@@ -56,32 +59,9 @@ public void processSplitKill() {
         IncomingNotification incoming = mParser.parseIncoming(SPLIT_KILL_NOTIFICATION);
         SplitKillNotification splitKill = mParser.parseSplitKill(incoming.getJsonData());
 
-        Assert.assertEquals(NotificationType.SPLIT_KILL, incoming.getType());
-        Assert.assertEquals("dep_split", splitKill.getSplitName());
-        Assert.assertEquals("off", splitKill.getDefaultTreatment());
-    }
-
-    @Test
-    public void processMySegmentUpdate() {
-        IncomingNotification incoming = mParser.parseIncoming(MY_SEGMENT_UDATE_NOTIFICATION);
-        MySegmentChangeNotification mySegmentUpdate = mParser.parseMySegmentUpdate(incoming.getJsonData());
-
-        Assert.assertEquals(NotificationType.MY_SEGMENTS_UPDATE, incoming.getType());
-        Assert.assertEquals(1584647532812L, mySegmentUpdate.getChangeNumber());
-        Assert.assertFalse(mySegmentUpdate.isIncludesPayload());
-    }
-
-    @Test
-    public void processMySegmentUpdateInline() {
-        IncomingNotification incoming = mParser.parseIncoming(MY_SEGMENT_UDATE_INLINE_NOTIFICATION);
-        MySegmentChangeNotification mySegmentUpdate = mParser.parseMySegmentUpdate(incoming.getJsonData());
-
-        Assert.assertEquals(NotificationType.MY_SEGMENTS_UPDATE, incoming.getType());
-        Assert.assertEquals(1584647532812L, mySegmentUpdate.getChangeNumber());
-        Assert.assertTrue(mySegmentUpdate.isIncludesPayload());
-        Assert.assertEquals(2, mySegmentUpdate.getSegmentList().size());
-        Assert.assertEquals("segment1", mySegmentUpdate.getSegmentList().get(0));
-        Assert.assertEquals("segment2", mySegmentUpdate.getSegmentList().get(1));
+        assertEquals(NotificationType.SPLIT_KILL, incoming.getType());
+        assertEquals("dep_split", splitKill.getSplitName());
+        assertEquals("off", splitKill.getDefaultTreatment());
     }
 
     @Test
@@ -90,8 +70,8 @@ public void processOccupancy() {
 
         OccupancyNotification notification = mParser.parseOccupancy(incoming.getJsonData());
 
-        Assert.assertEquals(NotificationType.OCCUPANCY, notification.getType());
-        Assert.assertEquals(1, notification.getMetrics().getPublishers());
+        assertEquals(NotificationType.OCCUPANCY, notification.getType());
+        assertEquals(1, notification.getMetrics().getPublishers());
     }
 
     @Test
@@ -99,8 +79,8 @@ public void processControl() {
         IncomingNotification incoming = mParser.parseIncoming(CONTROL);
         ControlNotification notification = mParser.parseControl(incoming.getJsonData());
 
-        Assert.assertEquals(NotificationType.CONTROL, notification.getType());
-        Assert.assertEquals(ControlNotification.ControlType.STREAMING_RESUMED, notification.getControlType());
+        assertEquals(NotificationType.CONTROL, notification.getType());
+        assertEquals(ControlNotification.ControlType.STREAMING_RESUMED, notification.getControlType());
     }
 
     @Test
@@ -110,9 +90,9 @@ public void parseErrorMessage() {
 
         StreamingError errorMsg = mParser.parseError(data);
 
-        Assert.assertEquals("Token expired", errorMsg.getMessage());
-        Assert.assertEquals(40142, errorMsg.getCode());
-        Assert.assertEquals(401, errorMsg.getStatusCode());
+        assertEquals("Token expired", errorMsg.getMessage());
+        assertEquals(40142, errorMsg.getCode());
+        assertEquals(401, errorMsg.getStatusCode());
     }
 
     @Test
@@ -153,4 +133,29 @@ public void NoCrashIfisNullEventError() {
 
         Assert.assertFalse(isError);
     }
+
+    @Test
+    public void parseMyLargeSegmentsIncomingNotification() {
+        IncomingNotification incoming = mParser.parseIncoming(MY_LARGE_SEGMENTS_UPDATE);
+
+        assertEquals(MEMBERSHIPS_LS_UPDATE, incoming.getType());
+        assertEquals("{\"type\":\"MEMBERSHIPS_LS_UPDATE\",\"cn\":1702507130121,\"n\":[\"android_test\"],\"c\":2,\"u\":2,\"d\":\"eJwEwLsRwzAMA9BdWKsg+IFBraJTkRXS5rK7388+tg+KdC8+jq4eBBQLFcUnO8FAAC36gndOSEyFqJFP32Vf2+f+3wAAAP//hUQQ9A==\",\"i\":100,\"h\":1,\"s\":325}", incoming.getJsonData());
+        assertEquals("NzM2MDI5Mzc0_MTc1MTYwODQxMQ==_memberships", incoming.getChannel());
+        assertEquals(1702507131100L, incoming.getTimestamp());
+    }
+
+    @Test
+    public void parseMyLargeSegmentsNotificationData() {
+        IncomingNotification incomingNotification = mParser.parseIncoming(MY_LARGE_SEGMENTS_UPDATE);
+        MembershipNotification notification = mParser.parseMembershipNotification(incomingNotification.getJsonData());
+
+        assertEquals("eJwEwLsRwzAMA9BdWKsg+IFBraJTkRXS5rK7388+tg+KdC8+jq4eBBQLFcUnO8FAAC36gndOSEyFqJFP32Vf2+f+3wAAAP//hUQQ9A==", notification.getData());
+        assertEquals((Long) 1702507130121L, notification.getChangeNumber());
+        assertEquals(Collections.singleton("android_test"), notification.getNames());
+        assertEquals(CompressionType.ZLIB, notification.getCompression());
+        assertEquals(MySegmentUpdateStrategy.KEY_LIST, notification.getUpdateStrategy());
+        assertEquals((Long) 100L, notification.getUpdateIntervalMs());
+        assertEquals((Integer) 325, notification.getAlgorithmSeed());
+        assertEquals(HashingAlgorithm.MURMUR3_32, notification.getHashingAlgorithm());
+    }
 }
diff --git a/src/test/java/io/split/android/client/service/sseclient/NotificationProcessorTest.java b/src/test/java/io/split/android/client/service/sseclient/NotificationProcessorTest.java
index 1f05cc94d..863d7805e 100644
--- a/src/test/java/io/split/android/client/service/sseclient/NotificationProcessorTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/NotificationProcessorTest.java
@@ -21,15 +21,13 @@
 import io.split.android.client.service.executor.SplitTaskFactory;
 import io.split.android.client.service.splits.SplitKillTask;
 import io.split.android.client.service.sseclient.notifications.IncomingNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
-import io.split.android.client.service.sseclient.notifications.MySegmentsPayloadDecoder;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
 import io.split.android.client.service.sseclient.notifications.NotificationParser;
 import io.split.android.client.service.sseclient.notifications.NotificationProcessor;
 import io.split.android.client.service.sseclient.notifications.NotificationType;
 import io.split.android.client.service.sseclient.notifications.SplitKillNotification;
 import io.split.android.client.service.sseclient.notifications.SplitsChangeNotification;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessor;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessor;
 
 public class NotificationProcessorTest {
 
@@ -43,8 +41,6 @@ public class NotificationProcessorTest {
     private BlockingQueue<SplitsChangeNotification> mSplitsChangeQueue;
     @Mock
     private IncomingNotification mIncomingNotification;
-    @Mock
-    private MySegmentsPayloadDecoder mMySegmentsPayloadDecoder;
     private NotificationProcessor mNotificationProcessor;
 
     @Before
@@ -57,7 +53,7 @@ public void setup() {
 
         mNotificationProcessor = new NotificationProcessor(mSplitTaskExecutor,
                 mSplitTaskFactory, mNotificationParser,
-                mSplitsChangeQueue, mMySegmentsPayloadDecoder);
+                mSplitsChangeQueue);
     }
 
     @Test
@@ -92,40 +88,20 @@ public void splitKillNotification() {
         verify(mSplitTaskExecutor, times(1)).submit(any(), isNull());
     }
 
-    @Test
-    public void notificationProcessorDelegatesRegisteredProcessorDependingOnKey() {
-        MySegmentsNotificationProcessor mySegmentsNotificationProcessor = mock(MySegmentsNotificationProcessor.class);
-        MySegmentsNotificationProcessor mySegmentsNotificationProcessor2 = mock(MySegmentsNotificationProcessor.class);
-        MySegmentChangeNotification mySegmentChangeNotification = mock(MySegmentChangeNotification.class);
-        when(mNotificationParser.extractUserKeyHashFromChannel("a_b_MjAwNjI0Nzg3NQ==_mySegments")).thenReturn("MjAwNjI0Nzg3NQ==");
-        when(mIncomingNotification.getChannel()).thenReturn("a_b_MjAwNjI0Nzg3NQ==_mySegments");
-        when(mMySegmentsPayloadDecoder.hashUserKeyForMySegmentsV1("user_key")).thenReturn("MjAwNjI0Nzg3NQ==");
-
-        when(mySegmentChangeNotification.getJsonData()).thenReturn("{}");
-        when(mIncomingNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(mySegmentChangeNotification);
-
-        mNotificationProcessor.registerMySegmentsProcessor("user_key", mySegmentsNotificationProcessor);
-        mNotificationProcessor.registerMySegmentsProcessor("button_key", mySegmentsNotificationProcessor2);
-        mNotificationProcessor.process(mIncomingNotification);
-
-        verify(mySegmentsNotificationProcessor).processMySegmentsUpdate(mySegmentChangeNotification);
-    }
-
     @Test
     public void notificationProcessorDelegatesMySegmentsNotificationsV2ToRegisteredProcessors() {
-        MySegmentsNotificationProcessor mySegmentsNotificationProcessor = mock(MySegmentsNotificationProcessor.class);
-        MySegmentsNotificationProcessor mySegmentsNotificationProcessor2 = mock(MySegmentsNotificationProcessor.class);
-        MySegmentChangeV2Notification mySegmentChangeNotification = mock(MySegmentChangeV2Notification.class);
+        MembershipsNotificationProcessor mySegmentsNotificationProcessor = mock(MembershipsNotificationProcessor.class);
+        MembershipsNotificationProcessor mySegmentsNotificationProcessor2 = mock(MembershipsNotificationProcessor.class);
+        MembershipNotification mySegmentChangeNotification = mock(MembershipNotification.class);
 
         when(mySegmentChangeNotification.getJsonData()).thenReturn("{}");
-        when(mIncomingNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(mySegmentChangeNotification);
+        when(mIncomingNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(mySegmentChangeNotification);
 
-        mNotificationProcessor.registerMySegmentsProcessor("1", mySegmentsNotificationProcessor);
-        mNotificationProcessor.registerMySegmentsProcessor("2", mySegmentsNotificationProcessor2);
+        mNotificationProcessor.registerMembershipsNotificationProcessor("1", mySegmentsNotificationProcessor);
+        mNotificationProcessor.registerMembershipsNotificationProcessor("2", mySegmentsNotificationProcessor2);
         mNotificationProcessor.process(mIncomingNotification);
 
-        verify(mySegmentsNotificationProcessor).processMySegmentsUpdateV2(mySegmentChangeNotification);
+        verify(mySegmentsNotificationProcessor).process(mySegmentChangeNotification);
     }
 }
diff --git a/src/test/java/io/split/android/client/service/sseclient/SseHandlerTest.java b/src/test/java/io/split/android/client/service/sseclient/SseHandlerTest.java
index 049784277..f6bec9eda 100644
--- a/src/test/java/io/split/android/client/service/sseclient/SseHandlerTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/SseHandlerTest.java
@@ -23,8 +23,7 @@
 import io.split.android.client.service.sseclient.feedbackchannel.PushStatusEvent.EventType;
 import io.split.android.client.service.sseclient.notifications.ControlNotification;
 import io.split.android.client.service.sseclient.notifications.IncomingNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
 import io.split.android.client.service.sseclient.notifications.NotificationParser;
 import io.split.android.client.service.sseclient.notifications.NotificationProcessor;
 import io.split.android.client.service.sseclient.notifications.NotificationType;
@@ -100,14 +99,14 @@ public void incomingSplitKill() {
     }
 
     @Test
-    public void incomingMySegmentsUpdate() {
+    public void incomingMembershipUpdate() {
 
         IncomingNotification incomingNotification =
-                new IncomingNotification(NotificationType.MY_SEGMENTS_UPDATE, "", "", 100);
-        MySegmentChangeNotification notification = new MySegmentChangeNotification();
+                new IncomingNotification(NotificationType.MEMBERSHIPS_MS_UPDATE, "", "", 100);
+        MembershipNotification notification = new MembershipNotification();
 
         when(mNotificationParser.parseIncoming(anyString())).thenReturn(incomingNotification);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(notification);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(notification);
         when(mManagerKeeper.isStreamingActive()).thenReturn(true);
 
         mSseHandler.handleIncomingMessage(buildMessage("{}"));
@@ -116,14 +115,14 @@ public void incomingMySegmentsUpdate() {
     }
 
     @Test
-    public void incomingMySegmentsUpdateV2() {
+    public void incomingLargeMembershipUpdate() {
 
         IncomingNotification incomingNotification =
-                new IncomingNotification(NotificationType.MY_SEGMENTS_UPDATE_V2, "", "", 100);
-        MySegmentChangeV2Notification notification = new MySegmentChangeV2Notification();
+                new IncomingNotification(NotificationType.MEMBERSHIPS_LS_UPDATE, "", "", 100);
+        MembershipNotification notification = new MembershipNotification();
 
         when(mNotificationParser.parseIncoming(anyString())).thenReturn(incomingNotification);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(notification);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(notification);
         when(mManagerKeeper.isStreamingActive()).thenReturn(true);
 
         mSseHandler.handleIncomingMessage(buildMessage("{}"));
@@ -135,11 +134,11 @@ public void incomingMySegmentsUpdateV2() {
     public void streamingPaused() {
 
         IncomingNotification incomingNotification =
-                new IncomingNotification(NotificationType.MY_SEGMENTS_UPDATE, "", "", 100);
-        MySegmentChangeNotification notification = new MySegmentChangeNotification();
+                new IncomingNotification(NotificationType.MEMBERSHIPS_LS_UPDATE, "", "", 100);
+        MembershipNotification notification = new MembershipNotification();
 
         when(mNotificationParser.parseIncoming(anyString())).thenReturn(incomingNotification);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(notification);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(notification);
         when(mManagerKeeper.isStreamingActive()).thenReturn(false);
 
         mSseHandler.handleIncomingMessage(buildMessage("{}"));
diff --git a/src/test/java/io/split/android/client/service/sseclient/SyncManagerTest.java b/src/test/java/io/split/android/client/service/sseclient/SyncManagerTest.java
index 1fb9ff16b..df6499bd5 100644
--- a/src/test/java/io/split/android/client/service/sseclient/SyncManagerTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/SyncManagerTest.java
@@ -269,6 +269,20 @@ public void successfulSyncEventUpdatesLastSyncInGuardian() {
         verify(mSyncGuardian).updateLastSyncTimestamp();
     }
 
+    @Test
+    public void startCallsLoadMySegmentsFromCache() {
+        mSyncManager.start();
+
+        verify(mSynchronizer).loadMySegmentsFromCache();
+    }
+
+    @Test
+    public void startCallsSynchronizeMySegments() {
+        mSyncManager.start();
+
+        verify(mSynchronizer).synchronizeMySegments();
+    }
+
     private void testStartUserConsentNotGranted(UserConsent userConsent) {
         when(mConfig.userConsent()).thenReturn(userConsent);
         mSyncManager.start();
diff --git a/src/test/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoderTest.java b/src/test/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoderTest.java
deleted file mode 100644
index 73217298c..000000000
--- a/src/test/java/io/split/android/client/service/sseclient/notifications/MySegmentsPayloadDecoderTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.split.android.client.service.sseclient.notifications;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class MySegmentsPayloadDecoderTest {
-
-    private MySegmentsPayloadDecoder mMySegmentsPayloadDecoder;
-
-    @Before
-    public void setUp() {
-        mMySegmentsPayloadDecoder = new MySegmentsPayloadDecoder();
-    }
-
-    @Test
-    public void encodingOfUserKeyWorksAsExpected() {
-        String expectedResult = "MjAwNjI0Nzg3NQ==";
-
-        assertEquals(expectedResult, mMySegmentsPayloadDecoder.hashUserKeyForMySegmentsV1("user_key"));
-    }
-}
diff --git a/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImplTest.java b/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImplTest.java
index 3d239a31b..f1df86439 100644
--- a/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImplTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/MySegmentsNotificationProcessorImplTest.java
@@ -3,8 +3,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -18,28 +21,28 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 
 import io.split.android.client.common.CompressionUtilProvider;
 import io.split.android.client.exceptions.MySegmentsParsingException;
 import io.split.android.client.service.executor.SplitTaskExecutor;
-import io.split.android.client.service.mysegments.MySegmentsOverwriteTask;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.mysegments.MySegmentsSyncTask;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 import io.split.android.client.service.mysegments.MySegmentsUpdateTask;
+import io.split.android.client.service.sseclient.notifications.HashingAlgorithm;
 import io.split.android.client.service.sseclient.notifications.KeyList;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeNotification;
-import io.split.android.client.service.sseclient.notifications.MySegmentChangeV2Notification;
+import io.split.android.client.service.sseclient.notifications.MembershipNotification;
 import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
 import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
 import io.split.android.client.service.sseclient.notifications.NotificationParser;
 import io.split.android.client.service.sseclient.notifications.NotificationType;
+import io.split.android.client.service.sseclient.notifications.memberships.MembershipsNotificationProcessorImpl;
 import io.split.android.client.utils.CompressionUtil;
 
 public class MySegmentsNotificationProcessorImplTest {
@@ -58,15 +61,17 @@ public class MySegmentsNotificationProcessorImplTest {
     private CompressionUtil mCompressionUtil;
     private final BigInteger mHashedUserKey = new BigInteger("11288179738259047283");
     @Mock
-    private BlockingQueue<MySegmentChangeNotification> mMySegmentChangeQueue;
+    private BlockingQueue<MySegmentUpdateParams> mMySegmentChangeQueue;
     @Mock
-    private MySegmentChangeNotification mIncomingNotification;
+    private MembershipNotification mMsMembershipNotification;
     @Mock
-    private MySegmentChangeV2Notification mIncomingNotificationV2;
+    private MembershipNotification mLsMembershipNotification;
     @Mock
     private MySegmentsNotificationProcessorConfiguration mConfiguration;
+    @Mock
+    private SyncDelayCalculator mSyncCalculator;
 
-    private MySegmentsNotificationProcessorImpl mNotificationProcessor;
+    private MembershipsNotificationProcessorImpl mNotificationProcessor;
 
     @Before
     public void setUp() {
@@ -74,85 +79,36 @@ public void setUp() {
 
         when(mCompressionUtilProvider.get(any())).thenReturn(mCompressionUtil);
         when(mMySegmentsPayloadDecoder.hashKey(anyString())).thenReturn(mHashedUserKey);
-        when(mIncomingNotification.getJsonData()).thenReturn("{}");
-        when(mIncomingNotificationV2.getJsonData()).thenReturn("{}");
-        when(mSplitTaskFactory.createMySegmentsUpdateTask(anyBoolean(), anyString()))
-                .thenReturn(Mockito.mock(MySegmentsUpdateTask.class));
-        when(mSplitTaskFactory.createMySegmentsOverwriteTask(any()))
-                .thenReturn(Mockito.mock(MySegmentsOverwriteTask.class));
-        when(mSplitTaskFactory.createMySegmentsSyncTask(anyBoolean()))
-                .thenReturn(Mockito.mock(MySegmentsSyncTask.class));
+        when(mMsMembershipNotification.getJsonData()).thenReturn("{}");
+        when(mLsMembershipNotification.getJsonData()).thenReturn("{}");
+        when(mSplitTaskFactory.createMySegmentsUpdateTask(anyBoolean(), anySet(), anyLong()))
+                .thenReturn(mock(MySegmentsUpdateTask.class));
+        when(mSplitTaskFactory.createMySegmentsSyncTask(anyBoolean(), anyLong(), anyLong()))
+                .thenReturn(mock(MySegmentsSyncTask.class));
+        when(mConfiguration.getUserKey()).thenReturn("key");
         when(mConfiguration.getHashedUserKey()).thenReturn(mHashedUserKey);
         when(mConfiguration.getMySegmentsTaskFactory()).thenReturn(mSplitTaskFactory);
-        when(mConfiguration.getMySegmentUpdateNotificationsQueue()).thenReturn(mMySegmentChangeQueue);
-        mNotificationProcessor = new MySegmentsNotificationProcessorImpl(
+        when(mConfiguration.getNotificationsQueue()).thenReturn(mMySegmentChangeQueue);
+        mNotificationProcessor = new MembershipsNotificationProcessorImpl(
                 mNotificationParser,
                 mSplitTaskExecutor,
                 mMySegmentsPayloadDecoder,
                 mCompressionUtilProvider,
-                mConfiguration
+                mConfiguration,
+                mSyncCalculator
         );
     }
 
-    @Test
-    public void mySegmentsUpdateWithSegmentListNotification() {
-        List<String> segments = new ArrayList<>();
-        segments.add("s1");
-        when(mIncomingNotification.isIncludesPayload()).thenReturn(true);
-        when(mIncomingNotification.getSegmentList()).thenReturn(segments);
-        when(mIncomingNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(mIncomingNotification);
-
-        mNotificationProcessor.processMySegmentsUpdate(mIncomingNotification);
-
-        verify(mSplitTaskFactory, times(1)).createMySegmentsOverwriteTask(any());
-        verify(mSplitTaskExecutor, times(1)).submit(any(), isNull());
-    }
-
-    @Test
-    public void mySegmentsUpdateWithNullSegmentListNotification() {
-        List<String> segments = null;
-        when(mIncomingNotification.isIncludesPayload()).thenReturn(true);
-        when(mIncomingNotification.getSegmentList()).thenReturn(segments);
-        when(mIncomingNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(mIncomingNotification);
-
-        mNotificationProcessor.processMySegmentsUpdate(mIncomingNotification);
-
-        verify(mSplitTaskFactory, times(1)).createMySegmentsOverwriteTask(any());
-        verify(mSplitTaskExecutor, times(1)).submit(any(), isNull());
-    }
-
-    @Test
-    public void mySegmentsUpdateNoSegmentListNotification() {
-
-        MySegmentChangeNotification mySegmentChangeNotification
-                = Mockito.mock(MySegmentChangeNotification.class);
-        when(mySegmentChangeNotification.isIncludesPayload()).thenReturn(false);
-        when(mIncomingNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE);
-        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE);
-        when(mNotificationParser.parseMySegmentUpdate(anyString())).thenReturn(mySegmentChangeNotification);
-
-        mNotificationProcessor.processMySegmentsUpdate(mIncomingNotification);
-
-        verify(mSplitTaskFactory, never()).createMySegmentsOverwriteTask(any());
-        ArgumentCaptor<MySegmentChangeNotification> messageCaptor =
-                ArgumentCaptor.forClass(MySegmentChangeNotification.class);
-        verify(mMySegmentChangeQueue, times(1)).offer(messageCaptor.capture());
-        Assert.assertEquals(NotificationType.MY_SEGMENTS_UPDATE, messageCaptor.getValue().getType());
-    }
-
     @Test
     public void mySegmentsUpdateV2UnboundedNotification() {
 
-        MySegmentChangeV2Notification mySegmentChangeNotification
-                = Mockito.mock(MySegmentChangeV2Notification.class);
+        MembershipNotification mySegmentChangeNotification
+                = mock(MembershipNotification.class);
         when(mySegmentChangeNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST);
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(mySegmentChangeNotification);
+        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(mySegmentChangeNotification);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mySegmentChangeNotification);
 
         verify(mMySegmentChangeQueue, times(1)).offer(any());
     }
@@ -161,19 +117,19 @@ public void mySegmentsUpdateV2UnboundedNotification() {
     public void mySegmentsUpdateV2RemovalNotification() {
 
         String segmentName = "ToRemove";
-        when(mIncomingNotificationV2.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.SEGMENT_REMOVAL);
-        when(mIncomingNotificationV2.getSegmentName()).thenReturn(segmentName);
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(mIncomingNotificationV2);
+        when(mMsMembershipNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.SEGMENT_REMOVAL);
+        when(mMsMembershipNotification.getNames()).thenReturn(Collections.singleton(segmentName));
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mMsMembershipNotification.getChangeNumber()).thenReturn(25L);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(mMsMembershipNotification);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mMsMembershipNotification);
 
-        ArgumentCaptor<String> messageCaptor =
-                ArgumentCaptor.forClass(String.class);
-        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(anyBoolean(), messageCaptor.capture());
+        ArgumentCaptor<Set> messageCaptor =
+                ArgumentCaptor.forClass(Set.class);
+        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(anyBoolean(), messageCaptor.capture(), eq(25L));
 
-        Assert.assertEquals(segmentName, messageCaptor.getValue());
+        Assert.assertEquals(Collections.singleton(segmentName), messageCaptor.getValue());
     }
 
     @Test
@@ -185,26 +141,26 @@ public void mySegmentsUpdateV2BoundedNotificationFetch() {
     @Test
     public void mySegmentsUpdateV2BoundedNotificationNoFetch() {
         mySegmentsUpdateV2BoundedNotification(false);
-        verify(mSplitTaskFactory, never()).createMySegmentsSyncTask(anyBoolean());
+        verify(mSplitTaskFactory, never()).createMySegmentsSyncTask(anyBoolean(), anyLong(), anyLong());
     }
 
     public void mySegmentsUpdateV2BoundedNotification(boolean hasToFetch) {
 
-        MySegmentChangeV2Notification mySegmentChangeNotification
-                = Mockito.mock(MySegmentChangeV2Notification.class);
+        MembershipNotification mySegmentChangeNotification
+                = mock(MembershipNotification.class);
         when(mySegmentChangeNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST);
         when(mySegmentChangeNotification.getData()).thenReturn("dummy");
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
         try {
             when(mMySegmentsPayloadDecoder.decodeAsBytes(anyString(), any())).thenReturn(new byte[]{});
         } catch (MySegmentsParsingException e) {
         }
         when(mMySegmentsPayloadDecoder.computeKeyIndex(any(), anyInt())).thenReturn(1);
         when(mMySegmentsPayloadDecoder.isKeyInBitmap(any(), anyInt())).thenReturn(hasToFetch);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(mySegmentChangeNotification);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(mySegmentChangeNotification);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mySegmentChangeNotification);
 
     }
 
@@ -212,34 +168,34 @@ public void mySegmentsUpdateV2BoundedNotification(boolean hasToFetch) {
     public void mySegmentsUpdateV2KeyListNotificationAdd() {
         String segment = "TheSegment";
         mySegmentsUpdateV2KeyListNotification(segment, ADD);
-        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(true, segment);
+        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(true, Collections.singleton(segment), 123456L);
     }
 
     @Test
     public void mySegmentsUpdateV2KeyListNotificationRemove() {
         String segment = "TheSegment";
         mySegmentsUpdateV2KeyListNotification(segment, REMOVE);
-        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(false, segment);
+        verify(mSplitTaskFactory, times(1)).createMySegmentsUpdateTask(false, Collections.singleton(segment), 123456L);
     }
 
     @Test
     public void mySegmentsUpdateV2KeyListNotificationNone() {
         mySegmentsUpdateV2KeyListNotification("", NONE);
-        verify(mSplitTaskFactory, never()).createMySegmentsUpdateTask(anyBoolean(), anyString());
+        verify(mSplitTaskFactory, never()).createMySegmentsUpdateTask(anyBoolean(), anySet(), anyLong());
     }
 
     @Test
     public void mySegmentsUpdateV2KeyListNotificationErrorFallback() throws MySegmentsParsingException {
 
-        MySegmentChangeV2Notification mySegmentChangeNotification
-                = Mockito.mock(MySegmentChangeV2Notification.class);
+        MembershipNotification mySegmentChangeNotification
+                = mock(MembershipNotification.class);
         when(mySegmentChangeNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.KEY_LIST);
-        when(mySegmentChangeNotification.getSegmentName()).thenReturn("s1");
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
+        when(mySegmentChangeNotification.getNames()).thenReturn(Collections.singleton("s1"));
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
         when(mMySegmentsPayloadDecoder.decodeAsString(anyString(), any())).thenThrow(MySegmentsParsingException.class);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mMsMembershipNotification);
 
         verify(mMySegmentsPayloadDecoder, never()).getKeyListAction(any(), any());
         verify(mMySegmentChangeQueue, times(1)).offer(any());
@@ -248,34 +204,49 @@ public void mySegmentsUpdateV2KeyListNotificationErrorFallback() throws MySegmen
     @Test
     public void mySegmentsUpdateV2BoundedNotificationErrorFallback() throws MySegmentsParsingException {
 
-        MySegmentChangeV2Notification mySegmentChangeNotification
-                = Mockito.mock(MySegmentChangeV2Notification.class);
+        MembershipNotification mySegmentChangeNotification
+                = mock(MembershipNotification.class);
         when(mySegmentChangeNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST);
-        when(mySegmentChangeNotification.getSegmentName()).thenReturn("s1");
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
+        when(mySegmentChangeNotification.getNames()).thenReturn(Collections.singleton("s1"));
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mySegmentChangeNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
         when(mMySegmentsPayloadDecoder.decodeAsBytes(anyString(), any())).thenThrow(MySegmentsParsingException.class);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mMsMembershipNotification);
 
         verify(mMySegmentsPayloadDecoder, never()).isKeyInBitmap(any(), anyInt());
         verify(mMySegmentChangeQueue, times(1)).offer(any());
     }
 
+    @Test
+    public void delayIsCalculatedWithCalculator() {
+        MembershipNotification notification = mock(MembershipNotification.class);
+        when(notification.getUpdateIntervalMs()).thenReturn(1000L);
+        when(notification.getAlgorithmSeed()).thenReturn(1234);
+        when(notification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST);
+        when(notification.getHashingAlgorithm()).thenReturn(HashingAlgorithm.MURMUR3_32);
+        when(mSyncCalculator.calculateSyncDelay("key", 1000L, 1234, MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32)).thenReturn(25L);
+
+        mNotificationProcessor.process(notification);
+
+        verify(mSyncCalculator).calculateSyncDelay("key", 1000L, 1234, MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+    }
+
     private void mySegmentsUpdateV2KeyListNotification(String segmentName, KeyList.Action action) {
 
-        when(mIncomingNotificationV2.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.KEY_LIST);
-        when(mIncomingNotificationV2.getSegmentName()).thenReturn(segmentName);
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
-        when(mIncomingNotificationV2.getType()).thenReturn(NotificationType.MY_SEGMENTS_UPDATE_V2);
+        when(mMsMembershipNotification.getUpdateStrategy()).thenReturn(MySegmentUpdateStrategy.KEY_LIST);
+        when(mMsMembershipNotification.getNames()).thenReturn(Collections.singleton(segmentName));
+        when(mMsMembershipNotification.getChangeNumber()).thenReturn(123456L);
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
+        when(mMsMembershipNotification.getType()).thenReturn(NotificationType.MEMBERSHIPS_MS_UPDATE);
         try {
             when(mMySegmentsPayloadDecoder.decodeAsString(anyString(), any())).thenReturn("");
         } catch (MySegmentsParsingException e) {
             e.printStackTrace();
         }
         when(mMySegmentsPayloadDecoder.getKeyListAction(any(), any())).thenReturn(action);
-        when(mNotificationParser.parseMySegmentUpdateV2(anyString())).thenReturn(mIncomingNotificationV2);
+        when(mNotificationParser.parseMembershipNotification(anyString())).thenReturn(mMsMembershipNotification);
 
-        mNotificationProcessor.processMySegmentsUpdateV2(mIncomingNotificationV2);
+        mNotificationProcessor.process(mMsMembershipNotification);
     }
 }
diff --git a/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorTest.java b/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorTest.java
new file mode 100644
index 000000000..405caa988
--- /dev/null
+++ b/src/test/java/io/split/android/client/service/sseclient/notifications/mysegments/SyncDelayCalculatorTest.java
@@ -0,0 +1,81 @@
+package io.split.android.client.service.sseclient.notifications.mysegments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.Random;
+
+import io.split.android.client.service.sseclient.notifications.HashingAlgorithm;
+import io.split.android.client.service.sseclient.notifications.MySegmentUpdateStrategy;
+
+public class SyncDelayCalculatorTest {
+
+    @Test
+    public void delayIsNotLowerThan0() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 600L, 1525, MySegmentUpdateStrategy.UNBOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay >= 0);
+    }
+
+    @Test
+    public void delayIsNotHigherThanUpdateInterval() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 600L, 24515, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay <= 600L);
+    }
+
+    @Test
+    public void delayIsZeroWhenUpdateStrategyIsKeyList() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 600L, 24515, MySegmentUpdateStrategy.KEY_LIST, HashingAlgorithm.MURMUR3_32);
+        assertEquals(0, delay);
+    }
+
+    @Test
+    public void delayIsZeroWhenUpdateStrategyIsSegmentRemoval() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 600L, 24515, MySegmentUpdateStrategy.SEGMENT_REMOVAL, HashingAlgorithm.MURMUR3_32);
+        assertEquals(0, delay);
+    }
+
+    @Test
+    public void delayIsZeroWhenHashingAlgorithmIsNone() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 600L, 24515, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.NONE);
+        assertEquals(0, delay);
+    }
+
+    @Test
+    public void delayIsLessThanSixtyMsWhenUpdateIntervalIsNull() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), null, 24515, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay <= 60000);
+    }
+
+    @Test
+    public void delayIsLessThanSixtyMsWhenUpdateIntervalIsZero() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 0L, 24515, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay <= 60000);
+    }
+
+    @Test
+    public void delayIsLessThanSixtyMsWhenUpdateIntervalIsNegative() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), -1L, 24515, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay <= 60000);
+    }
+
+    @Test
+    public void delayIsCalculatedWithNullSeed() {
+        SyncDelayCalculator calculator = new SyncDelayCalculatorImpl();
+        long delay = calculator.calculateSyncDelay(getRandomString(), 1L, null, MySegmentUpdateStrategy.BOUNDED_FETCH_REQUEST, HashingAlgorithm.MURMUR3_32);
+        assertTrue(delay >= 0 && delay <= 1);
+    }
+
+    private String getRandomString() {
+        return String.valueOf(new Random().nextInt());
+    }
+}
diff --git a/src/test/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimerTest.java b/src/test/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimerTest.java
index d0d07fbaf..36f847b39 100644
--- a/src/test/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimerTest.java
+++ b/src/test/java/io/split/android/client/service/sseclient/sseclient/RetryBackoffCounterTimerTest.java
@@ -7,6 +7,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.lang.Boolean.TRUE;
+
 import androidx.annotation.NonNull;
 
 import org.junit.After;
@@ -167,6 +169,22 @@ public void nonRetryableErrorTaskNotifiesListenerWithErrorStatus() {
         counterTimer.start();
 
         verify(mockListener).taskExecuted(argThat(taskInfo -> taskInfo.getStatus() == SplitTaskExecutionStatus.ERROR &&
-                taskInfo.getTaskType() == SplitTaskType.SPLITS_SYNC));
+                taskInfo.getTaskType() == SplitTaskType.SPLITS_SYNC && TRUE.equals(taskInfo.getBoolValue("DO_NOT_RETRY"))));
+    }
+
+    @Test
+    public void testWithListenerAndInitialDelay() throws InterruptedException {
+        SplitTaskExecutionListener mockListener = mock(SplitTaskExecutionListener.class);
+        counterTimer = new RetryBackoffCounterTimer(taskExecutor, backoffCounter, 2);
+        when(taskExecutor.schedule(mockTask, 5, counterTimer)).then(invocation -> {
+            counterTimer.taskExecuted(SplitTaskExecutionInfo.error(SplitTaskType.SPLITS_SYNC, Collections.singletonMap("DO_NOT_RETRY", true)));
+            return "100";
+        });
+
+        counterTimer.setTask(mockTask, 5000L, mockListener);
+
+        counterTimer.start();
+
+        verify(taskExecutor).schedule(mockTask, 5L, counterTimer);
     }
 }
diff --git a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImplTest.java b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImplTest.java
index f2d116e96..2c87b101a 100644
--- a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImplTest.java
+++ b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerImplTest.java
@@ -23,15 +23,16 @@
 import java.util.concurrent.TimeUnit;
 
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.executor.SplitTask;
 import io.split.android.client.service.executor.SplitTaskExecutionInfo;
 import io.split.android.client.service.executor.SplitTaskExecutionListener;
 import io.split.android.client.service.executor.SplitTaskExecutor;
 import io.split.android.client.service.executor.SplitTaskType;
 import io.split.android.client.service.mysegments.LoadMySegmentsTask;
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
 import io.split.android.client.service.mysegments.MySegmentsSyncTask;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
-import io.split.android.client.service.splits.SplitsSyncTask;
 import io.split.android.client.service.sseclient.sseclient.RetryBackoffCounterTimer;
 
 public class MySegmentsSynchronizerImplTest {
@@ -54,7 +55,7 @@ public void setUp() {
                 mSplitTaskExecutor,
                 mSplitEventsManager,
                 mMySegmentsTaskFactory,
-                SEGMENTS_REFRESH_RATE);
+                SEGMENTS_REFRESH_RATE, SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE);
     }
 
     @Test
@@ -70,7 +71,7 @@ public void loadMySegmentsFromCacheSubmitsTasksToTaskExecutor() {
     @Test
     public void synchronizeMySegmentsStartsSegmentsSyncTask() {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null)).thenReturn(mockTask);
 
         mMySegmentsSynchronizer.synchronizeMySegments();
 
@@ -81,18 +82,35 @@ public void synchronizeMySegmentsStartsSegmentsSyncTask() {
     @Test
     public void forceMySegmentsSyncLaunchesSegmentsSyncTaskAvoidingCache() {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(true)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(true, null, 10L)).thenReturn(mockTask);
 
-        mMySegmentsSynchronizer.forceMySegmentsSync();
+        mMySegmentsSynchronizer.forceMySegmentsSync(new MySegmentUpdateParams(1L, null, 10L));
 
-        verify(mRetryBackoffCounterTimer).setTask(eq(mockTask), any());
+        verify(mRetryBackoffCounterTimer).setTask(eq(mockTask), eq(1L), any());
         verify(mRetryBackoffCounterTimer).start();
     }
 
+    @Test
+    public void forceMySegmentsSyncDoesNotStopPreviouslyRunningTask() {
+        MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
+        MySegmentsSyncTask mockTask2 = mock(MySegmentsSyncTask.class);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(true, null, null))
+                .thenReturn(mockTask)
+                .thenReturn(mockTask2);
+
+        mMySegmentsSynchronizer.forceMySegmentsSync(new MySegmentUpdateParams(1000L, null, null));
+        mMySegmentsSynchronizer.forceMySegmentsSync(new MySegmentUpdateParams(0L, null, null));
+
+        verify(mRetryBackoffCounterTimer).setTask(eq(mockTask), eq(1000L), any());
+        verify(mRetryBackoffCounterTimer, times(0)).setTask(eq(mockTask2), eq(0L), any());
+        verify(mRetryBackoffCounterTimer, times(1)).stop();
+        verify(mRetryBackoffCounterTimer, times(1)).start();
+    }
+
     @Test
     public void scheduleSegmentsSyncTaskSchedulesSyncTaskInTaskExecutor() {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null)).thenReturn(mockTask);
         when(mSplitTaskExecutor.schedule(eq(mockTask), eq(1L), eq(1L), notNull())).thenReturn("TaskID");
 
         mMySegmentsSynchronizer.scheduleSegmentsSyncTask();
@@ -103,7 +121,7 @@ public void scheduleSegmentsSyncTaskSchedulesSyncTaskInTaskExecutor() {
     @Test
     public void stopPeriodicFetchingCallsStopTaskOnExecutor() {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null)).thenReturn(mockTask);
         when(mSplitTaskExecutor.schedule(eq(mockTask), eq(1L), eq(1L), notNull())).thenReturn("TaskID");
 
         mMySegmentsSynchronizer.scheduleSegmentsSyncTask();
@@ -118,11 +136,11 @@ public void stopPeriodicFetchingCallsStopTaskOnExecutor() {
     public void startPeriodicFetchingCancelsPreviousTaskIfExecutedSequentially() {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
         MySegmentsSyncTask mockTask2 = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false))
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null))
                 .thenReturn(mockTask)
                 .thenReturn(mockTask2);
-        when(mSplitTaskExecutor.schedule(eq(mockTask), eq(1L), eq(1L), notNull())).thenReturn("TaskID");
-        when(mSplitTaskExecutor.schedule(eq(mockTask2), eq(1L), eq(1L), notNull())).thenReturn("TaskID2");
+        when(mSplitTaskExecutor.schedule(eq(mockTask), anyLong(), anyLong(), notNull())).thenReturn("TaskID");
+        when(mSplitTaskExecutor.schedule(eq(mockTask2), anyLong(), anyLong(), notNull())).thenReturn("TaskID2");
 
         mMySegmentsSynchronizer.scheduleSegmentsSyncTask();
         mMySegmentsSynchronizer.scheduleSegmentsSyncTask();
@@ -130,13 +148,12 @@ public void startPeriodicFetchingCancelsPreviousTaskIfExecutedSequentially() {
         verify(mSplitTaskExecutor).schedule(eq(mockTask), eq(1L), eq(1L), notNull());
         verify(mSplitTaskExecutor).stopTask("TaskID");
         verify(mSplitTaskExecutor).schedule(eq(mockTask2), eq(1L), eq(1L), notNull());
-        verify(mSplitTaskExecutor, times(0)).stopTask("TaskID2");
     }
 
     @Test
     public void syncTaskIsStoppedWhenTaskResultIsDoNotRetry() throws InterruptedException {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null)).thenReturn(mockTask);
         when(mockTask.execute()).thenReturn(SplitTaskExecutionInfo.error(
                 SplitTaskType.MY_SEGMENTS_SYNC,
                 Collections.singletonMap(SplitTaskExecutionInfo.DO_NOT_RETRY, true)));
@@ -170,7 +187,7 @@ public String answer(InvocationOnMock invocation) {
     @Test
     public void syncTaskIsNotRestartedWhenTaskResultIsDoNotRetry() throws InterruptedException {
         MySegmentsSyncTask mockTask = mock(MySegmentsSyncTask.class);
-        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false)).thenReturn(mockTask);
+        when(mMySegmentsTaskFactory.createMySegmentsSyncTask(false, null, null)).thenReturn(mockTask);
         when(mockTask.execute()).thenReturn(SplitTaskExecutionInfo.error(
                 SplitTaskType.MY_SEGMENTS_SYNC,
                 Collections.singletonMap(SplitTaskExecutionInfo.DO_NOT_RETRY, true)));
diff --git a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java
index cc82f0810..9052de36a 100644
--- a/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java
+++ b/src/test/java/io/split/android/client/service/synchronizer/mysegments/MySegmentsSynchronizerRegistryImplTest.java
@@ -6,6 +6,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import io.split.android.client.service.mysegments.MySegmentUpdateParams;
+
 public class MySegmentsSynchronizerRegistryImplTest {
 
     private MySegmentsSynchronizerRegistryImpl mRegistry;
@@ -38,11 +40,12 @@ public void synchronizeMySegmentsGetCalledInEveryRegisteredSync() {
     @Test
     public void forceMySegmentsSyncGetCalledInEveryRegisteredSync() {
         MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class);
+        MySegmentUpdateParams params = new MySegmentUpdateParams(4L, 1L, 2L);
 
         mRegistry.registerMySegmentsSynchronizer("key", syncMock);
-        mRegistry.forceMySegmentsSync();
+        mRegistry.forceMySegmentsSync(params);
 
-        verify(syncMock).forceMySegmentsSync();
+        verify(syncMock).forceMySegmentsSync(params);
     }
 
     @Test
@@ -100,35 +103,44 @@ public void unregisterStopsTasksBeforeRemovingSync() {
     public void callLoadSegmentsFromCacheForNewlyRegisteredSyncIfNecessary() {
         MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class);
         MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class);
+        MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class);
 
         mRegistry.registerMySegmentsSynchronizer("key", syncMock);
         mRegistry.loadMySegmentsFromCache();
         mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2);
+        mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3);
 
         verify(syncMock2).loadMySegmentsFromCache();
+        verify(syncMock3).loadMySegmentsFromCache();
     }
 
     @Test
     public void callSynchronizeMySegmentsForNewlyRegisteredSyncIfNecessary() {
         MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class);
         MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class);
+        MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class);
 
         mRegistry.registerMySegmentsSynchronizer("key", syncMock);
         mRegistry.synchronizeMySegments();
         mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2);
+        mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3);
 
         verify(syncMock2).synchronizeMySegments();
+        verify(syncMock3).synchronizeMySegments();
     }
 
     @Test
     public void callScheduleSegmentsSyncTaskForNewlyRegisteredSyncIfNecessary() {
         MySegmentsSynchronizer syncMock = mock(MySegmentsSynchronizer.class);
         MySegmentsSynchronizer syncMock2 = mock(MySegmentsSynchronizer.class);
+        MySegmentsSynchronizer syncMock3 = mock(MySegmentsSynchronizer.class);
 
         mRegistry.registerMySegmentsSynchronizer("key", syncMock);
         mRegistry.scheduleSegmentsSyncTask();
         mRegistry.registerMySegmentsSynchronizer("new_key", syncMock2);
+        mRegistry.registerMySegmentsSynchronizer("new_key", syncMock3);
 
         verify(syncMock2).scheduleSegmentsSyncTask();
+        verify(syncMock3).scheduleSegmentsSyncTask();
     }
 }
diff --git a/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java b/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java
index a3f6c3fd9..fac2fbd93 100644
--- a/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java
+++ b/src/test/java/io/split/android/client/shared/ClientComponentsRegisterImplTest.java
@@ -5,18 +5,23 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import androidx.annotation.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.math.BigInteger;
+
 import io.split.android.client.SplitClientConfig;
 import io.split.android.client.api.Key;
 import io.split.android.client.events.EventsManagerRegistry;
 import io.split.android.client.events.SplitEventsManager;
+import io.split.android.client.events.SplitInternalEvent;
 import io.split.android.client.service.mysegments.MySegmentsTaskFactory;
 import io.split.android.client.service.sseclient.notifications.MySegmentsV2PayloadDecoder;
-import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorFactory;
+import io.split.android.client.service.sseclient.notifications.mysegments.MembershipsNotificationProcessorFactory;
 import io.split.android.client.service.sseclient.notifications.mysegments.MySegmentsNotificationProcessorRegistry;
 import io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorkerRegistry;
 import io.split.android.client.service.sseclient.sseclient.SseAuthenticator;
@@ -48,7 +53,7 @@ public class ClientComponentsRegisterImplTest {
     @Mock
     private MySegmentsNotificationProcessorRegistry mMySegmentsNotificationProcessorRegistry;
     @Mock
-    private MySegmentsNotificationProcessorFactory mMySegmentsNotificationProcessorFactory;
+    private MembershipsNotificationProcessorFactory mMembershipsNotificationProcessorFactory;
     @Mock
     private MySegmentsV2PayloadDecoder mMySegmentsV2PayloadDecoder;
 
@@ -67,56 +72,45 @@ public class ClientComponentsRegisterImplTest {
     public void setUp() {
         MockitoAnnotations.openMocks(this);
 
-        when(mMySegmentsSynchronizerFactory.getSynchronizer(mMySegmentsTaskFactory, mSplitEventsManager))
+        when(mMySegmentsV2PayloadDecoder.hashKey("matching_key")).thenReturn(BigInteger.valueOf(123));
+
+        when(mMySegmentsSynchronizerFactory.getSynchronizer(mMySegmentsTaskFactory, mSplitEventsManager, SplitInternalEvent.MY_SEGMENTS_LOADED_FROM_STORAGE, 1800))
                 .thenReturn(mMySegmentsSynchronizer);
 
-        register = new ClientComponentsRegisterImpl(
-                new SplitClientConfig.Builder().build(),
-                mMySegmentsSynchronizerFactory,
-                mStorageContainer,
-                mAttributesSynchronizerFactory,
-                mAttributesSynchronizerRegistry,
-                mMySegmentsSynchronizerRegistry,
-                mMySegmentsUpdateWorkerRegistry,
-                mEventsManagerRegistry,
-                mSseAuthenticator,
-                mMySegmentsNotificationProcessorRegistry,
-                mMySegmentsNotificationProcessorFactory,
-                mMySegmentsV2PayloadDecoder
-        );
+        register = getRegister();
     }
 
     @Test
     public void attributesSynchronizerIsRegistered() {
-        register.registerComponents(mMatchingKey, mMySegmentsTaskFactory, mSplitEventsManager);
+        register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory);
 
         verify(mAttributesSynchronizerRegistry).registerAttributesSynchronizer(eq("matching_key"), any());
     }
 
     @Test
     public void mySegmentsSynchronizerIsRegistered() {
-        register.registerComponents(mMatchingKey, mMySegmentsTaskFactory, mSplitEventsManager);
+        register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory);
 
         verify(mMySegmentsSynchronizerRegistry).registerMySegmentsSynchronizer("matching_key", mMySegmentsSynchronizer);
     }
 
     @Test
     public void mySegmentsUpdateWorkerIsRegistered() {
-        register.registerComponents(mMatchingKey, mMySegmentsTaskFactory, mSplitEventsManager);
+        register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory);
 
         verify(mMySegmentsUpdateWorkerRegistry).registerMySegmentsUpdateWorker(eq("matching_key"), any());
     }
 
     @Test
     public void mySegmentsNotificationProcessorIsRegistered() {
-        register.registerComponents(mMatchingKey, mMySegmentsTaskFactory, mSplitEventsManager);
+        register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory);
 
-        verify(mMySegmentsNotificationProcessorRegistry).registerMySegmentsProcessor(eq("matching_key"), any());
+        verify(mMySegmentsNotificationProcessorRegistry).registerMembershipsNotificationProcessor(eq("matching_key"), any());
     }
 
     @Test
     public void eventsManagerIsRegistered() {
-        register.registerComponents(mMatchingKey, mMySegmentsTaskFactory, mSplitEventsManager);
+        register.registerComponents(mMatchingKey, mSplitEventsManager, mMySegmentsTaskFactory);
 
         verify(mEventsManagerRegistry).registerEventsManager(mMatchingKey, mSplitEventsManager);
     }
@@ -128,7 +122,25 @@ public void componentsAreCorrectlyUnregistered() {
         verify(mAttributesSynchronizerRegistry).unregisterAttributesSynchronizer("matching_key");
         verify(mMySegmentsSynchronizerRegistry).unregisterMySegmentsSynchronizer("matching_key");
         verify(mMySegmentsUpdateWorkerRegistry).unregisterMySegmentsUpdateWorker("matching_key");
-        verify(mMySegmentsNotificationProcessorRegistry).unregisterMySegmentsProcessor("matching_key");
+        verify(mMySegmentsNotificationProcessorRegistry).unregisterMembershipsProcessor("matching_key");
         verify(mEventsManagerRegistry).unregisterEventsManager(mMatchingKey);
     }
+
+    @NonNull
+    private ClientComponentsRegisterImpl getRegister() {
+        return new ClientComponentsRegisterImpl(
+                new SplitClientConfig.Builder().build(),
+                mMySegmentsSynchronizerFactory,
+                mStorageContainer,
+                mAttributesSynchronizerFactory,
+                mAttributesSynchronizerRegistry,
+                mMySegmentsSynchronizerRegistry,
+                mMySegmentsUpdateWorkerRegistry,
+                mEventsManagerRegistry,
+                mSseAuthenticator,
+                mMySegmentsNotificationProcessorRegistry,
+                mMembershipsNotificationProcessorFactory,
+                mMySegmentsV2PayloadDecoder
+        );
+    }
 }
diff --git a/src/test/java/io/split/android/client/shared/SplitClientContainerImplTest.java b/src/test/java/io/split/android/client/shared/SplitClientContainerImplTest.java
index b91c660bd..e33e50b1f 100644
--- a/src/test/java/io/split/android/client/shared/SplitClientContainerImplTest.java
+++ b/src/test/java/io/split/android/client/shared/SplitClientContainerImplTest.java
@@ -77,6 +77,7 @@ public void setUp() {
         MockitoAnnotations.openMocks(this);
         when(mSplitApiFacade.getMySegmentsFetcher(any())).thenReturn(mock(HttpFetcher.class));
         when(mStorageContainer.getMySegmentsStorage(any())).thenReturn(mock(MySegmentsStorage.class));
+        when(mStorageContainer.getMyLargeSegmentsStorage(any())).thenReturn(mock(MySegmentsStorage.class));
         mClientContainer = getSplitClientContainer(mDefaultMatchingKey, true);
     }
 
diff --git a/src/test/java/io/split/android/client/storage/cipher/ApplyCipherTaskTest.kt b/src/test/java/io/split/android/client/storage/cipher/ApplyCipherTaskTest.kt
index 33a654687..6d5f2b77c 100644
--- a/src/test/java/io/split/android/client/storage/cipher/ApplyCipherTaskTest.kt
+++ b/src/test/java/io/split/android/client/storage/cipher/ApplyCipherTaskTest.kt
@@ -3,18 +3,33 @@ package io.split.android.client.storage.cipher
 import io.split.android.client.service.executor.SplitTaskExecutionStatus
 import io.split.android.client.service.executor.SplitTaskType
 import io.split.android.client.storage.db.EventDao
+import io.split.android.client.storage.db.EventEntity
 import io.split.android.client.storage.db.ImpressionDao
+import io.split.android.client.storage.db.ImpressionEntity
 import io.split.android.client.storage.db.ImpressionsCountDao
+import io.split.android.client.storage.db.ImpressionsCountEntity
+import io.split.android.client.storage.db.MyLargeSegmentDao
+import io.split.android.client.storage.db.MyLargeSegmentEntity
 import io.split.android.client.storage.db.MySegmentDao
+import io.split.android.client.storage.db.MySegmentEntity
+import io.split.android.client.storage.db.SegmentDao
+import io.split.android.client.storage.db.SegmentEntity
 import io.split.android.client.storage.db.SplitDao
+import io.split.android.client.storage.db.SplitEntity
 import io.split.android.client.storage.db.SplitRoomDatabase
 import io.split.android.client.storage.db.attributes.AttributesDao
+import io.split.android.client.storage.db.attributes.AttributesEntity
+import io.split.android.client.storage.db.impressions.unique.UniqueKeyEntity
 import io.split.android.client.storage.db.impressions.unique.UniqueKeysDao
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
+import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.argThat
 import org.mockito.Mock
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
@@ -23,48 +38,296 @@ class ApplyCipherTaskTest {
 
     @Mock
     private lateinit var splitDatabase: SplitRoomDatabase
+
     @Mock
     private lateinit var fromCipher: SplitCipher
+
     @Mock
     private lateinit var toCipher: SplitCipher
 
     @Mock
     private lateinit var splitDao: SplitDao
+
     @Mock
     private lateinit var mySegmentDao: MySegmentDao
+
+    @Mock
+    private lateinit var myLargeSegmentDao: MyLargeSegmentDao
+
     @Mock
     private lateinit var impressionDao: ImpressionDao
+
     @Mock
     private lateinit var eventDao: EventDao
+
     @Mock
     private lateinit var impressionsCountDao: ImpressionsCountDao
+
     @Mock
     private lateinit var uniqueKeysDao: UniqueKeysDao
+
     @Mock
     private lateinit var attributesDao: AttributesDao
 
+    private lateinit var applyCipherTask: ApplyCipherTask
+
     @Before
     fun setup() {
         MockitoAnnotations.openMocks(this)
-    }
-
-    @Test
-    fun testExecute() {
         `when`(splitDatabase.splitDao()).thenReturn(splitDao)
         `when`(splitDatabase.mySegmentDao()).thenReturn(mySegmentDao)
+        `when`(splitDatabase.myLargeSegmentDao()).thenReturn(myLargeSegmentDao)
         `when`(splitDatabase.impressionDao()).thenReturn(impressionDao)
         `when`(splitDatabase.eventDao()).thenReturn(eventDao)
         `when`(splitDatabase.impressionsCountDao()).thenReturn(impressionsCountDao)
         `when`(splitDatabase.uniqueKeysDao()).thenReturn(uniqueKeysDao)
         `when`(splitDatabase.attributesDao()).thenReturn(attributesDao)
 
-        `when`(fromCipher.decrypt(anyString())).thenAnswer { invocation -> invocation.arguments[0] }
-        `when`(toCipher.encrypt(anyString())).thenAnswer { invocation -> invocation.arguments[0] }
+        `when`(fromCipher.decrypt(anyString())).thenAnswer { invocation -> "decrypted_${invocation.arguments[0]}" }
+        `when`(toCipher.encrypt(anyString())).thenAnswer { invocation -> "encrypted_${invocation.arguments[0]}" }
+        `when`(splitDatabase.runInTransaction(any())).thenAnswer { invocation -> (invocation.arguments[0] as Runnable).run() }
+
+        applyCipherTask = ApplyCipherTask(splitDatabase, fromCipher, toCipher)
+    }
 
-        val applyCipherTask = ApplyCipherTask(splitDatabase, fromCipher, toCipher)
+    @Test
+    fun testExecute() {
         val result = applyCipherTask.execute()
 
         assertEquals(SplitTaskType.GENERIC_TASK, result.taskType)
         assertEquals(SplitTaskExecutionStatus.SUCCESS, result.status)
     }
+
+    @Test
+    fun `flags are migrated`() {
+        `when`(splitDao.all).thenReturn(
+            listOf(
+                SplitEntity().apply { name = "name1"; body = "body1" },
+                SplitEntity().apply { name = "name2"; body = "body2" },
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(splitDao).all
+        verify(fromCipher).decrypt("name1")
+        verify(fromCipher).decrypt("body1")
+        verify(toCipher).encrypt("decrypted_name1")
+        verify(toCipher).encrypt("decrypted_body1")
+        verify(splitDao).update("name1", "encrypted_decrypted_name1", "encrypted_decrypted_body1")
+
+        verify(fromCipher).decrypt("name2")
+        verify(fromCipher).decrypt("body2")
+        verify(toCipher).encrypt("decrypted_name2")
+        verify(toCipher).encrypt("decrypted_body2")
+        verify(splitDao).update("name2", "encrypted_decrypted_name2", "encrypted_decrypted_body2")
+    }
+
+    @Test
+    fun `segments are migrated`() {
+        `when`(mySegmentDao.all).thenReturn(
+            listOf(
+                MySegmentEntity.creator().createEntity("userKey1", "segment1,segment2", 999999),
+                MySegmentEntity.creator().createEntity("userKey2", "segment3,segment4", 999999),
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(mySegmentDao).all
+        verify(fromCipher).decrypt("userKey1")
+        verify(fromCipher).decrypt("segment1,segment2")
+        verify(toCipher).encrypt("decrypted_userKey1")
+        verify(toCipher).encrypt("decrypted_segment1,segment2")
+        verify(mySegmentDao).update(
+            "userKey1",
+            "encrypted_decrypted_userKey1",
+            "encrypted_decrypted_segment1,segment2"
+        )
+
+        verify(fromCipher).decrypt("userKey2")
+        verify(fromCipher).decrypt("segment3,segment4")
+        verify(toCipher).encrypt("decrypted_userKey2")
+        verify(toCipher).encrypt("decrypted_segment3,segment4")
+        verify(mySegmentDao).update(
+            "userKey2",
+            "encrypted_decrypted_userKey2",
+            "encrypted_decrypted_segment3,segment4"
+        )
+    }
+
+    @Test
+    fun `large segments are migrated`() {
+        `when`(myLargeSegmentDao.all).thenReturn(
+            listOf(
+                MyLargeSegmentEntity.creator()
+                    .createEntity("userKey1", "segment1,segment2", 999999),
+                MyLargeSegmentEntity.creator()
+                    .createEntity("userKey2", "segment3,segment4", 999999),
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(myLargeSegmentDao).all
+        verify(fromCipher).decrypt("userKey1")
+        verify(fromCipher).decrypt("segment1,segment2")
+        verify(toCipher).encrypt("decrypted_userKey1")
+        verify(toCipher).encrypt("decrypted_segment1,segment2")
+        verify(myLargeSegmentDao).update(
+            "userKey1",
+            "encrypted_decrypted_userKey1",
+            "encrypted_decrypted_segment1,segment2"
+        )
+
+        verify(fromCipher).decrypt("userKey2")
+        verify(fromCipher).decrypt("segment3,segment4")
+        verify(toCipher).encrypt("decrypted_userKey2")
+        verify(toCipher).encrypt("decrypted_segment3,segment4")
+        verify(myLargeSegmentDao).update(
+            "userKey2",
+            "encrypted_decrypted_userKey2",
+            "encrypted_decrypted_segment3,segment4"
+        )
+    }
+
+    @Test
+    fun `impressions are migrated`() {
+        `when`(impressionDao.all).thenReturn(
+            listOf(
+                ImpressionEntity().apply { id = 1; testName = "test1"; body = "body1" },
+                ImpressionEntity().apply { id = 2; testName = "test2"; body = "body2" },
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(impressionDao).all
+        verify(fromCipher, times(0)).decrypt("1")
+        verify(fromCipher).decrypt("test1")
+        verify(fromCipher).decrypt("body1")
+        verify(toCipher).encrypt("decrypted_test1")
+        verify(toCipher).encrypt("decrypted_body1")
+        verify(impressionDao).insert(argThat(ArgumentMatcher<ImpressionEntity> { entity ->
+            entity.id == 1L && entity.testName == "encrypted_decrypted_test1" && entity.body == "encrypted_decrypted_body1"
+        }))
+
+        verify(fromCipher, times(0)).decrypt("2")
+        verify(fromCipher).decrypt("test2")
+        verify(fromCipher).decrypt("body2")
+        verify(toCipher).encrypt("decrypted_test2")
+        verify(toCipher).encrypt("decrypted_body2")
+        verify(impressionDao).insert(argThat(ArgumentMatcher<ImpressionEntity> { entity ->
+            entity.id == 2L && entity.testName == "encrypted_decrypted_test2" && entity.body == "encrypted_decrypted_body2"
+        }))
+    }
+
+    @Test
+    fun `events are migrated`() {
+        `when`(eventDao.all).thenReturn(
+            listOf(
+                EventEntity().apply { id = 1; body = "body1"; createdAt = 999991 },
+                EventEntity().apply { id = 2; body = "body2"; createdAt = 999992 },
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(eventDao).all
+        verify(fromCipher, times(0)).decrypt("1")
+        verify(fromCipher).decrypt("body1")
+        verify(toCipher).encrypt("decrypted_body1")
+        verify(eventDao).insert(argThat(ArgumentMatcher<EventEntity> { entity ->
+            entity.id == 1.toLong() && entity.body == "encrypted_decrypted_body1" && entity.createdAt == 999991L
+        }))
+
+        verify(fromCipher, times(0)).decrypt("2")
+        verify(fromCipher).decrypt("body2")
+        verify(toCipher).encrypt("decrypted_body2")
+        verify(eventDao).insert(argThat(ArgumentMatcher<EventEntity> { entity ->
+            entity.id == 2.toLong() && entity.body == "encrypted_decrypted_body2" && entity.createdAt == 999992L
+        }))
+    }
+
+    @Test
+    fun `impressions count are migrated`() {
+        `when`(impressionsCountDao.all).thenReturn(
+            listOf(
+                ImpressionsCountEntity().apply { id = 1; body = "body1"; createdAt = 999991 },
+                ImpressionsCountEntity().apply { id = 2; body = "body2"; createdAt = 999992 },
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(impressionsCountDao).all
+        verify(fromCipher, times(0)).decrypt("1")
+        verify(fromCipher).decrypt("body1")
+        verify(toCipher).encrypt("decrypted_body1")
+        verify(impressionsCountDao).insert(argThat(ArgumentMatcher<ImpressionsCountEntity> { entity ->
+            entity.id == 1.toLong() && entity.body == "encrypted_decrypted_body1" && entity.createdAt == 999991L
+        }))
+
+        verify(fromCipher, times(0)).decrypt("2")
+        verify(fromCipher).decrypt("body2")
+        verify(toCipher).encrypt("decrypted_body2")
+        verify(impressionsCountDao).insert(argThat(ArgumentMatcher<ImpressionsCountEntity> { entity ->
+            entity.id == 2.toLong() && entity.body == "encrypted_decrypted_body2" && entity.createdAt == 999992L
+        }))
+    }
+
+    @Test
+    fun `unique keys are migrated`() {
+        `when`(uniqueKeysDao.all).thenReturn(
+            listOf(
+                UniqueKeyEntity().apply { id = 1; userKey = "key1"; featureList = "feature1,feature2"; createdAt = 999991 },
+                UniqueKeyEntity().apply { id = 2; userKey = "key2"; featureList = "feature3,feature4"; createdAt = 999992 },
+            )
+        )
+
+        applyCipherTask.execute()
+
+        verify(uniqueKeysDao).all
+        verify(fromCipher, times(0)).decrypt("1")
+        verify(fromCipher).decrypt("key1")
+        verify(fromCipher).decrypt("feature1,feature2")
+        verify(toCipher).encrypt("decrypted_key1")
+        verify(toCipher).encrypt("decrypted_feature1,feature2")
+        verify(uniqueKeysDao).insert(argThat(ArgumentMatcher<UniqueKeyEntity> { entity ->
+            entity.id == 1.toLong() && entity.userKey == "encrypted_decrypted_key1" && entity.featureList == "encrypted_decrypted_feature1,feature2" && entity.createdAt == 999991L
+        }))
+
+        verify(fromCipher, times(0)).decrypt("2")
+        verify(fromCipher).decrypt("key2")
+        verify(fromCipher).decrypt("feature3,feature4")
+        verify(toCipher).encrypt("decrypted_key2")
+        verify(toCipher).encrypt("decrypted_feature3,feature4")
+        verify(uniqueKeysDao).insert(argThat(ArgumentMatcher<UniqueKeyEntity> { entity ->
+            entity.id == 2.toLong() && entity.userKey == "encrypted_decrypted_key2" && entity.featureList == "encrypted_decrypted_feature3,feature4" && entity.createdAt == 999992L
+        }))
+    }
+
+    @Test
+    fun `attributes are migrated`() {
+        `when`(attributesDao.all).thenReturn(
+            listOf(
+                AttributesEntity().apply { userKey = "key1"; attributes = "{\"attr1\":\"val1\",\"attr2\":\"val2\"}"; updatedAt = 999991 },
+                AttributesEntity().apply { userKey = "key2"; attributes = "{\"attr3\":\"val3\",\"attr4\":\"val4\"}"; updatedAt = 999992 },
+            ))
+
+        applyCipherTask.execute()
+
+        verify(attributesDao).all
+        verify(fromCipher).decrypt("key1")
+        verify(fromCipher).decrypt("{\"attr1\":\"val1\",\"attr2\":\"val2\"}")
+        verify(toCipher).encrypt("decrypted_key1")
+        verify(toCipher).encrypt("decrypted_{\"attr1\":\"val1\",\"attr2\":\"val2\"}")
+        verify(attributesDao).update("key1", "encrypted_decrypted_key1", "encrypted_decrypted_{\"attr1\":\"val1\",\"attr2\":\"val2\"}")
+
+        verify(fromCipher).decrypt("key2")
+        verify(fromCipher).decrypt("{\"attr3\":\"val3\",\"attr4\":\"val4\"}")
+        verify(toCipher).encrypt("decrypted_key2")
+        verify(toCipher).encrypt("decrypted_{\"attr3\":\"val3\",\"attr4\":\"val4\"}")
+        verify(attributesDao).update("key2", "encrypted_decrypted_key2", "encrypted_decrypted_{\"attr3\":\"val3\",\"attr4\":\"val4\"}")
+    }
 }
diff --git a/src/test/java/io/split/android/client/storage/mysegments/MySegmentsStorageContainerImplTest.java b/src/test/java/io/split/android/client/storage/mysegments/MySegmentsStorageContainerImplTest.java
index 521374426..d3a049f28 100644
--- a/src/test/java/io/split/android/client/storage/mysegments/MySegmentsStorageContainerImplTest.java
+++ b/src/test/java/io/split/android/client/storage/mysegments/MySegmentsStorageContainerImplTest.java
@@ -10,6 +10,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
+import java.util.HashSet;
+
+import io.split.android.client.dtos.SegmentsChange;
 
 public class MySegmentsStorageContainerImplTest {
 
@@ -55,8 +58,8 @@ public void getUniqueAmountReturnsUniqueSegmentCount() {
         MySegmentsStorage storageForKey = mContainer.getStorageForKey(userKey);
         MySegmentsStorage storageForKey2 = mContainer.getStorageForKey(userKey2);
 
-        storageForKey.set(Arrays.asList("s1", "s2"));
-        storageForKey2.set(Arrays.asList("s2", "s4", "s6"));
+        storageForKey.set(SegmentsChange.create(new HashSet<>(Arrays.asList("s1", "s2")), -1L));
+        storageForKey2.set(SegmentsChange.create(new HashSet<>(Arrays.asList("s2", "s4", "s6")), -1L));
 
         long distinctAmount = mContainer.getUniqueAmount();
 
diff --git a/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMyLargeSegmentsStorageTest.java b/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMyLargeSegmentsStorageTest.java
new file mode 100644
index 000000000..1e594ee48
--- /dev/null
+++ b/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMyLargeSegmentsStorageTest.java
@@ -0,0 +1,113 @@
+package io.split.android.client.storage.mysegments;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import io.split.android.client.dtos.SegmentsChange;
+import io.split.android.client.storage.cipher.SplitCipher;
+import io.split.android.client.storage.db.MyLargeSegmentDao;
+import io.split.android.client.storage.db.MyLargeSegmentEntity;
+import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
+
+public class SqLitePersistentMyLargeSegmentsStorageTest {
+
+    @Mock
+    private SplitRoomDatabase mDatabase;
+    @Mock
+    private SplitCipher mSplitCipher;
+    @Mock
+    private MyLargeSegmentDao mDao;
+    private SqLitePersistentMySegmentsStorage<MyLargeSegmentEntity> mStorage;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.openMocks(this);
+        when(mDatabase.myLargeSegmentDao()).thenReturn(mDao);
+        mStorage = new SqLitePersistentMySegmentsStorage<>(mSplitCipher, mDatabase.myLargeSegmentDao(), MyLargeSegmentEntity.creator());
+    }
+
+    @Test
+    public void encryptedValuesAreStoredWithDao() {
+        String userKey = "user_key";
+        Set<String> segments = new HashSet<>(Arrays.asList("segment1", "segment2", "segment3"));
+        String encryptedSegments = "encrypted_segments";
+
+        when(mSplitCipher.encrypt(anyString())).thenReturn(encryptedSegments);
+
+        mStorage.set(userKey, SegmentsChange.create(segments, 2415L));
+
+        verify(mSplitCipher).encrypt(argThat(new ArgumentMatcher<String>() {
+            @Override
+            public boolean matches(String argument) {
+                if (argument.contains("{")) {
+                    SegmentsChange segmentsChange = Json.fromJson(argument, SegmentsChange.class);
+                    return segmentsChange.getSegments().size() == 3 &&
+                            segmentsChange.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")) &&
+                            segmentsChange.getChangeNumber() == 2415;
+                } else {
+                    return false;
+                }
+            }
+        }));
+        verify(mDao).update(any(MyLargeSegmentEntity.class));
+    }
+
+    @Test
+    public void noUpdatesAreMadeWhenEncryptionResultIsNull() {
+        String userKey = "user_key";
+        Set<String> segments = new HashSet<>(Arrays.asList("segment1", "segment2", "segment3"));
+
+        when(mSplitCipher.encrypt(anyString())).thenReturn(null);
+
+        mStorage.set(userKey, SegmentsChange.create(segments, 2415L));
+
+        verify(mSplitCipher).encrypt(argThat(new ArgumentMatcher<String>() {
+            @Override
+            public boolean matches(String argument) {
+                if (argument.contains("{")) {
+                    SegmentsChange segmentsChange = Json.fromJson(argument, SegmentsChange.class);
+                    return segmentsChange.getSegments().size() == 3 &&
+                            segmentsChange.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")) &&
+                            segmentsChange.getChangeNumber() == 2415;
+                } else {
+                    return false;
+                }
+            }
+        }));
+        verifyNoInteractions(mDao);
+    }
+
+    @Test
+    public void getSnapshotReturnsDecryptedValues() {
+        String userKey = "user_key";
+        String encryptedSegments = "encrypted_segments";
+        String decryptedSegments = "segment1,segment2,segment3";
+        MyLargeSegmentEntity entity = new MyLargeSegmentEntity();
+        entity.setUserKey(userKey);
+        entity.setSegmentList(encryptedSegments);
+
+        when(mDao.getByUserKey("encrypted_user_key")).thenReturn(entity);
+        when(mSplitCipher.encrypt(userKey)).thenReturn("encrypted_user_key");
+        when(mSplitCipher.decrypt(encryptedSegments)).thenReturn(decryptedSegments);
+
+        SegmentsChange result = mStorage.getSnapshot(userKey);
+
+        assertTrue(result.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")));
+    }
+}
diff --git a/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorageTest.java b/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorageTest.java
index 0e142b650..112b5e4cc 100644
--- a/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorageTest.java
+++ b/src/test/java/io/split/android/client/storage/mysegments/SqLitePersistentMySegmentsStorageTest.java
@@ -1,24 +1,29 @@
 package io.split.android.client.storage.mysegments;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
+import io.split.android.client.dtos.SegmentsChange;
 import io.split.android.client.storage.cipher.SplitCipher;
 import io.split.android.client.storage.db.MySegmentDao;
 import io.split.android.client.storage.db.MySegmentEntity;
 import io.split.android.client.storage.db.SplitRoomDatabase;
+import io.split.android.client.utils.Json;
 
 public class SqLitePersistentMySegmentsStorageTest {
 
@@ -28,39 +33,63 @@ public class SqLitePersistentMySegmentsStorageTest {
     private SplitCipher mSplitCipher;
     @Mock
     private MySegmentDao mDao;
-    private SqLitePersistentMySegmentsStorage mStorage;
+    private SqLitePersistentMySegmentsStorage<MySegmentEntity> mStorage;
 
     @Before
     public void setup() {
         MockitoAnnotations.openMocks(this);
         when(mDatabase.mySegmentDao()).thenReturn(mDao);
-        mStorage = new SqLitePersistentMySegmentsStorage(mDatabase, mSplitCipher);
+        mStorage = new SqLitePersistentMySegmentsStorage<>(mSplitCipher, mDatabase.mySegmentDao(), MySegmentEntity.creator());
     }
 
     @Test
     public void encryptedValuesAreStoredWithDao() {
         String userKey = "user_key";
-        List<String> segments = Arrays.asList("segment1", "segment2", "segment3");
+        Set<String> segments = new HashSet<>(Arrays.asList("segment1", "segment2", "segment3"));
         String encryptedSegments = "encrypted_segments";
 
         when(mSplitCipher.encrypt(anyString())).thenReturn(encryptedSegments);
 
-        mStorage.set(userKey, segments);
-
-        verify(mSplitCipher).encrypt("segment1,segment2,segment3");
+        mStorage.set(userKey, SegmentsChange.create(segments, -1L));
+
+        verify(mSplitCipher).encrypt(argThat(new ArgumentMatcher<String>() {
+            @Override
+            public boolean matches(String argument) {
+                if (argument.contains("{")) {
+                    SegmentsChange segmentsChange = Json.fromJson(argument, SegmentsChange.class);
+                    return segmentsChange.getSegments().size() == 3 &&
+                            segmentsChange.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")) &&
+                            segmentsChange.getChangeNumber() == -1;
+                } else {
+                    return false;
+                }
+            }
+        }));
         verify(mDao).update(any(MySegmentEntity.class));
     }
 
     @Test
     public void noUpdatesAreMadeWhenEncryptionResultIsNull() {
         String userKey = "user_key";
-        List<String> segments = Arrays.asList("segment1", "segment2", "segment3");
+        Set<String> segments = new HashSet<>(Arrays.asList("segment1", "segment2", "segment3"));
 
         when(mSplitCipher.encrypt(anyString())).thenReturn(null);
 
-        mStorage.set(userKey, segments);
-
-        verify(mSplitCipher).encrypt("segment1,segment2,segment3");
+        mStorage.set(userKey, SegmentsChange.create(segments, -1L));
+
+        verify(mSplitCipher).encrypt(argThat(new ArgumentMatcher<String>() {
+            @Override
+            public boolean matches(String argument) {
+                if (argument.contains("{")) {
+                    SegmentsChange segmentsChange = Json.fromJson(argument, SegmentsChange.class);
+                    return segmentsChange.getSegments().size() == 3 &&
+                            segmentsChange.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")) &&
+                            segmentsChange.getChangeNumber() == -1;
+                } else {
+                    return false;
+                }
+            }
+        }));
         verifyNoInteractions(mDao);
     }
 
@@ -77,8 +106,8 @@ public void getSnapshotReturnsDecryptedValues() {
         when(mSplitCipher.encrypt(userKey)).thenReturn("encrypted_user_key");
         when(mSplitCipher.decrypt(encryptedSegments)).thenReturn(decryptedSegments);
 
-        List<String> result = mStorage.getSnapshot(userKey);
+        SegmentsChange result = mStorage.getSnapshot(userKey);
 
-        assertEquals(Arrays.asList("segment1", "segment2", "segment3"), result);
+        assertTrue(result.getNames().containsAll(Arrays.asList("segment1", "segment2", "segment3")));
     }
 }
diff --git a/src/test/java/io/split/android/client/telemetry/TelemetryConfigBodySerializerTest.java b/src/test/java/io/split/android/client/telemetry/TelemetryConfigBodySerializerTest.java
index f15584ed5..9c2677c38 100644
--- a/src/test/java/io/split/android/client/telemetry/TelemetryConfigBodySerializerTest.java
+++ b/src/test/java/io/split/android/client/telemetry/TelemetryConfigBodySerializerTest.java
@@ -24,7 +24,7 @@ public void setUp() {
     @Test
     public void jsonIsBuiltAsExpected() {
 
-        final String expectedJson = "{\"oM\":0,\"st\":\"memory\",\"sE\":true,\"rR\":{\"sp\":4000,\"ms\":5000,\"im\":3000,\"ev\":2000,\"te\":1000},\"uO\":{\"s\":true,\"e\":true,\"a\":true,\"st\":true,\"t\":true},\"iQ\":4000,\"eQ\":3000,\"iM\":1,\"iL\":true,\"hP\":true,\"aF\":1,\"rF\":0,\"tR\":300,\"tC\":0,\"nR\":3,\"uC\":1,\"t\":[\"tag1\",\"tag2\"],\"i\":[\"integration1\",\"integration2\"],\"fsT\":4,\"fsI\":2}";
+        final String expectedJson = "{\"oM\":0,\"st\":\"memory\",\"sE\":true,\"rR\":{\"sp\":4000,\"ms\":5000,\"mls\":6000,\"im\":3000,\"ev\":2000,\"te\":1000},\"uO\":{\"s\":true,\"e\":true,\"a\":true,\"st\":true,\"t\":true},\"iQ\":4000,\"eQ\":3000,\"iM\":1,\"iL\":true,\"hP\":true,\"aF\":1,\"rF\":0,\"tR\":300,\"tC\":0,\"nR\":3,\"uC\":1,\"t\":[\"tag1\",\"tag2\"],\"i\":[\"integration1\",\"integration2\"],\"fsT\":4,\"fsI\":2,\"lsE\":true,\"wls\":true}";
         final String serializedConfig = telemetryConfigBodySerializer.serialize(buildMockConfig());
 
         assertEquals(expectedJson, serializedConfig);
@@ -33,7 +33,7 @@ public void jsonIsBuiltAsExpected() {
     @Test
     public void nullValuesAreIgnoredForJson() {
 
-        final String expectedJson = "{\"oM\":0,\"st\":\"memory\",\"sE\":true,\"iQ\":4000,\"eQ\":3000,\"iM\":1,\"iL\":true,\"hP\":true,\"aF\":1,\"rF\":0,\"tR\":300,\"tC\":0,\"nR\":3,\"uC\":0,\"t\":[\"tag1\",\"tag2\"],\"i\":[\"integration1\",\"integration2\"],\"fsT\":0,\"fsI\":0}";
+        final String expectedJson = "{\"oM\":0,\"st\":\"memory\",\"sE\":true,\"iQ\":4000,\"eQ\":3000,\"iM\":1,\"iL\":true,\"hP\":true,\"aF\":1,\"rF\":0,\"tR\":300,\"tC\":0,\"nR\":3,\"uC\":0,\"t\":[\"tag1\",\"tag2\"],\"i\":[\"integration1\",\"integration2\"],\"fsT\":0,\"fsI\":0,\"lsE\":false,\"wls\":false}";
         final String serializedConfig = telemetryConfigBodySerializer.serialize(buildMockConfigWithNulls());
 
         assertEquals(expectedJson, serializedConfig);
@@ -48,6 +48,7 @@ private Config buildMockConfig() {
         refreshRates.setImpressions(3000);
         refreshRates.setSplits(4000);
         refreshRates.setMySegments(5000);
+        refreshRates.setMyLargeSegments(6000);
 
         UrlOverrides urlOverrides = new UrlOverrides();
         urlOverrides.setTelemetry(true);
@@ -73,6 +74,8 @@ private Config buildMockConfig() {
         config.setIntegrations(Arrays.asList("integration1", "integration2"));
         config.setFlagSetsTotal(4);
         config.setFlagSetsInvalid(2);
+        config.setLargeSegmentsEnabled(true);
+        config.setWaitForLargeSegments(true);
 
         return config;
     }
diff --git a/src/test/java/io/split/android/client/telemetry/TelemetryStatsBodySerializerTest.java b/src/test/java/io/split/android/client/telemetry/TelemetryStatsBodySerializerTest.java
index 1a6ec1cd4..2ca9d13ee 100644
--- a/src/test/java/io/split/android/client/telemetry/TelemetryStatsBodySerializerTest.java
+++ b/src/test/java/io/split/android/client/telemetry/TelemetryStatsBodySerializerTest.java
@@ -30,7 +30,7 @@ public void setUp() {
     public void jsonIsBuiltAsExpected() {
         String serializedStats = telemetryStatsBodySerializer.serialize(getMockStats());
 
-        assertEquals("{\"lS\":{\"sp\":1000,\"ms\":2000,\"im\":3000,\"ic\":4000,\"ev\":5000,\"te\":6000,\"to\":7000},\"mL\":{\"t\":[0,0,2,0],\"ts\":[0,0,3,0],\"tc\":[0,0,5,0],\"tcs\":[0,0,4,0],\"tf\":[1,0,0,0],\"tfs\":[2,0,0,0],\"tcf\":[3,0,0,0],\"tcfs\":[4,0,0,0],\"tr\":[0,0,1,0]},\"mE\":{\"t\":2,\"ts\":3,\"tc\":5,\"tcs\":4,\"tf\":10,\"tfs\":20,\"tcf\":30,\"tcfs\":40,\"tr\":1},\"hE\":{},\"hL\":{\"sp\":[0,0,3,0],\"ms\":[0,0,5,0],\"im\":[0,0,1,0],\"ic\":[0,0,4,0],\"ev\":[0,0,2,0],\"te\":[1,0,0,0],\"to\":[0,0,6,0]},\"tR\":4,\"aR\":5,\"iQ\":2,\"iDe\":5,\"iDr\":4,\"spC\":456,\"seC\":4,\"skC\":1,\"sL\":2000,\"eQ\":4,\"eD\":2,\"sE\":[{\"e\":0,\"t\":5000},{\"e\":20,\"d\":4,\"t\":2000}],\"t\":[\"tag1\",\"tag2\"],\"ufs\":{\"sp\":4,\"ms\":8}}", serializedStats);
+        assertEquals("{\"lS\":{\"sp\":1000,\"ms\":2000,\"mls\":425,\"im\":3000,\"ic\":4000,\"ev\":5000,\"te\":6000,\"to\":7000},\"mL\":{\"t\":[0,0,2,0],\"ts\":[0,0,3,0],\"tc\":[0,0,5,0],\"tcs\":[0,0,4,0],\"tf\":[1,0,0,0],\"tfs\":[2,0,0,0],\"tcf\":[3,0,0,0],\"tcfs\":[4,0,0,0],\"tr\":[0,0,1,0]},\"mE\":{\"t\":2,\"ts\":3,\"tc\":5,\"tcs\":4,\"tf\":10,\"tfs\":20,\"tcf\":30,\"tcfs\":40,\"tr\":1},\"hE\":{},\"hL\":{\"sp\":[0,0,3,0],\"ms\":[0,0,5,0],\"mls\":[0,0,6,0],\"im\":[0,0,1,0],\"ic\":[0,0,4,0],\"ev\":[0,0,2,0],\"te\":[1,0,0,0],\"to\":[0,0,6,0]},\"tR\":4,\"aR\":5,\"iQ\":2,\"iDe\":5,\"iDr\":4,\"spC\":456,\"seC\":4,\"lsC\":25,\"skC\":1,\"sL\":2000,\"eQ\":4,\"eD\":2,\"sE\":[{\"e\":0,\"t\":5000},{\"e\":20,\"d\":4,\"t\":2000}],\"t\":[\"tag1\",\"tag2\"],\"ufs\":{\"sp\":4,\"ms\":8,\"mls\":3}}", serializedStats);
     }
 
     private Stats getMockStats() {
@@ -46,6 +46,7 @@ private Stats getMockStats() {
         httpLatencies.setSplits(Arrays.asList(0L, 0L, 3L, 0L));
         httpLatencies.setImpressionsCount(Arrays.asList(0L, 0L, 4L, 0L));
         httpLatencies.setMySegments(Arrays.asList(0L, 0L, 5L, 0L));
+        httpLatencies.setMyLargeSegments(Arrays.asList(0L, 0L, 6L, 0L));
         httpLatencies.setToken(Arrays.asList(0L, 0L, 6L, 0L));
 
         MethodLatencies methodLatencies = new MethodLatencies();
@@ -80,6 +81,7 @@ private Stats getMockStats() {
         LastSync lastSynchronizations = new LastSync();
         lastSynchronizations.setLastSplitSync(1000);
         lastSynchronizations.setLastMySegmentSync(2000);
+        lastSynchronizations.setLastMyLargeSegmentSync(425);
         lastSynchronizations.setLastImpressionSync(3000);
         lastSynchronizations.setLastImpressionCountSync(4000);
         lastSynchronizations.setLastEventSync(5000);
@@ -87,6 +89,7 @@ private Stats getMockStats() {
         lastSynchronizations.setLastTokenRefresh(7000);
         stats.setLastSynchronizations(lastSynchronizations);
         stats.setSegmentCount(4);
+        stats.setLargeSegmentCount(25);
         stats.setMethodExceptions(methodExceptions);
         stats.setMethodLatencies(methodLatencies);
         stats.setSessionLengthMs(2000);
@@ -94,7 +97,7 @@ private Stats getMockStats() {
         stats.setTags(Arrays.asList("tag1", "tag2"));
         stats.setTokenRefreshes(4);
         stats.setStreamingEvents(Arrays.asList(new ConnectionEstablishedStreamingEvent(5000), new OccupancySecStreamingEvent(4, 2000)));
-        stats.setUpdatesFromSSE(new UpdatesFromSSE(4L, 8L));
+        stats.setUpdatesFromSSE(new UpdatesFromSSE(4L, 8L, 3L));
 
         return stats;
     }
diff --git a/src/test/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorageTest.java b/src/test/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorageTest.java
index 4227802af..ee6001ad3 100644
--- a/src/test/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorageTest.java
+++ b/src/test/java/io/split/android/client/telemetry/storage/InMemoryTelemetryStorageTest.java
@@ -288,6 +288,7 @@ public void lastSyncDataBuildsCorrectly() {
         telemetryStorage.recordSuccessfulSync(OperationType.MY_SEGMENT, 5000);
         telemetryStorage.recordSuccessfulSync(OperationType.SPLITS, 6000);
         telemetryStorage.recordSuccessfulSync(OperationType.TOKEN, 7000);
+        telemetryStorage.recordSuccessfulSync(OperationType.MY_LARGE_SEGMENT, 8000);
 
         LastSync lastSync = telemetryStorage.getLastSynchronization();
 
@@ -296,6 +297,7 @@ public void lastSyncDataBuildsCorrectly() {
         assertEquals(3000, lastSync.getLastImpressionSync());
         assertEquals(4000, lastSync.getLastImpressionCountSync());
         assertEquals(5000, lastSync.getLastMySegmentSync());
+        assertEquals(8000, lastSync.getLastMyLargeSegmentSync());
         assertEquals(6000, lastSync.getLastSplitSync());
         assertEquals(7000, lastSync.getLastTokenRefresh());
     }
@@ -324,6 +326,7 @@ public void popHttpErrorsBuildObjectCorrectly() {
         telemetryStorage.recordSyncError(OperationType.SPLITS, 404);
         telemetryStorage.recordSyncError(OperationType.SPLITS, 500);
         telemetryStorage.recordSyncError(OperationType.TOKEN, 401);
+        telemetryStorage.recordSyncError(OperationType.MY_LARGE_SEGMENT, 401);
 
         HttpErrors httpErrors = telemetryStorage.popHttpErrors();
 
@@ -344,6 +347,7 @@ public void popHttpErrorsBuildObjectCorrectly() {
         assertEquals(expectedEventMap, httpErrors.getImpressionSyncErrs());
         assertEquals(expectedEventMap, httpErrors.getTelemetrySyncErrs());
         assertEquals(expectedEventMap, httpErrors.getMySegmentSyncErrs());
+        assertEquals(expectedEventMap, httpErrors.getMyLargeSegmentsSyncErrs());
         assertEquals(expectedSplitSyncMap, httpErrors.getSplitSyncErrs());
         assertEquals(expectedEventMap, httpErrors.getTokenGetErrs());
     }
@@ -355,6 +359,7 @@ public void popHttpErrorsReinitializesMap() {
         telemetryStorage.recordSyncError(OperationType.IMPRESSIONS, 401);
         telemetryStorage.recordSyncError(OperationType.TELEMETRY, 401);
         telemetryStorage.recordSyncError(OperationType.MY_SEGMENT, 401);
+        telemetryStorage.recordSyncError(OperationType.MY_LARGE_SEGMENT, 401);
         telemetryStorage.recordSyncError(OperationType.SPLITS, 401);
         telemetryStorage.recordSyncError(OperationType.TOKEN, 401);
 
@@ -366,6 +371,7 @@ public void popHttpErrorsReinitializesMap() {
         assertTrue(httpErrors.getImpressionSyncErrs().isEmpty());
         assertTrue(httpErrors.getTelemetrySyncErrs().isEmpty());
         assertTrue(httpErrors.getMySegmentSyncErrs().isEmpty());
+        assertTrue(httpErrors.getMyLargeSegmentsSyncErrs().isEmpty());
         assertTrue(httpErrors.getSplitSyncErrs().isEmpty());
         assertTrue(httpErrors.getTokenGetErrs().isEmpty());
     }
@@ -380,6 +386,7 @@ public void popHttpLatenciesBuildsObjectCorrectly() {
         telemetryStorage.recordSyncLatency(OperationType.IMPRESSIONS, 200);
         telemetryStorage.recordSyncLatency(OperationType.IMPRESSIONS_COUNT, 10);
         telemetryStorage.recordSyncLatency(OperationType.MY_SEGMENT, 2000);
+        telemetryStorage.recordSyncLatency(OperationType.MY_LARGE_SEGMENT, 200);
         telemetryStorage.recordSyncLatency(OperationType.TOKEN, 2000);
 
         HttpLatencies httpLatencies = telemetryStorage.popHttpLatencies();
@@ -396,6 +403,7 @@ public void popHttpLatenciesBuildsObjectCorrectly() {
         assertEquals(1, (long) httpLatencies.getSplits().get(14));
         assertEquals(1, (long) httpLatencies.getSplits().get(22));
         assertEquals(1, (long) httpLatencies.getMySegments().get(19));
+        assertEquals(1, (long) httpLatencies.getMyLargeSegments().get(14));
         assertEquals(1, (long) httpLatencies.getImpressions().get(14));
         assertEquals(1, (long) httpLatencies.getImpressionsCount().get(6));
         assertEquals(1, (long) httpLatencies.getEvents().get(15));
diff --git a/src/test/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImplTest.java b/src/test/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImplTest.java
index 1776d6c6a..ab15ad5e0 100644
--- a/src/test/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImplTest.java
+++ b/src/test/java/io/split/android/client/telemetry/storage/TelemetryStatsProviderImplTest.java
@@ -39,12 +39,14 @@ public class TelemetryStatsProviderImplTest {
     private SplitsStorage splitsStorage;
     @Mock
     private MySegmentsStorageContainer mySegmentsStorageContainer;
+    @Mock
+    private MySegmentsStorageContainer myLargeSegmentsStorageContainer;
     private TelemetryStatsProvider telemetryStatsProvider;
 
     @Before
     public void setUp() {
         MockitoAnnotations.openMocks(this);
-        telemetryStatsProvider = new TelemetryStatsProviderImpl(telemetryStorageConsumer, splitsStorage, mySegmentsStorageContainer);
+        telemetryStatsProvider = new TelemetryStatsProviderImpl(telemetryStorageConsumer, splitsStorage, mySegmentsStorageContainer, myLargeSegmentsStorageContainer);
     }
 
     @Test
@@ -64,6 +66,7 @@ public void clearRemovesExistingStatsFromProvider() {
     public void statsAreCorrectlyBuilt() {
 
         long mySegmentsUniqueCount = 3;
+        long myLargeSegmentsUniqueCount = 152516;
         int splitsCount = 40;
 
         List<StreamingEvent> streamingEvents = Arrays.asList(
@@ -89,6 +92,7 @@ public void statsAreCorrectlyBuilt() {
         long eventsDropped = 21L;
         long sseSplits = 2L;
         long sseMySegments = 4L;
+        long sseMyLargeSegments = 5L;
 
         Map<String, Split> splits = new HashMap<>();
         for (int i = 0; i < splitsCount; i++) {
@@ -97,6 +101,7 @@ public void statsAreCorrectlyBuilt() {
 
         when(splitsStorage.getAll()).thenReturn(splits);
         when(mySegmentsStorageContainer.getUniqueAmount()).thenReturn(mySegmentsUniqueCount);
+        when(myLargeSegmentsStorageContainer.getUniqueAmount()).thenReturn(myLargeSegmentsUniqueCount);
         when(telemetryStorageConsumer.popStreamingEvents()).thenReturn(streamingEvents);
         when(telemetryStorageConsumer.popTags()).thenReturn(tags);
         when(telemetryStorageConsumer.popLatencies()).thenReturn(methodLatencies);
@@ -112,7 +117,7 @@ public void statsAreCorrectlyBuilt() {
         when(telemetryStorageConsumer.popAuthRejections()).thenReturn(authRejections);
         when(telemetryStorageConsumer.getEventsStats(EventsDataRecordsEnum.EVENTS_QUEUED)).thenReturn(eventsQueued);
         when(telemetryStorageConsumer.getEventsStats(EventsDataRecordsEnum.EVENTS_DROPPED)).thenReturn(eventsDropped);
-        when(telemetryStorageConsumer.popUpdatesFromSSE()).thenReturn(new UpdatesFromSSE(sseSplits, sseMySegments));
+        when(telemetryStorageConsumer.popUpdatesFromSSE()).thenReturn(new UpdatesFromSSE(sseSplits, sseMySegments, sseMyLargeSegments));
 
         Stats stats = telemetryStatsProvider.getTelemetryStats();
         assertEquals(streamingEvents, stats.getStreamingEvents());
@@ -120,6 +125,7 @@ public void statsAreCorrectlyBuilt() {
         assertEquals(tags, stats.getTags());
         assertEquals(methodLatencies, stats.getMethodLatencies());
         assertEquals(mySegmentsUniqueCount, stats.getSegmentCount());
+        assertEquals(myLargeSegmentsUniqueCount, stats.getLargeSegmentCount());
         assertEquals(sessionLength, stats.getSessionLengthMs());
         assertEquals(lastSync, stats.getLastSynchronizations());
         assertEquals(impDropped, stats.getImpressionsDropped());
diff --git a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java
index 63a14768d..73f254230 100644
--- a/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java
+++ b/src/test/java/io/split/android/client/utils/SplitClientImplFactory.java
@@ -34,7 +34,7 @@ public class SplitClientImplFactory {
     public static SplitClientImpl get(Key key, SplitsStorage splitsStorage) {
         SplitClientConfig cfg = SplitClientConfig.builder().build();
         SplitEventsManager eventsManager = new SplitEventsManager(cfg, new SplitTaskExecutorStub());
-        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class));
+        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class));
         TelemetryStorage telemetryStorage = mock(TelemetryStorage.class);
         TreatmentManagerFactory treatmentManagerFactory = new TreatmentManagerFactoryImpl(
                 new KeyValidatorImpl(), new SplitValidatorImpl(), new ImpressionListener.NoopImpressionListener(),
@@ -61,7 +61,7 @@ false, new AttributesMergerImpl(), telemetryStorage, splitParser,
     }
 
     public static SplitClientImpl get(Key key, ImpressionListener impressionListener) {
-        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class));
+        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class));
         SplitClientConfig cfg = SplitClientConfig.builder().build();
         return new SplitClientImpl(
                 mock(SplitFactory.class),
@@ -79,7 +79,7 @@ public static SplitClientImpl get(Key key, ImpressionListener impressionListener
     }
 
     public static SplitClientImpl get(Key key, SplitEventsManager eventsManager) {
-        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class));
+        SplitParser splitParser = new SplitParser(mock(MySegmentsStorageContainer.class), mock(MySegmentsStorageContainer.class));
         return new SplitClientImpl(
                 mock(SplitFactory.class),
                 mock(SplitClientContainer.class),
diff --git a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java
index af7ae6ef9..77d185093 100644
--- a/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java
+++ b/src/test/java/io/split/android/engine/experiments/EvaluatorTest.java
@@ -7,6 +7,8 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -32,25 +34,39 @@ public class EvaluatorTest {
     private Evaluator evaluator;
 
     @Before
-    public void loadSplitsFromFile(){
-        if(evaluator == null) {
+    public void loadSplitsFromFile() {
+        if (evaluator == null) {
             FileHelper fileHelper = new FileHelper();
             MySegmentsStorage mySegmentsStorage = mock(MySegmentsStorage.class);
+            MySegmentsStorage myLargeSegmentsStorage = mock(MySegmentsStorage.class);
             MySegmentsStorageContainer mySegmentsStorageContainer = mock(MySegmentsStorageContainer.class);
+            MySegmentsStorageContainer myLargeSegmentsStorageContainer = mock(MySegmentsStorageContainer.class);
             SplitsStorage splitsStorage = mock(SplitsStorage.class);
 
             Set<String> mySegments = new HashSet<>(Arrays.asList("s1", "s2", "test_copy"));
+            Set<String> myLargeSegments = new HashSet<>(Arrays.asList("segment1"));
             List<Split> splits = fileHelper.loadAndParseSplitChangeFile("split_changes_1.json");
-            SplitParser splitParser = new SplitParser(mySegmentsStorageContainer);
+            SplitParser splitParser = new SplitParser(mySegmentsStorageContainer, myLargeSegmentsStorageContainer);
 
             Map<String, Split> splitsMap = splitsMap(splits);
             when(splitsStorage.getAll()).thenReturn(splitsMap);
+            when(splitsStorage.get(any())).thenAnswer(new Answer<Split>() {
+                @Override
+                public Split answer(InvocationOnMock invocation) throws Throwable {
+                    return splitsMap.get(invocation.getArgument(0));
+                }
+            });
+
+
             when(splitsStorage.get("FACUNDO_TEST")).thenReturn(splitsMap.get("FACUNDO_TEST"));
             when(splitsStorage.get("a_new_split_2")).thenReturn(splitsMap.get("a_new_split_2"));
             when(splitsStorage.get("Test")).thenReturn(splitsMap.get("Test"));
+            when(splitsStorage.get("ls_split")).thenReturn(splitsMap.get("ls_split"));
 
             when(mySegmentsStorageContainer.getStorageForKey(any())).thenReturn(mySegmentsStorage);
+            when(myLargeSegmentsStorageContainer.getStorageForKey("anyKey")).thenReturn(myLargeSegmentsStorage);
             when(mySegmentsStorage.getAll()).thenReturn(mySegments);
+            when(myLargeSegmentsStorage.getAll()).thenReturn(myLargeSegments);
 
             evaluator = new EvaluatorImpl(splitsStorage, splitParser);
         }
@@ -99,6 +115,16 @@ public void testInSegmentTestKey() {
         Assert.assertEquals("whitelisted segment", result.getLabel());
     }
 
+    @Test
+    public void testInLargeSegmentKey() {
+        String matchingKey = "anyKey";
+        String splitName = "ls_split";
+        EvaluationResult result = evaluator.getTreatment(matchingKey, matchingKey, splitName, null);
+        Assert.assertNotNull(result);
+        Assert.assertEquals("on", result.getTreatment());
+        Assert.assertEquals("whitelisted segment", result.getLabel());
+    }
+
     @Test
     public void testKilledSplit() {
         String matchingKey = "anyKey";
@@ -174,7 +200,7 @@ public void changeNumberExceptionReturnsResultWithExceptionLabelAndChangeNumber(
 
     private Map<String, Split> splitsMap(List<Split> splits) {
         Map<String, Split> splitsMap = new HashMap<>();
-        for(Split split : splits) {
+        for (Split split : splits) {
             splitsMap.put(split.name, split);
         }
         return splitsMap;
diff --git a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java
index 6028439b6..e6c1d6ca6 100644
--- a/src/test/java/io/split/android/engine/experiments/SplitParserTest.java
+++ b/src/test/java/io/split/android/engine/experiments/SplitParserTest.java
@@ -4,8 +4,12 @@
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import androidx.annotation.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -27,6 +31,7 @@
 import io.split.android.client.dtos.Partition;
 import io.split.android.client.dtos.Split;
 import io.split.android.client.dtos.Status;
+import io.split.android.client.dtos.UserDefinedLargeSegmentMatcherData;
 import io.split.android.client.dtos.WhitelistMatcherData;
 import io.split.android.client.storage.mysegments.MySegmentsStorage;
 import io.split.android.client.storage.mysegments.MySegmentsStorageContainer;
@@ -56,6 +61,8 @@ public class SplitParserTest {
     MySegmentsStorage mMySegmentsStorage;
     @Mock
     MySegmentsStorageContainer mMySegmentsStorageContainer;
+    @Mock
+    MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
 
     @Before
     public void setup() {
@@ -65,7 +72,7 @@ public void setup() {
 
     @Test
     public void less_than_or_equal_to() {
-        SplitParser parser = SplitParser.get(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         Matcher ageLessThan10 = ConditionsTestUtil.numericMatcher("user", "age", MatcherType.LESS_THAN_OR_EQUAL_TO, DataType.NUMBER, 10L, false);
 
@@ -95,7 +102,7 @@ public void less_than_or_equal_to() {
     @Test
     public void equal_to() {
 
-        SplitParser parser = SplitParser.get(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         Matcher ageLessThan10 = ConditionsTestUtil.numericMatcher("user", "age", MatcherType.EQUAL_TO, DataType.NUMBER, 10L, true);
 
@@ -122,7 +129,7 @@ public void equal_to() {
     @Test
     public void equal_to_negative_number() {
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         Matcher equalToNegative10 = ConditionsTestUtil.numericMatcher("user", "age", MatcherType.EQUAL_TO, DataType.NUMBER, -10L, false);
 
@@ -146,10 +153,15 @@ public void equal_to_negative_number() {
         assertThat(actual, is(equalTo(expected)));
     }
 
+    @NonNull
+    private SplitParser createParser() {
+        return new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer);
+    }
+
     @Test
     public void between() {
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         Matcher ageBetween10And11 = ConditionsTestUtil.betweenMatcher("user",
                 "age",
@@ -327,7 +339,7 @@ public void equalToSemverParsing() {
         condition.matcherGroup.matchers = Collections.singletonList(matcher);
         Split split = makeSplit("test1", Collections.singletonList(condition));
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         ParsedSplit parsedSplit = parser.parse(split);
         assertEquals("test1", parsedSplit.feature());
@@ -354,7 +366,7 @@ public void greaterThanOrEqualToSemverParsing() {
         condition.matcherGroup.matchers = Collections.singletonList(matcher);
         Split split = makeSplit("test1", Collections.singletonList(condition));
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         ParsedSplit parsedSplit = parser.parse(split);
         assertEquals("test1", parsedSplit.feature());
@@ -381,7 +393,7 @@ public void lessThanOrEqualToSemverParsing() {
         condition.matcherGroup.matchers = Collections.singletonList(matcher);
         Split split = makeSplit("test1", Collections.singletonList(condition));
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         ParsedSplit parsedSplit = parser.parse(split);
         assertEquals("test1", parsedSplit.feature());
@@ -411,7 +423,7 @@ public void betweenSemverParsing() {
         condition.matcherGroup.matchers = Collections.singletonList(matcher);
         Split split = makeSplit("test1", Collections.singletonList(condition));
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         ParsedSplit parsedSplit = parser.parse(split);
         assertEquals("test1", parsedSplit.feature());
@@ -440,7 +452,7 @@ public void inListSemverParsing() {
         condition.matcherGroup.matchers = Collections.singletonList(matcher);
         Split split = makeSplit("test1", Collections.singletonList(condition));
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         ParsedSplit parsedSplit = parser.parse(split);
         assertEquals("test1", parsedSplit.feature());
@@ -452,9 +464,40 @@ public void inListSemverParsing() {
         assertEquals(2, parsedCondition.partitions().size());
     }
 
+    @Test
+    public void inLargeSegmentMatcherParsingTest() {
+        Condition condition = new Condition();
+        condition.conditionType = ConditionType.ROLLOUT;
+        condition.label = "new label";
+        condition.partitions = Arrays.asList(
+                ConditionsTestUtil.partition("on", 50),
+                ConditionsTestUtil.partition("0ff", 50));
+        Matcher matcher = new Matcher();
+        matcher.matcherType = MatcherType.IN_LARGE_SEGMENT;
+        UserDefinedLargeSegmentMatcherData userDefinedLargeSegmentMatcherData = new UserDefinedLargeSegmentMatcherData();
+        userDefinedLargeSegmentMatcherData.largeSegmentName = "segment1";
+        matcher.userDefinedLargeSegmentMatcherData = userDefinedLargeSegmentMatcherData;
+        condition.matcherGroup = new MatcherGroup();
+        condition.matcherGroup.matchers = Collections.singletonList(matcher);
+        Split split = makeSplit("test1", Collections.singletonList(condition));
+
+        SplitParser parser = createParser();
+
+        ParsedSplit parsedSplit = parser.parse(split, "matching_key");
+        assertEquals("test1", parsedSplit.feature());
+        assertEquals("off", parsedSplit.defaultTreatment());
+        assertEquals(1, parsedSplit.parsedConditions().size());
+        ParsedCondition parsedCondition = parsedSplit.parsedConditions().get(0);
+        assertEquals("new label", parsedCondition.label());
+        assertEquals(ConditionType.ROLLOUT, parsedCondition.conditionType());
+        assertEquals(2, parsedCondition.partitions().size());
+        verify(mMyLargeSegmentsStorageContainer).getStorageForKey("matching_key");
+        verify(mMySegmentsStorageContainer, never()).getStorageForKey("matching_key");
+    }
+
     private void set_matcher_test(Condition c, io.split.android.engine.matchers.Matcher m) {
 
-        SplitParser parser = new SplitParser(mMySegmentsStorageContainer);
+        SplitParser parser = createParser();
 
         List<Partition> partitions = Arrays.asList(ConditionsTestUtil.partition("on", 100));
 
diff --git a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java
index 4472debb3..4a972866d 100644
--- a/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java
+++ b/src/test/java/io/split/android/engine/experiments/UnsupportedMatcherSplitParserTest.java
@@ -24,6 +24,8 @@ public class UnsupportedMatcherSplitParserTest {
     @Mock
     private MySegmentsStorageContainer mMySegmentsStorageContainer;
     @Mock
+    private MySegmentsStorageContainer mMyLargeSegmentsStorageContainer;
+    @Mock
     private DefaultConditionsProvider mDefaultConditionsProvider;
     private final Split mTestFlag = Json.fromJson("{\"changeNumber\":1709843458770,\"trafficTypeName\":\"user\",\"name\":\"feature_flag_for_test\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-1364119282,\"seed\":-605938843,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"algo\":2,\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":null},\"matcherType\":\"WRONG_MATCHER_TYPE\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"123123\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100}],\"label\":\"wrong matcher type\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":\"sem\"},\"matcherType\":\"MATCHES_STRING\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"dependencyMatcherData\":null,\"booleanMatcherData\":null,\"stringMatcherData\":\"1.2.3\"}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0}],\"label\":\"sem matches 1.2.3\"}],\"configurations\":{},\"sets\":[]}", Split.class);
     private AutoCloseable mAutoCloseable;
@@ -35,7 +37,7 @@ public void setUp() {
         when(mMySegmentsStorageContainer.getStorageForKey("")).thenReturn(mMySegmentsStorage);
         when(mMySegmentsStorage.getAll()).thenReturn(Collections.emptySet());
         when(mDefaultConditionsProvider.getDefaultConditions()).thenReturn(Collections.emptyList());
-        mSplitParser = new SplitParser(mMySegmentsStorageContainer, mDefaultConditionsProvider);
+        mSplitParser = new SplitParser(mMySegmentsStorageContainer, mMyLargeSegmentsStorageContainer, mDefaultConditionsProvider);
     }
 
     @After
diff --git a/src/test/java/io/split/android/fake/SplitTaskExecutorStub.java b/src/test/java/io/split/android/fake/SplitTaskExecutorStub.java
index a197b0cee..21cacf357 100644
--- a/src/test/java/io/split/android/fake/SplitTaskExecutorStub.java
+++ b/src/test/java/io/split/android/fake/SplitTaskExecutorStub.java
@@ -58,6 +58,6 @@ public void stop() {
 
     @Override
     public void submitOnMainThread(SplitTask splitTask) {
-
+        splitTask.execute();
     }
 }
diff --git a/src/test/resources/split_changes_1.json b/src/test/resources/split_changes_1.json
index 838cfd0c2..7d72dbbcf 100644
--- a/src/test/resources/split_changes_1.json
+++ b/src/test/resources/split_changes_1.json
@@ -2514,6 +2514,52 @@
         }
       ]
     },
+    {
+      "trafficTypeName":"account",
+      "name":"ls_split",
+      "trafficAllocation":100,
+      "trafficAllocationSeed":-285565213,
+      "seed":-1992295819,
+      "status":"ACTIVE",
+      "killed":false,
+      "defaultTreatment":"off",
+      "changeNumber":1506703262916,
+      "algo":2,
+      "conditions":[
+        {
+          "conditionType":"WHITELIST",
+          "matcherGroup":{
+            "combiner":"AND",
+            "matchers":[
+              {
+                "keySelector":null,
+                "matcherType":"IN_LARGE_SEGMENT",
+                "negate":false,
+                "userDefinedSegmentMatcherData":{
+                  "segmentName":"segment1"
+                },
+                "userDefinedLargeSegmentMatcherData":{
+                  "largeSegmentName":"segment1"
+                },
+                "whitelistMatcherData":null,
+                "unaryNumericMatcherData":null,
+                "betweenMatcherData":null,
+                "booleanMatcherData":null,
+                "dependencyMatcherData":null,
+                "stringMatcherData":null
+              }
+            ]
+          },
+          "partitions":[
+            {
+              "treatment":"on",
+              "size":100
+            }
+          ],
+          "label":"whitelisted segment"
+        }
+      ]
+    },
     {
       "trafficTypeName":"user",
       "name":"broken_split",