nutritious-garden-91325
07/30/2025, 6:49 PMval trackingCallback = object : TrackingCallback {
override fun <ValueType : Any?> onTrack(
experiment: Experiment<ValueType?>,
experimentResult: ExperimentResult<ValueType?>,
) {
println("[TrackingCallback] Experiment is: $experiment")
println("[TrackingCallback] ExperimentResult is: ${experimentResult.toJson()}")
}
}
val stickyBucketService = object : StickyBucketService {
override fun getAssignments(
attributeName: String?,
attributeValue: String?,
): StickyAssignmentsDocument? {
println("[StickyBucketService] Getting assignments")
return StickyAssignmentsDocument(
"id",
"b0c7f2f4-7575-4c31-88b2-823d2c88484f",
mapOf("test__0" to "0"),
)
}
override fun saveAssignments(doc: StickyAssignmentsDocument?) {
println("[StickyBucketService] Saving assignments $doc")
}
override fun getAllAssignments(attributes: Map<String?, String?>?): Map<String?, StickyAssignmentsDocument?>? {
println("[StickyBucketService] Getting all assignments")
return null
}
}
fun main() {
val featuresRepository = GBFeaturesRepository
.builder()
.apiHost("MY_HOST")
.clientKey("MY_CLIENT_KEY")
.refreshStrategy(FeatureRefreshStrategy.STALE_WHILE_REVALIDATE)
.build()
.apply {
initialize()
}
val ctx = GBContext
.builder()
.featuresJson(featuresRepository.featuresJson)
.attributesJson(
"""
{
"id": "b0c7f2f4-7575-4c31-88b2-823d2c88484f"
}
""".trimIndent(),
)
.trackingCallback(trackingCallback)
.enabled(true)
.isQaMode(false)
.stickyBucketService(stickyBucketService)
.build()
val gb = GrowthBook(ctx)
val result = gb.getFeatureValue("test", "error")
println("Result is: $result")
}strong-mouse-55694
07/31/2025, 8:55 PMfreezing-postman-69602
07/31/2025, 10:03 PMstickyBucketService needs some help. getAllAssignments
returns null and saveAssignments doesn't do anything and getAssignments returns sort of a hard coded value regardless of the attr value or name.freezing-postman-69602
07/31/2025, 10:08 PMgetAllAssignments returns null, Growthbook thinks that there are no assigned docs, evaluates the experiment normally, assigns a variation and tries to save it for future use using saveAssignments.
if you don't require custom implementation for sticky bucketing, you could use InMemoryStickyBucketServiceImpl that relies on the local storage for sticky bucketing. Here's an example:
// 1. Create the in-memory storage
val localStorage = HashMap<String, StickyAssignmentsDocument>()
// 2. Initialize the sticky bucket service
val stickyBucketService = InMemoryStickyBucketServiceImpl(localStorage)
// 3. Set up features repository
// 4. Create GBContext with sticky bucketing
val context = GBContext
.builder()
.featuresJson(featuresRepository.featuresJson)
.attributesJson("""{"id": "b0c7f2f4-7575-4c31-88b2-823d2c88484f"}""")
.stickyBucketService(stickyBucketService)
.stickyBucketIdentifierAttributes(listOf("id")) // Key part!
.enabled(true)
.isQaMode(false)
.build()freezing-postman-69602
07/31/2025, 10:09 PMfreezing-postman-69602
07/31/2025, 10:11 PMadorable-balloon-99556
08/01/2025, 2:06 PMfreezing-postman-69602
08/01/2025, 5:59 PMstickyBucketIdentifierAttributes.
Do you mind sharing more about:
1. What does your feature "test" look like in the GrowthBook dashboard? What are the rules?
2. What's the targeting condition?
I'm happy to collab w/ you to dig more into this.adorable-balloon-99556
08/01/2025, 6:31 PMstickyBucketIdentifierAttributes api.
@nutritious-garden-91325 can you please share what Madhu is asking, please?nutritious-garden-91325
08/01/2025, 7:13 PM"features":{"test":{"defaultValue":"NOT IN EXPERIMENT","rules":[{"id":"fr_4gtibomdqbr94v","coverage":1,"hashAttribute":"id","bucketVersion":1,"seed":"3a7577e0-eb80-4a14-863f-f29db9fea0c7","hashVersion":2,"variations":["OFF","ON"],"weights":[0.99,0.01],"key":"test","meta":[{"key":"0","name":"Control"},{"key":"1","name":"Variation 1"}],"phase":"2","name":"test-2"}]}