statsig-python-core 0.20.0__tar.gz → 0.20.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/Cargo.lock +6 -6
  2. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/Cargo.toml +1 -1
  3. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/PKG-INFO +1 -1
  4. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/statsig_python_core.pyi +5 -1
  5. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/pyo_utils.rs +1 -4
  6. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_options_py.rs +6 -1
  7. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_persistent_storage_override_adapter_py.rs +2 -6
  8. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_types_py.rs +4 -0
  9. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig_options.py +38 -0
  10. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/Cargo.toml +1 -1
  11. statsig_python_core-0.20.1/statsig-rust/examples/eval_contention_bench.rs +155 -0
  12. statsig_python_core-0.20.1/statsig-rust/examples/intern_contention_bench.rs +63 -0
  13. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/__tests__/dynamic_returnable_tests.rs +14 -3
  14. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/country_lookup.rs +4 -4
  15. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/dynamic_returnable.rs +5 -0
  16. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluator.rs +55 -8
  17. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluator_context.rs +7 -0
  18. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluator_value.rs +4 -0
  19. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/ua_parser.rs +4 -4
  20. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/hashing/memo_sha_256.rs +52 -79
  21. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/instance_registry.rs +3 -3
  22. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_string.rs +5 -0
  23. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/__tests__/interned_store_eval_value_tests.rs +2 -0
  24. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/__tests__/interned_store_returnable_tests.rs +12 -1
  25. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/__tests__/interned_store_string_tests.rs +2 -0
  26. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/interned_store.rs +167 -129
  27. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/persistent_storage/persistent_values_manager.rs +246 -40
  28. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/spec_types.rs +48 -0
  29. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig.rs +173 -46
  30. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_core_api_options.rs +20 -0
  31. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_metadata.rs +1 -1
  32. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_types.rs +3 -3
  33. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/statsig_user_internal.rs +1 -4
  34. statsig_python_core-0.20.1/statsig-rust/tests/check_gate_concurrency_bench.rs +101 -0
  35. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/flushing_tests.rs +1 -0
  36. statsig_python_core-0.20.1/statsig-rust/tests/persistent_storage_enforce_tests.rs +532 -0
  37. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/__init__.py +0 -0
  38. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/data_store.py +0 -0
  39. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/error_boundary.py +0 -0
  40. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/observability_client.py +0 -0
  41. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/output_logger_provider.py +0 -0
  42. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/persistent_storage.py +0 -0
  43. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/py.typed +0 -0
  44. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/requirements.txt +0 -0
  45. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/statsig.py +0 -0
  46. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/py_src/statsig_python_core/statsig_types.py +0 -0
  47. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/pyproject.toml +0 -0
  48. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/Cargo.toml +0 -0
  49. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/README.md +0 -0
  50. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/build.rs +0 -0
  51. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/lib.rs +0 -0
  52. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/mock_forward_proxy.rs +0 -0
  53. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/protos/statsig_forward_proxy.proto +0 -0
  54. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/statsig_forward_proxy.rs +0 -0
  55. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/statsig_grpc_client.rs +0 -0
  56. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-grpc/src/statsig_grpc_err.rs +0 -0
  57. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/.github/workflows/CI.yml +0 -0
  58. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/.gitignore +0 -0
  59. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/Cargo.toml +0 -0
  60. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/README.md +0 -0
  61. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/bin/stub_gen.rs +0 -0
  62. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/data_store_base_py.rs +0 -0
  63. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/interned_store_py.rs +0 -0
  64. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/lib.rs +0 -0
  65. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/observability_client_base_py.rs +0 -0
  66. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/output_logger_provider_base_py.rs +0 -0
  67. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/raw_evaluation_compat_py.rs +0 -0
  68. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/safe_gil.rs +0 -0
  69. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_base_py.rs +0 -0
  70. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_metadata_py.rs +0 -0
  71. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/statsig_user_py.rs +0 -0
  72. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/unit_id_py.rs +0 -0
  73. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/src/valid_primitives_py.rs +0 -0
  74. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/fork_runner.py +0 -0
  75. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/mock_output_logger.py +0 -0
  76. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/mock_scrapi.py +0 -0
  77. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/profile_util.py +0 -0
  78. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/shutdown_sub_proc.py +0 -0
  79. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_data_store.py +0 -0
  80. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_error_boundary.py +0 -0
  81. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_experiment_group_targeting.py +0 -0
  82. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_experiment_serialization.py +0 -0
  83. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_exposure_logging.py +0 -0
  84. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_feature_gate_serialization.py +0 -0
  85. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_forking.py +0 -0
  86. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_get_autotune_list.py +0 -0
  87. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_get_dynamic_config.py +0 -0
  88. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_initialize_with_details.py +0 -0
  89. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_interned_store_preload.py +0 -0
  90. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_layer_param_exposure_sampling.py +0 -0
  91. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_layer_serialization.py +0 -0
  92. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_observability_client.py +0 -0
  93. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_output_logger_provider.py +0 -0
  94. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_override_adapter.py +0 -0
  95. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_performance.py +0 -0
  96. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_sec_expo_as_primary.py +0 -0
  97. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_shared_statsig.py +0 -0
  98. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_shutdown.py +0 -0
  99. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig.py +0 -0
  100. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig_user.py +0 -0
  101. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig_user_json_serialization.py +0 -0
  102. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig_user_none_fields.py +0 -0
  103. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_statsig_user_pickle.py +0 -0
  104. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/test_user_exposure_e2e.py +0 -0
  105. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-pyo3/tests/utils.py +0 -0
  106. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/build.rs +0 -0
  107. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/resources/ip_supalite.table +0 -0
  108. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/resources/ua_parser_regex.yaml +0 -0
  109. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/resources/ua_parser_regex_lite.yaml +0 -0
  110. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/__tests__/interned_string_tests.rs +0 -0
  111. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/__tests__/mod.rs +0 -0
  112. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/__tests__/spec_store_tests.rs +0 -0
  113. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/compression/compression_helper.rs +0 -0
  114. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/compression/mod.rs +0 -0
  115. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/console_capture/console_capture_handler.rs +0 -0
  116. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/console_capture/console_capture_instances.rs +0 -0
  117. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/console_capture/console_capture_options.rs +0 -0
  118. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/console_capture/console_log_line_levels.rs +0 -0
  119. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/console_capture/mod.rs +0 -0
  120. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/data_store_interface.rs +0 -0
  121. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/dcs_str.rs +0 -0
  122. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/__tests__/mod.rs +0 -0
  123. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/__tests__/rkyv_value_tests.rs +0 -0
  124. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/cmab_evaluator.rs +0 -0
  125. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_arrays.rs +0 -0
  126. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_numbers.rs +0 -0
  127. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_str_with_regex.rs +0 -0
  128. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_strings_in_array.rs +0 -0
  129. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_time.rs +0 -0
  130. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/compare_versions.rs +0 -0
  131. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/comparisons/mod.rs +0 -0
  132. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/dynamic_string.rs +0 -0
  133. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/dynamic_value.rs +0 -0
  134. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluation_details.rs +0 -0
  135. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluation_types.rs +0 -0
  136. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluation_types_initialize_v2.rs +0 -0
  137. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluation_types_v2.rs +0 -0
  138. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/evaluator_result.rs +0 -0
  139. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/get_unit_id.rs +0 -0
  140. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/mod.rs +0 -0
  141. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/rkyv_value.rs +0 -0
  142. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/secondary_exposure_key.rs +0 -0
  143. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/first_party_ua_parser.rs +0 -0
  144. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/mod.rs +0 -0
  145. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/__tests__/mod.rs +0 -0
  146. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/__tests__/test_helpers.rs +0 -0
  147. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/__tests__/test_tokenizer.rs +0 -0
  148. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/__tests__/test_ua_parser_browser.rs +0 -0
  149. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/__tests__/test_ua_parser_os.rs +0 -0
  150. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/mod.rs +0 -0
  151. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/tokenizer.rs +0 -0
  152. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/ua_parser.rs +0 -0
  153. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/statsig_uaparser/window_iter.rs +0 -0
  154. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/evaluation/user_agent_parsing/third_party_ua_parser.rs +0 -0
  155. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_emitter/statsig_event_emitter_tests.rs +0 -0
  156. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_logger.rs +0 -0
  157. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_logger_constants.rs +0 -0
  158. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_logger_ops_stats.rs +0 -0
  159. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/batch.rs +0 -0
  160. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/mod.rs +0 -0
  161. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queue.rs +0 -0
  162. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_config_expo.rs +0 -0
  163. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_event.rs +0 -0
  164. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_experiment_expo.rs +0 -0
  165. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_expo.rs +0 -0
  166. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_gate_expo.rs +0 -0
  167. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_layer_param_expo.rs +0 -0
  168. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_passthrough.rs +0 -0
  169. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/event_queue/queued_secondary_expo.rs +0 -0
  170. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/exposure_sampling.rs +0 -0
  171. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/exposure_utils.rs +0 -0
  172. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/flush_interval.rs +0 -0
  173. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/flush_type.rs +0 -0
  174. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/mod.rs +0 -0
  175. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/sec_expo_as_primary_experiment.rs +0 -0
  176. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/statsig_event.rs +0 -0
  177. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging/statsig_event_internal.rs +0 -0
  178. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging_adapter/event_logging_adapter_trait.rs +0 -0
  179. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging_adapter/log_event_payload.rs +0 -0
  180. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging_adapter/mod.rs +0 -0
  181. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging_adapter/statsig_http_event_logging_adapter.rs +0 -0
  182. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/event_logging_adapter/statsig_local_file_event_logging_adapter.rs +0 -0
  183. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/dynamic_configs_processor.rs +0 -0
  184. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/feature_gates_processor.rs +0 -0
  185. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/gcir_formatter.rs +0 -0
  186. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/gcir_options.rs +0 -0
  187. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/gcir_process_iter.rs +0 -0
  188. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/layer_configs_processor.rs +0 -0
  189. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/mod.rs +0 -0
  190. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/param_stores_processor.rs +0 -0
  191. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/stringify_sec_exposures.rs +0 -0
  192. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/gcir/target_app_id_utils.rs +0 -0
  193. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/global_configs.rs +0 -0
  194. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/hashing/ahash.rs +0 -0
  195. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/hashing/djb2.rs +0 -0
  196. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/hashing/hash_util.rs +0 -0
  197. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/hashing/mod.rs +0 -0
  198. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/id_lists_adapter/id_list.rs +0 -0
  199. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/id_lists_adapter/id_lists_adapter_trait.rs +0 -0
  200. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/id_lists_adapter/mod.rs +0 -0
  201. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/id_lists_adapter/statsig_http_id_lists_adapter.rs +0 -0
  202. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/init_details.rs +0 -0
  203. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/initialize_evaluations_response.rs +0 -0
  204. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/initialize_response.rs +0 -0
  205. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/initialize_v2_response.rs +0 -0
  206. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/__tests__/mod.rs +0 -0
  207. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/interned_values/mod.rs +0 -0
  208. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/lib.rs +0 -0
  209. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/logging_utils.rs +0 -0
  210. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/macros.rs +0 -0
  211. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/__tests__/mod.rs +0 -0
  212. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/__tests__/response_data_tests.rs +0 -0
  213. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/http_types.rs +0 -0
  214. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/mod.rs +0 -0
  215. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/network_client.rs +0 -0
  216. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/network_error.rs +0 -0
  217. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/providers/mod.rs +0 -0
  218. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/providers/net_provider_global.rs +0 -0
  219. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/providers/net_provider_noop.rs +0 -0
  220. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/providers/net_provider_reqwest.rs +0 -0
  221. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/networking/proxy_config.rs +0 -0
  222. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/console_capture_observer.rs +0 -0
  223. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/diagnostics_observer.rs +0 -0
  224. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/mod.rs +0 -0
  225. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/observability_client_adapter.rs +0 -0
  226. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/ops_stats.rs +0 -0
  227. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/observability/sdk_errors_observer.rs +0 -0
  228. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/output_logger.rs +0 -0
  229. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/override_adapter/mod.rs +0 -0
  230. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/override_adapter/override_adapter_trait.rs +0 -0
  231. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/override_adapter/statsig_local_override_adapter.rs +0 -0
  232. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/persistent_storage/__tests__/mod.rs +0 -0
  233. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/persistent_storage/__tests__/sticky_values_serialization_tests.rs +0 -0
  234. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/persistent_storage/mod.rs +0 -0
  235. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/persistent_storage/persistent_storage_trait.rs +0 -0
  236. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_diagnostics/diagnostics.rs +0 -0
  237. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_diagnostics/diagnostics_utils.rs +0 -0
  238. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_diagnostics/marker.rs +0 -0
  239. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_diagnostics/mod.rs +0 -0
  240. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_event_emitter/event_emitter.rs +0 -0
  241. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_event_emitter/event_emitter_tests.rs +0 -0
  242. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_event_emitter/event_types.rs +0 -0
  243. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/sdk_event_emitter/mod.rs +0 -0
  244. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/spec_store.rs +0 -0
  245. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/config_spec_background_sync_metrics.rs +0 -0
  246. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/mod.rs +0 -0
  247. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/response_format.rs +0 -0
  248. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/specs_adapter_trait.rs +0 -0
  249. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_bootstrap_specs_adapter.rs +0 -0
  250. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_customized_specs_adapter.rs +0 -0
  251. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_data_store_specs_adapter.rs +0 -0
  252. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_grpc_specs_adapter.rs +0 -0
  253. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_http_specs_adapter.rs +0 -0
  254. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_adapter/statsig_local_file_specs_adapter.rs +0 -0
  255. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/__tests__/mod.rs +0 -0
  256. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/__tests__/proto_specs_syncing_tests.rs +0 -0
  257. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/__tests__/proto_specs_unknown_tests.rs +0 -0
  258. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/__tests__/proto_specs_vs_json_tests.rs +0 -0
  259. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/__tests__/proto_stream_reader_tests.rs +0 -0
  260. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/cmab_types.rs +0 -0
  261. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/explicit_params.rs +0 -0
  262. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/mod.rs +0 -0
  263. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/param_store_types.rs +0 -0
  264. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/proto_specs.rs +0 -0
  265. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/proto_stream_reader.rs +0 -0
  266. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/specs_hash_map.rs +0 -0
  267. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/specs_response/statsig_config_specs.rs +0 -0
  268. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_err.rs +0 -0
  269. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_global.rs +0 -0
  270. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_options.rs +0 -0
  271. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_runtime.rs +0 -0
  272. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_type_factories.rs +0 -0
  273. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/statsig_types_raw.rs +0 -0
  274. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/into_optional.rs +0 -0
  275. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/mod.rs +0 -0
  276. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/statsig_user.rs +0 -0
  277. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/statsig_user_builder.rs +0 -0
  278. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/statsig_user_loggable.rs +0 -0
  279. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/unit_id.rs +0 -0
  280. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/user/user_data.rs +0 -0
  281. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/src/utils/mod.rs +0 -0
  282. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/check_gate_tests.rs +0 -0
  283. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/customized_specs_adapter_tests.rs +0 -0
  284. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/big_number_dcs.json +0 -0
  285. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/check_gate_perf_dcs.json +0 -0
  286. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/company_id_list +0 -0
  287. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_error.json +0 -0
  288. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_missing_gates.json +0 -0
  289. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_non_case_sensitive.json +0 -0
  290. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_user_agent_versions.json +0 -0
  291. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_with_analytical_exposure_sampling.json +0 -0
  292. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_with_checksum.json +0 -0
  293. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_with_sampling.json +0 -0
  294. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/dcs_with_sdk_configs.json +0 -0
  295. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/demo_proj_dcs.json +0 -0
  296. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/demo_proj_dcs.pb.br +0 -0
  297. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/eval_proj_dcs.json +0 -0
  298. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/eval_proj_dcs.pb.br +0 -0
  299. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/get_id_lists.json +0 -0
  300. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/perf_proj_dcs.json +0 -0
  301. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/perf_proj_dcs.pb.br +0 -0
  302. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/shared_dict_dict_only.json +0 -0
  303. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/shared_dict_original_dcs.json +0 -0
  304. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/shared_dict_response_uncompressed.json +0 -0
  305. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/shared_dict_response_with_dict.json +0 -0
  306. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/shared_dict_response_without_dict.json +0 -0
  307. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/test_must_pass_user_agents.txt +0 -0
  308. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/test_user_agents.txt +0 -0
  309. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/ua_string_cases.json +0 -0
  310. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data/unknown_enum.pb.br +0 -0
  311. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data_store_bytes_adapter_tests.rs +0 -0
  312. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data_store_json_bytes_tests.rs +0 -0
  313. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data_store_json_only_no_bytes_methods_tests.rs +0 -0
  314. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data_store_lock_tests.rs +0 -0
  315. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/data_store_proto_bytes_tests.rs +0 -0
  316. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/direct_gcir_tests.rs +0 -0
  317. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/dynamic_string_tests.rs +0 -0
  318. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/dynamic_value_tests.rs +0 -0
  319. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/evaluation_details_tests.rs +0 -0
  320. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/evaluator_value_tests.rs +0 -0
  321. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/event_logger_failing_flush_tests.rs +0 -0
  322. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/event_logger_flush_tests.rs +0 -0
  323. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/event_logger_high_qps_tests.rs +0 -0
  324. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/event_logger_usage_tests.rs +0 -0
  325. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/expected_evaluation_tests.rs +0 -0
  326. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/exposure_sampling_key_tests.rs +0 -0
  327. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/exposure_tests.rs +0 -0
  328. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/gate_inversion_eval_tests.rs +0 -0
  329. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/gcir_tests.rs +0 -0
  330. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/global_configs_test.rs +0 -0
  331. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/grpc_specs_adapter_tests.rs +0 -0
  332. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/hash_util_evaluation_hash_tests.rs +0 -0
  333. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/http_specs_adapter_tests.rs +0 -0
  334. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/initialize_timeout_test.rs +0 -0
  335. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/instance_registry_tests.rs +0 -0
  336. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/layer_serialization_tests.rs +0 -0
  337. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/list_methods_tests.rs +0 -0
  338. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/memory_leak_per_request_users_tests.rs +0 -0
  339. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/network_client_streaming_tests.rs +0 -0
  340. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/network_client_tests.rs +0 -0
  341. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/network_failure_tests.rs +0 -0
  342. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/observability_client_usage_tests.rs +0 -0
  343. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/output_logger_tests.rs +0 -0
  344. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/override_adapter_tests.rs +0 -0
  345. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/perf_tests.rs +0 -0
  346. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/proto_specs_usage_tests.rs +0 -0
  347. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/spec_store_lock_tests.rs +0 -0
  348. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/spec_types_tests.rs +0 -0
  349. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_disable_network_tests.rs +0 -0
  350. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_environment_tests.rs +0 -0
  351. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_event_emitter_usage_test.rs +0 -0
  352. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_global_tests.rs +0 -0
  353. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_local_file_event_logging_adapter_tests.rs +0 -0
  354. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_local_file_specs_adapter_tests.rs +0 -0
  355. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_options_tests.rs +0 -0
  356. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_shared_tests.rs +0 -0
  357. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_tests.rs +0 -0
  358. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_user_builder_tests.rs +0 -0
  359. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_user_field_accessor_tests.rs +0 -0
  360. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_user_internal_tests.rs +0 -0
  361. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_user_loggable_tests.rs +0 -0
  362. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/statsig_user_tests.rs +0 -0
  363. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/syncing_network_failure_tests.rs +0 -0
  364. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/typed_getters_tests.rs +0 -0
  365. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/ua_parser_util_test.rs +0 -0
  366. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/user_agent_tests_first_party.rs +0 -0
  367. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/user_json_tests.rs +0 -0
  368. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/env_var_guard.rs +0 -0
  369. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/helpers.rs +0 -0
  370. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_data_store.rs +0 -0
  371. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_event_logging_adapter.rs +0 -0
  372. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_log_provider.rs +0 -0
  373. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_observability_client.rs +0 -0
  374. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_scrapi.rs +0 -0
  375. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_specs_adapter.rs +0 -0
  376. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mock_specs_listener.rs +0 -0
  377. {statsig_python_core-0.20.0 → statsig_python_core-0.20.1}/statsig-rust/tests/utils/mod.rs +0 -0
@@ -2902,7 +2902,7 @@ dependencies = [
2902
2902
 
2903
2903
  [[package]]
2904
2904
  name = "sigstat-grpc"
2905
- version = "0.20.0"
2905
+ version = "0.20.1"
2906
2906
  dependencies = [
2907
2907
  "async-trait",
2908
2908
  "chrono",
@@ -2994,7 +2994,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
2994
2994
 
2995
2995
  [[package]]
2996
2996
  name = "statsig-node"
2997
- version = "0.20.0"
2997
+ version = "0.20.1"
2998
2998
  dependencies = [
2999
2999
  "async-trait",
3000
3000
  "napi",
@@ -3009,7 +3009,7 @@ dependencies = [
3009
3009
 
3010
3010
  [[package]]
3011
3011
  name = "statsig-pyo3"
3012
- version = "0.20.0"
3012
+ version = "0.20.1"
3013
3013
  dependencies = [
3014
3014
  "async-trait",
3015
3015
  "lazy_static",
@@ -3024,7 +3024,7 @@ dependencies = [
3024
3024
 
3025
3025
  [[package]]
3026
3026
  name = "statsig-rust"
3027
- version = "0.20.0"
3027
+ version = "0.20.1"
3028
3028
  dependencies = [
3029
3029
  "ahash",
3030
3030
  "arc-swap",
@@ -3075,7 +3075,7 @@ dependencies = [
3075
3075
 
3076
3076
  [[package]]
3077
3077
  name = "statsig_elixir"
3078
- version = "0.20.0"
3078
+ version = "0.20.1"
3079
3079
  dependencies = [
3080
3080
  "async-trait",
3081
3081
  "parking_lot",
@@ -3088,7 +3088,7 @@ dependencies = [
3088
3088
 
3089
3089
  [[package]]
3090
3090
  name = "statsig_ffi"
3091
- version = "0.20.0"
3091
+ version = "0.20.1"
3092
3092
  dependencies = [
3093
3093
  "async-trait",
3094
3094
  "cbindgen",
@@ -19,7 +19,7 @@ exclude = [
19
19
  [workspace.package]
20
20
  edition = "2021"
21
21
  license = "ISC"
22
- version = "0.20.0"
22
+ version = "0.20.1"
23
23
  homepage = "https://statsig.com/"
24
24
  authors = ["Statsig", "Daniel Loomb <daniel@statsig.com>"]
25
25
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statsig_python_core
3
- Version: 0.20.0
3
+ Version: 0.20.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -289,6 +289,10 @@ class StatsigOptions:
289
289
  @event_logging_max_pending_batch_queue_size.setter
290
290
  def event_logging_max_pending_batch_queue_size(self, value: typing.Optional[builtins.int]) -> None: ...
291
291
  @property
292
+ def exposure_dedupe_max_keys(self) -> typing.Optional[builtins.int]: ...
293
+ @exposure_dedupe_max_keys.setter
294
+ def exposure_dedupe_max_keys(self, value: typing.Optional[builtins.int]) -> None: ...
295
+ @property
292
296
  def enable_id_lists(self) -> typing.Optional[builtins.bool]: ...
293
297
  @enable_id_lists.setter
294
298
  def enable_id_lists(self, value: typing.Optional[builtins.bool]) -> None: ...
@@ -388,7 +392,7 @@ class StatsigOptions:
388
392
  def id_lists_request_timeout_ms(self) -> typing.Optional[builtins.int]: ...
389
393
  @id_lists_request_timeout_ms.setter
390
394
  def id_lists_request_timeout_ms(self, value: typing.Optional[builtins.int]) -> None: ...
391
- def __new__(cls, specs_url: typing.Optional[builtins.str] = None, specs_sync_interval_ms: typing.Optional[builtins.int] = None, init_timeout_ms: typing.Optional[builtins.int] = None, log_event_url: typing.Optional[builtins.str] = None, disable_all_logging: typing.Optional[builtins.bool] = None, disable_network: typing.Optional[builtins.bool] = None, log_event_connection_reuse: typing.Optional[builtins.bool] = None, event_logging_flush_interval_ms: typing.Optional[builtins.int] = None, event_logging_max_queue_size: typing.Optional[builtins.int] = None, event_logging_max_pending_batch_queue_size: typing.Optional[builtins.int] = None, enable_id_lists: typing.Optional[builtins.bool] = None, enable_dcs_deltas: typing.Optional[builtins.bool] = None, wait_for_user_agent_init: typing.Optional[builtins.bool] = None, wait_for_country_lookup_init: typing.Optional[builtins.bool] = None, disable_country_lookup: typing.Optional[builtins.bool] = None, id_lists_url: typing.Optional[builtins.str] = None, download_id_list_file_api: typing.Optional[builtins.str] = None, id_lists_sync_interval_ms: typing.Optional[builtins.int] = None, fallback_to_statsig_api: typing.Optional[builtins.bool] = None, environment: typing.Optional[builtins.str] = None, service_name: typing.Optional[builtins.str] = None, output_log_level: typing.Optional[builtins.str] = None, global_custom_fields: typing.Optional[typing.Mapping[builtins.str, typing.Union[builtins.str, builtins.int, builtins.float, builtins.bool, typing.List[typing.Optional[typing.Union[builtins.str, builtins.int, builtins.float, builtins.bool]]]]]] = None, observability_client: typing.Optional[ObservabilityClientBase] = None, data_store: typing.Optional[DataStoreBase] = None, data_store_key_schema_version: typing.Optional[builtins.str] = None, persistent_storage: typing.Optional[PersistentStorageBaseClass] = None, config_compression_mode: typing.Optional[builtins.str] = None, proxy_config: typing.Optional[ProxyConfig] = None, output_logger_provider: typing.Optional[OutputLoggerProviderBase] = None, spec_adapter_configs: typing.Optional[list] = None, use_third_party_ua_parser: typing.Optional[builtins.bool] = None, disable_disk_access: typing.Optional[builtins.bool] = None, experimental_flags: typing.Optional[builtins.set[builtins.str]] = None, id_lists_request_timeout_ms: typing.Optional[builtins.int] = None) -> StatsigOptions: ...
395
+ def __new__(cls, specs_url: typing.Optional[builtins.str] = None, specs_sync_interval_ms: typing.Optional[builtins.int] = None, init_timeout_ms: typing.Optional[builtins.int] = None, log_event_url: typing.Optional[builtins.str] = None, disable_all_logging: typing.Optional[builtins.bool] = None, disable_network: typing.Optional[builtins.bool] = None, log_event_connection_reuse: typing.Optional[builtins.bool] = None, event_logging_flush_interval_ms: typing.Optional[builtins.int] = None, event_logging_max_queue_size: typing.Optional[builtins.int] = None, event_logging_max_pending_batch_queue_size: typing.Optional[builtins.int] = None, enable_id_lists: typing.Optional[builtins.bool] = None, enable_dcs_deltas: typing.Optional[builtins.bool] = None, wait_for_user_agent_init: typing.Optional[builtins.bool] = None, wait_for_country_lookup_init: typing.Optional[builtins.bool] = None, disable_country_lookup: typing.Optional[builtins.bool] = None, id_lists_url: typing.Optional[builtins.str] = None, download_id_list_file_api: typing.Optional[builtins.str] = None, id_lists_sync_interval_ms: typing.Optional[builtins.int] = None, fallback_to_statsig_api: typing.Optional[builtins.bool] = None, environment: typing.Optional[builtins.str] = None, service_name: typing.Optional[builtins.str] = None, output_log_level: typing.Optional[builtins.str] = None, global_custom_fields: typing.Optional[typing.Mapping[builtins.str, typing.Union[builtins.str, builtins.int, builtins.float, builtins.bool, typing.List[typing.Optional[typing.Union[builtins.str, builtins.int, builtins.float, builtins.bool]]]]]] = None, observability_client: typing.Optional[ObservabilityClientBase] = None, data_store: typing.Optional[DataStoreBase] = None, data_store_key_schema_version: typing.Optional[builtins.str] = None, persistent_storage: typing.Optional[PersistentStorageBaseClass] = None, config_compression_mode: typing.Optional[builtins.str] = None, proxy_config: typing.Optional[ProxyConfig] = None, output_logger_provider: typing.Optional[OutputLoggerProviderBase] = None, spec_adapter_configs: typing.Optional[list] = None, use_third_party_ua_parser: typing.Optional[builtins.bool] = None, disable_disk_access: typing.Optional[builtins.bool] = None, experimental_flags: typing.Optional[builtins.set[builtins.str]] = None, id_lists_request_timeout_ms: typing.Optional[builtins.int] = None, exposure_dedupe_max_keys: typing.Optional[builtins.int] = None) -> StatsigOptions: ...
392
396
 
393
397
  @typing.final
394
398
  class StatsigUser:
@@ -65,10 +65,7 @@ pub fn map_to_py_dict(py: Python, map: &HashMap<String, Value>) -> Py<PyAny> {
65
65
  pub fn py_list_to_list(py_list: &Bound<PyList>) -> PyResult<Vec<String>> {
66
66
  let mut converted_list = Vec::new();
67
67
  for value in py_list {
68
- match value.extract::<String>() {
69
- Ok(s) => converted_list.push(s),
70
- Err(e) => return Err(e),
71
- }
68
+ converted_list.push(value.extract::<String>()?);
72
69
  }
73
70
  Ok(converted_list)
74
71
  }
@@ -180,6 +180,8 @@ pub struct StatsigOptionsPy {
180
180
  #[pyo3(get, set)]
181
181
  pub event_logging_max_pending_batch_queue_size: Option<u32>,
182
182
  #[pyo3(get, set)]
183
+ pub exposure_dedupe_max_keys: Option<u32>,
184
+ #[pyo3(get, set)]
183
185
  pub enable_id_lists: Option<bool>,
184
186
  #[pyo3(get, set)]
185
187
  pub enable_dcs_deltas: Option<bool>,
@@ -272,6 +274,7 @@ impl StatsigOptionsPy {
272
274
  disable_disk_access=None,
273
275
  experimental_flags=None,
274
276
  id_lists_request_timeout_ms=None,
277
+ exposure_dedupe_max_keys=None,
275
278
  ))]
276
279
  #[allow(clippy::too_many_arguments)]
277
280
  pub fn new(
@@ -310,6 +313,7 @@ impl StatsigOptionsPy {
310
313
  disable_disk_access: Option<bool>,
311
314
  experimental_flags: Option<HashSet<String>>,
312
315
  id_lists_request_timeout_ms: Option<u64>,
316
+ exposure_dedupe_max_keys: Option<u32>,
313
317
  ) -> Self {
314
318
  Self {
315
319
  specs_url,
@@ -321,6 +325,7 @@ impl StatsigOptionsPy {
321
325
  event_logging_flush_interval_ms,
322
326
  event_logging_max_queue_size,
323
327
  event_logging_max_pending_batch_queue_size,
328
+ exposure_dedupe_max_keys,
324
329
  enable_id_lists,
325
330
  enable_dcs_deltas,
326
331
  wait_for_user_agent_init,
@@ -410,7 +415,7 @@ fn create_inner_statsig_options(
410
415
  event_logging_flush_interval_ms: None,
411
416
  event_logging_max_queue_size: opts.event_logging_max_queue_size,
412
417
  event_logging_max_pending_batch_queue_size: opts.event_logging_max_pending_batch_queue_size,
413
- exposure_dedupe_max_keys: None,
418
+ exposure_dedupe_max_keys: opts.exposure_dedupe_max_keys,
414
419
  enable_id_lists: opts.enable_id_lists,
415
420
  enable_dcs_deltas: opts.enable_dcs_deltas,
416
421
  id_lists_url: opts.id_lists_url.clone(),
@@ -169,12 +169,8 @@ fn convert_py_lists_to_secondary_exposures(
169
169
  ) -> PyResult<Vec<SecondaryExposure>> {
170
170
  let mut exposures = Vec::new();
171
171
  for item in py_list {
172
- match item.extract::<Bound<PyDict>>() {
173
- Ok(e) => {
174
- exposures.push(convert_py_dict_to_secondary_exposure(&e)?);
175
- }
176
- Err(e) => return Err(e.into()),
177
- }
172
+ let e = item.extract::<Bound<PyDict>>()?;
173
+ exposures.push(convert_py_dict_to_secondary_exposure(&e)?);
178
174
  }
179
175
 
180
176
  Ok(exposures)
@@ -311,6 +311,8 @@ impl From<&ExperimentEvaluationOptionsPy> for ExperimentEvaluationOptions {
311
311
  disable_exposure_logging: val.disable_exposure_logging,
312
312
  // For performance consideration, conversion to user persisted values use convert_dict_to_user_persisted_values()
313
313
  user_persisted_values: None,
314
+ // enforce_overrides / enforce_targeting are wired up in a follow-up binding sub-issue.
315
+ ..Default::default()
314
316
  }
315
317
  }
316
318
  }
@@ -321,6 +323,8 @@ impl From<&LayerEvaluationOptionsPy> for LayerEvaluationOptions {
321
323
  disable_exposure_logging: val.disable_exposure_logging,
322
324
  // For performance consideration, conversion to user persisted values use convert_dict_to_user_persisted_values()
323
325
  user_persisted_values: None,
326
+ // enforce_overrides / enforce_targeting are wired up in a follow-up binding sub-issue.
327
+ ..Default::default()
324
328
  }
325
329
  }
326
330
  }
@@ -45,6 +45,44 @@ def test_full_statsig_options():
45
45
  assert options.service_name == "statsig-python-service"
46
46
 
47
47
 
48
+ def test_exposure_dedupe_max_keys_defaults_to_none():
49
+ options = StatsigOptions()
50
+ assert options.exposure_dedupe_max_keys is None
51
+
52
+
53
+ def test_exposure_dedupe_max_keys_round_trips():
54
+ # Settable via constructor and as an attribute; the getter reads the same
55
+ # field forwarded to the core StatsigOptions.
56
+ options = StatsigOptions(exposure_dedupe_max_keys=250_000)
57
+ assert options.exposure_dedupe_max_keys == 250_000
58
+
59
+ options.exposure_dedupe_max_keys = 5
60
+ assert options.exposure_dedupe_max_keys == 5
61
+
62
+
63
+ def test_exposure_dedupe_max_keys_appended_without_breaking_positional_args():
64
+ # exposure_dedupe_max_keys is appended AFTER the pre-existing constructor
65
+ # params, so existing positional calls keep binding to the same options and
66
+ # the new field defaults to None. The 11th positional arg is enable_id_lists;
67
+ # if the new param had been inserted mid-list, `True` would bind to it here.
68
+ options = StatsigOptions(
69
+ "https://cdn.statsig.com/v1", # specs_url
70
+ None, # specs_sync_interval_ms
71
+ None, # init_timeout_ms
72
+ None, # log_event_url
73
+ None, # disable_all_logging
74
+ None, # disable_network
75
+ None, # log_event_connection_reuse
76
+ None, # event_logging_flush_interval_ms
77
+ None, # event_logging_max_queue_size
78
+ None, # event_logging_max_pending_batch_queue_size
79
+ True, # enable_id_lists
80
+ )
81
+ assert options.specs_url == "https://cdn.statsig.com/v1"
82
+ assert options.enable_id_lists is True
83
+ assert options.exposure_dedupe_max_keys is None
84
+
85
+
48
86
  def _assert_no_tls(config):
49
87
  assert config.adapter_type == "network_grpc_websocket"
50
88
  assert config.specs_url == "grpc://localhost:50051"
@@ -41,7 +41,7 @@ serde_json = { version = "1.0.143", features = [
41
41
  ] }
42
42
  serde_with = "3.4.0"
43
43
  sha2 = "0.10.8"
44
- sigstat-grpc = { path = "../statsig-grpc", version = "0.20.0", optional = true }
44
+ sigstat-grpc = { path = "../statsig-grpc", version = "0.20.1", optional = true }
45
45
  simple_logger = { version = "5.0.0" }
46
46
  tempfile = "3.8.1"
47
47
  tokio = { version = "1.39.1", features = ["full"] }
@@ -0,0 +1,155 @@
1
+ // Pure-Rust eval contention bench (S2SDK-140 follow-up).
2
+ //
3
+ // Java-harness profiling of the heavy-gate workload showed ~50% of CPU time
4
+ // in futex/sched_yield - lock churn on the eval hot path - but the native
5
+ // frames had no symbols. This bench removes the JVM/JNI entirely (which also
6
+ // removes InstanceRegistry from the path) so we can (a) see if the thread-
7
+ // scaling collapse reproduces in pure Rust and (b) profile with symbols.
8
+ //
9
+ // Isolation knobs:
10
+ // BENCH_THREADS comma list of thread counts to sweep (default 1,4,16,64)
11
+ // BENCH_GATE gate to evaluate (default test_many_rules)
12
+ // BENCH_MANY_GATES >0: cycle syn_gate_NNNN names to spread the
13
+ // non_exposed_checks DashMap across shards (default 0)
14
+ // BENCH_DURATION_SEC seconds per measurement (default 10)
15
+ // BENCH_DCS_FILE specs payload (default ../grammarly-load-repro/dcs_payload_big.json)
16
+ // BENCH_EXPOSURES "1": log exposures instead of the non-exposure path
17
+ // BENCH_REUSE_USER evals per fresh user (default 40)
18
+ //
19
+ // Run: cargo run --release --example eval_contention_bench
20
+ //
21
+ // Comparing `same gate` vs `BENCH_MANY_GATES=3000` isolates single-key
22
+ // contention on non_exposed_checks; profiling with symbols (samply/sample)
23
+ // attributes the rest (spec_store RwLock vs eval compute).
24
+
25
+ use statsig_rust::{
26
+ hashing::djb2, FeatureGateEvaluationOptions, Statsig, StatsigLocalFileSpecsAdapter,
27
+ StatsigOptions, StatsigUser, StatsigUserBuilder,
28
+ };
29
+ use std::collections::HashMap;
30
+ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
31
+ use std::sync::Arc;
32
+ use std::time::{Duration, Instant};
33
+
34
+ fn env(name: &str, default: &str) -> String {
35
+ std::env::var(name).unwrap_or_else(|_| default.to_string())
36
+ }
37
+
38
+ fn make_user(idx: u64) -> StatsigUser {
39
+ StatsigUserBuilder::new_with_user_id(format!("user_{idx}"))
40
+ .email(Some(format!("u{}@example.com", idx % 1000)))
41
+ .custom_ids(Some(HashMap::from([(
42
+ "companyID".to_string(),
43
+ format!("c{}", idx % 500),
44
+ )])))
45
+ .country(Some("US".to_string()))
46
+ .locale(Some("en_US".to_string()))
47
+ .app_version(Some(format!("5.2.{}", idx % 90)))
48
+ .custom_from_str_map(Some(HashMap::from([
49
+ ("plan".to_string(), "premium".to_string()),
50
+ ("cohort".to_string(), format!("c{}", idx % 20)),
51
+ ("platform".to_string(), "mac".to_string()),
52
+ ("abGroup".to_string(), format!("g{}", idx % 8)),
53
+ ])))
54
+ .build()
55
+ }
56
+
57
+ #[tokio::main]
58
+ async fn main() {
59
+ let dcs_file = env(
60
+ "BENCH_DCS_FILE",
61
+ "../../grammarly-load-repro/dcs_payload_big.json",
62
+ );
63
+ let gate = env("BENCH_GATE", "test_many_rules");
64
+ let many_gates: u64 = env("BENCH_MANY_GATES", "0").parse().unwrap();
65
+ let duration_sec: u64 = env("BENCH_DURATION_SEC", "10").parse().unwrap();
66
+ let reuse_user: u64 = env("BENCH_REUSE_USER", "40").parse().unwrap();
67
+ let log_exposures = env("BENCH_EXPOSURES", "0") == "1";
68
+ let thread_counts: Vec<usize> = env("BENCH_THREADS", "1,4,16,64")
69
+ .split(',')
70
+ .map(|s| s.trim().parse().unwrap())
71
+ .collect();
72
+
73
+ let sdk_key = "secret-bench-key";
74
+
75
+ // StatsigLocalFileSpecsAdapter reads {dir}/{djb2(key)}_specs.json.
76
+ // Per-process dir so concurrent bench runs don't clobber each other's specs.
77
+ let dir = std::env::temp_dir().join(format!("statsig_eval_bench_{}", std::process::id()));
78
+ std::fs::create_dir_all(&dir).unwrap();
79
+ let specs_path = dir.join(format!("{}_specs.json", djb2(sdk_key)));
80
+ std::fs::copy(&dcs_file, &specs_path)
81
+ .unwrap_or_else(|e| panic!("copy {dcs_file} -> {specs_path:?}: {e}"));
82
+
83
+ let adapter = Arc::new(StatsigLocalFileSpecsAdapter::new(
84
+ sdk_key,
85
+ dir.to_str().unwrap(),
86
+ None,
87
+ false,
88
+ true, // disable_network
89
+ ));
90
+
91
+ let options = StatsigOptions {
92
+ specs_adapter: Some(adapter.clone()),
93
+ disable_network: Some(true),
94
+ ..Default::default()
95
+ };
96
+
97
+ let statsig = Arc::new(Statsig::new(sdk_key, Some(Arc::new(options))));
98
+ statsig.initialize().await.expect("initialize");
99
+ adapter.resync_from_file().expect("resync specs from file");
100
+
101
+ let probe = make_user(0);
102
+ println!(
103
+ "probe check_gate({gate}) = {} (exposures={log_exposures}, manyGates={many_gates})",
104
+ statsig.check_gate(&probe, &gate)
105
+ );
106
+
107
+ for &threads in &thread_counts {
108
+ let stop = Arc::new(AtomicBool::new(false));
109
+ let total_ops = Arc::new(AtomicU64::new(0));
110
+ let mut handles = Vec::new();
111
+
112
+ for t in 0..threads {
113
+ let statsig = statsig.clone();
114
+ let stop = stop.clone();
115
+ let total_ops = total_ops.clone();
116
+ let gate = gate.clone();
117
+ handles.push(std::thread::spawn(move || {
118
+ let opts = FeatureGateEvaluationOptions {
119
+ disable_exposure_logging: !log_exposures,
120
+ };
121
+ let mut ops: u64 = 0;
122
+ let mut user = make_user(t as u64);
123
+ while !stop.load(Ordering::Relaxed) {
124
+ ops += 1;
125
+ if ops % reuse_user == 0 {
126
+ user = make_user((t as u64) << 32 | ops);
127
+ }
128
+ let gate_name = if many_gates > 0 {
129
+ format!("syn_gate_{:04}", ops % many_gates)
130
+ } else {
131
+ gate.clone()
132
+ };
133
+ statsig.check_gate_with_options(&user, &gate_name, opts.clone());
134
+ }
135
+ total_ops.fetch_add(ops, Ordering::Relaxed);
136
+ }));
137
+ }
138
+
139
+ let start = Instant::now();
140
+ std::thread::sleep(Duration::from_secs(duration_sec));
141
+ stop.store(true, Ordering::Relaxed);
142
+ for h in handles {
143
+ h.join().unwrap();
144
+ }
145
+ let elapsed = start.elapsed().as_secs_f64();
146
+ let ops = total_ops.load(Ordering::Relaxed);
147
+ println!(
148
+ "threads={threads:>3} total={ops:>12} qps={:>12.0} per-thread qps={:>10.0}",
149
+ ops as f64 / elapsed,
150
+ ops as f64 / elapsed / threads as f64
151
+ );
152
+ }
153
+
154
+ statsig.shutdown().await.ok();
155
+ }
@@ -0,0 +1,63 @@
1
+ // Micro-bench: is InternedString get+drop the eval-path serializer?
2
+ //
3
+ // Every evaluation does InternedString::from_str_ref(spec_name) (evaluator.rs)
4
+ // and later drops it (release_string). Both operations touch global interned
5
+ // storage. This bench isolates exactly that pair, same hot key on all threads,
6
+ // mirroring a hot gate at high QPS.
7
+ //
8
+ // BENCH_THREADS comma list (default 1,4,16,64)
9
+ // BENCH_DURATION_SEC (default 5)
10
+ //
11
+ // Run: cargo run --release --example intern_contention_bench
12
+
13
+ use statsig_rust::interned_string::InternedString;
14
+ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
15
+ use std::sync::Arc;
16
+ use std::time::{Duration, Instant};
17
+
18
+ fn main() {
19
+ let thread_counts: Vec<usize> = std::env::var("BENCH_THREADS")
20
+ .unwrap_or_else(|_| "1,4,16,64".into())
21
+ .split(',')
22
+ .map(|s| s.trim().parse().unwrap())
23
+ .collect();
24
+ let duration_sec: u64 = std::env::var("BENCH_DURATION_SEC")
25
+ .unwrap_or_else(|_| "5".into())
26
+ .parse()
27
+ .unwrap();
28
+
29
+ for &threads in &thread_counts {
30
+ let stop = Arc::new(AtomicBool::new(false));
31
+ let total = Arc::new(AtomicU64::new(0));
32
+ let mut handles = Vec::new();
33
+ for _ in 0..threads {
34
+ let stop = stop.clone();
35
+ let total = total.clone();
36
+ handles.push(std::thread::spawn(move || {
37
+ let mut ops = 0u64;
38
+ while !stop.load(Ordering::Relaxed) {
39
+ // The exact per-eval pair: intern the (hot) spec name,
40
+ // then drop it (which calls release_string).
41
+ let s = InternedString::from_str_ref("test_many_rules");
42
+ std::hint::black_box(&s);
43
+ drop(s);
44
+ ops += 1;
45
+ }
46
+ total.fetch_add(ops, Ordering::Relaxed);
47
+ }));
48
+ }
49
+ let start = Instant::now();
50
+ std::thread::sleep(Duration::from_secs(duration_sec));
51
+ stop.store(true, Ordering::Relaxed);
52
+ for h in handles {
53
+ h.join().unwrap();
54
+ }
55
+ let el = start.elapsed().as_secs_f64();
56
+ let ops = total.load(Ordering::Relaxed);
57
+ println!(
58
+ "threads={threads:>3} intern+drop/sec={:>12.0} per-thread={:>11.0}",
59
+ ops as f64 / el,
60
+ ops as f64 / el / threads as f64
61
+ );
62
+ }
63
+ }
@@ -27,27 +27,38 @@ fn test_jsonify_bool_two_ways() {
27
27
  #[test]
28
28
  #[serial]
29
29
  fn test_memoization_from_json_object() {
30
+ // Baseline-relative: eviction is amortized, so other tests' dropped values
31
+ // may still be in the store until a sweep runs.
32
+ InternedStore::run_maintenance();
33
+ let before = get_memo_len();
34
+
30
35
  let raw = r#"{"once":{"key":"value"},"twice":{"key":"value"}}"#;
31
36
 
32
37
  let deserialized: HashMap<String, DynamicReturnable> = serde_json::from_str(raw).unwrap();
33
38
 
34
- assert_eq!(get_memo_len(), 1);
39
+ assert_eq!(get_memo_len(), before + 1);
35
40
 
36
41
  drop(deserialized);
42
+ InternedStore::run_maintenance();
37
43
 
38
- assert_eq!(get_memo_len(), 0);
44
+ assert_eq!(get_memo_len(), before);
39
45
  }
40
46
 
41
47
  #[test]
42
48
  #[serial]
43
49
  fn test_memoization_from_json_bool() {
50
+ // Compare against a baseline instead of asserting an empty store: eviction
51
+ // is amortized now, so other tests' dropped values may not be swept yet.
52
+ InternedStore::run_maintenance();
53
+ let before = get_memo_len();
54
+
44
55
  let raw = r#"{"once":true,"twice":true}"#;
45
56
 
46
57
  let deserialized: HashMap<String, DynamicReturnable> = serde_json::from_str(raw).unwrap();
47
58
  let value = deserialized.get("once").unwrap();
48
59
  assert_eq!(value.get_bool(), Some(true));
49
60
 
50
- assert_eq!(get_memo_len(), 0, "Bools are not memoized");
61
+ assert_eq!(get_memo_len(), before, "Bools are not memoized");
51
62
  }
52
63
 
53
64
  fn get_memo_len() -> usize {
@@ -128,12 +128,12 @@ impl CountryLookup {
128
128
  return None;
129
129
  }
130
130
 
131
- let ip = match user.get_user_value(&Some(DynamicString::from(IP.to_string()))) {
132
- Some(v) => match &v.string_value {
131
+ let ip = {
132
+ let v = user.get_user_value(&Some(DynamicString::from(IP.to_string())))?;
133
+ match &v.string_value {
133
134
  Some(s) => &s.value,
134
135
  _ => return None,
135
- },
136
- None => return None,
136
+ }
137
137
  };
138
138
 
139
139
  Self::lookup(ip, evaluator_context)
@@ -138,6 +138,11 @@ impl Serialize for DynamicReturnable {
138
138
 
139
139
  impl Drop for DynamicReturnable {
140
140
  fn drop(&mut self) {
141
+ // Only JsonPointer values live in the mutable store; other variants
142
+ // (Null/Bool/Static/Archived) have nothing to release.
143
+ if !matches!(self.value, DynamicReturnableValue::JsonPointer(_)) {
144
+ return;
145
+ }
141
146
  self.value = DynamicReturnableValue::Null;
142
147
  InternedStore::release_returnable(self.hash);
143
148
  }
@@ -58,6 +58,14 @@ impl Evaluator {
58
58
  ) -> Result<Recognition, StatsigErr> {
59
59
  let spec_name_intern = InternedString::from_str_ref(spec_name);
60
60
 
61
+ // Persistent-assignment enforceTargeting / enforceOverrides re-run a
62
+ // top-level evaluation restricted to the targeting-only / override-only
63
+ // rules of the spec. In that mode we bypass overrides / config mapping /
64
+ // CMAB and only consider the matching subset of `spec.rules`. Nested
65
+ // evaluations (nested_count > 0) always see the full rule set.
66
+ let filter_active =
67
+ ctx.nested_count == 0 && (ctx.only_evaluate_targeting || ctx.only_evaluate_overrides);
68
+
61
69
  let opt_spec = match spec_type {
62
70
  SpecType::Gate => ctx.specs_data.feature_gates.get(&spec_name_intern),
63
71
  SpecType::DynamicConfig => ctx.specs_data.dynamic_configs.get(&spec_name_intern),
@@ -69,16 +77,18 @@ impl Evaluator {
69
77
  }
70
78
  .map(|sp| sp.as_spec_ref());
71
79
 
72
- if try_apply_override(ctx, spec_name, spec_type, opt_spec) {
73
- return Ok(Recognition::Recognized);
74
- }
80
+ if !filter_active {
81
+ if try_apply_override(ctx, spec_name, spec_type, opt_spec) {
82
+ return Ok(Recognition::Recognized);
83
+ }
75
84
 
76
- if try_apply_config_mapping(ctx, spec_name, spec_type, opt_spec) {
77
- return Ok(Recognition::Recognized);
78
- }
85
+ if try_apply_config_mapping(ctx, spec_name, spec_type, opt_spec) {
86
+ return Ok(Recognition::Recognized);
87
+ }
79
88
 
80
- if evaluate_cmab(ctx, spec_name, spec_type) {
81
- return Ok(Recognition::Recognized);
89
+ if evaluate_cmab(ctx, spec_name, spec_type) {
90
+ return Ok(Recognition::Recognized);
91
+ }
82
92
  }
83
93
 
84
94
  let spec = unwrap_or_return!(opt_spec, Ok(Recognition::Unrecognized));
@@ -113,7 +123,21 @@ impl Evaluator {
113
123
  return new_layer_eval(ctx, spec);
114
124
  }
115
125
 
126
+ // In filtered mode, an empty subset means the user is not gated out of
127
+ // targeting / is not overridden. Mirror the legacy Java server SDK by
128
+ // returning a false evaluation rather than falling through to the
129
+ // spec's default value.
130
+ if filter_active && !spec_has_filtered_rule(ctx, spec) {
131
+ ctx.result.bool_value = false;
132
+ ctx.finalize_evaluation(spec, None);
133
+ return Ok(Recognition::Recognized);
134
+ }
135
+
116
136
  for rule in &spec.rules {
137
+ if filter_active && !rule_matches_filter(ctx, rule) {
138
+ continue;
139
+ }
140
+
117
141
  evaluate_rule(ctx, rule)?;
118
142
 
119
143
  if ctx.result.unsupported {
@@ -175,7 +199,19 @@ fn new_layer_eval<'a>(
175
199
  let mut secondary_exposures: Vec<SecondaryExposure> = Vec::new();
176
200
  let mut undelegated_secondary_exposures: Vec<SecondaryExposure> = Vec::new();
177
201
 
202
+ let filter_active =
203
+ ctx.nested_count == 0 && (ctx.only_evaluate_targeting || ctx.only_evaluate_overrides);
204
+ if filter_active && !spec_has_filtered_rule(ctx, spec) {
205
+ ctx.result.bool_value = false;
206
+ ctx.finalize_evaluation(spec, None);
207
+ return Ok(Recognition::Recognized);
208
+ }
209
+
178
210
  for rule in &spec.rules {
211
+ if filter_active && !rule_matches_filter(ctx, rule) {
212
+ continue;
213
+ }
214
+
179
215
  evaluate_rule(ctx, rule)?;
180
216
  secondary_exposures.append(&mut ctx.result.secondary_exposures);
181
217
  undelegated_secondary_exposures.append(&mut ctx.result.secondary_exposures);
@@ -258,6 +294,15 @@ fn new_layer_eval<'a>(
258
294
  Ok(Recognition::Recognized)
259
295
  }
260
296
 
297
+ fn rule_matches_filter(ctx: &EvaluatorContext, rule: &Rule) -> bool {
298
+ (ctx.only_evaluate_targeting && rule.is_targeting_rule())
299
+ || (ctx.only_evaluate_overrides && rule.is_override_rule())
300
+ }
301
+
302
+ fn spec_has_filtered_rule(ctx: &EvaluatorContext, spec: &Spec) -> bool {
303
+ spec.rules.iter().any(|rule| rule_matches_filter(ctx, rule))
304
+ }
305
+
261
306
  fn update_parameter_values(
262
307
  value: &mut HashMap<String, Value>,
263
308
  rule_ids: &mut HashMap<InternedString, InternedString>,
@@ -602,6 +647,8 @@ fn evaluate_experiment_group<'a>(
602
647
  ExperimentEvaluationOptions {
603
648
  disable_exposure_logging: ctx.disable_exposure_logging,
604
649
  user_persisted_values: None,
650
+ enforce_overrides: false,
651
+ enforce_targeting: false,
605
652
  },
606
653
  );
607
654
  res.group_name
@@ -36,6 +36,11 @@ pub struct EvaluatorContext<'a> {
36
36
  pub statsig: Option<&'a Statsig>,
37
37
  pub disable_exposure_logging: bool,
38
38
  pub gcir_hashes: Vec<u64>,
39
+ // When set, a top-level evaluation only considers the corresponding subset of
40
+ // rules (targeting-only / override-only). Used by persistent-assignment
41
+ // enforceTargeting / enforceOverrides to decide whether a sticky value still holds.
42
+ pub only_evaluate_targeting: bool,
43
+ pub only_evaluate_overrides: bool,
39
44
  }
40
45
 
41
46
  impl<'a> EvaluatorContext<'a> {
@@ -67,6 +72,8 @@ impl<'a> EvaluatorContext<'a> {
67
72
  statsig,
68
73
  disable_exposure_logging,
69
74
  gcir_hashes: Vec::new(),
75
+ only_evaluate_targeting: false,
76
+ only_evaluate_overrides: false,
70
77
  }
71
78
  }
72
79