Untitled

mail@pastecode.io avatar
unknown
plain_text
2 months ago
7.2 kB
2
Indexable
Never
CLUSTER_HOST = 'remote_host'

r = StrictRedis(CLUSTER_HOST, 6379)


"""
CREATE TABLE `experiments` (
  `experiment_id` int AUTO_INCREMENT,
  `title` varchar(100) NOT NULL,
  `is_disabled` tinyint DEFAULT '0',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `start_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `end_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`experiment_id`)
) ENGINE = InnoDB

CREATE TABLE `experiment_variants` (
  `variant_id` int AUTO_INCREMENT NOT NULL,
  `experiment_id` int NOT NULL,
  `title` varchar(50) NOT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`variant_id`),
  KEY `experiment_id` (`experiment_id`),
  CONSTRAINT `experiment_variants_ibfk_1` FOREIGN KEY (`experiment_id`)
  REFERENCES `experiments` (`experiment_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB

CREATE TABLE `device_experiments` (
  `id` int AUTO_INCREMENT NOT NULL ,
  `device_id` int NOT NULL,
  `experiment_id` int NOT NULL,
  `variant_id` int NOT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `variant_id` (`variant_id`),
  KEY `experiment_id` (`experiment_id`),
  CONSTRAINT `device_experiments_ibfk_1` FOREIGN KEY (`variant_id`) REFERENCES `experiment_variants` (`variant_id`)
   ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `device_experiments_ibfk_2` FOREIGN KEY (`experiment_id`) REFERENCES `experiments` (`experiment_id`)
  ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB

"""


def get_experiment_by_id(experiment_id):
    key, ttl = f"exp:detail:{experiment_id}", 3600 * 12

    details = r.get(key)
    if details:
        return json.loads(details)

    query = f"""SELECT * FROM experiments where experiment_id = '{experiment_id}'"""

    cursor = connection.cursor()
    cursor.execute(query)
    data = cursor.fetchone()
    cursor.close()
    details = {}
    if data:
        details = data[0]
        r.set(key, json.dumps(details))
        r.expire(key, ttl)

    return details


def get_variant_by_id(variant_id):
    """
    Get variant details by variant id
    :param variant_id: Variant Id
    :type variant_id: str
    :return: Variant Details
    :rtype: dict
    """
    cache_key, ttl = f'variant:detail:{variant_id}', 3600 * 12

    variant = r.get(cache_key)
    if variant:
        variant_json = json.loads(variant)
        return variant_json

    query = f"""SELECT * FROM experiment_variants where variant_id = '{variant_id}'"""

    cursor = connection.cursor()
    cursor.execute(query)
    data = cursor.fetchone()
    cursor.close()
    details = {}
    if data:
        details = data[0]
        r.set(cache_key, json.dumps(details))
        r.expire(cache_key, ttl)

    return details


def get_variants_by_experiment_id(experiment_id):
    cache_key, ttl = f'exp:variants:{experiment_id}', 3600 * 12
    variant_details = r.get(cache_key)
    if variant_details:
        variants_list = json.loads(variant_details)
        return variants_list

    query = f"""SELECT variant_id FROM experiment_variants where experiment_id = '{experiment_id}'"""

    cursor = connection.cursor()
    cursor.execute(query)
    data = cursor.fetchall()
    cursor.close()
    variant_ids = []
    for row in data:
        variant_id = row[0]
        variant_ids.append(variant_id)

    if variant_ids:
        r.set(cache_key, json.dumps(variant_ids))

    return variant_ids


def get_device_variant(device_id, experiment_id):
    cache_key, ttl = f'device:experiment_var:{device_id}:{experiment_id}', 3600 * 12
    variant = r.get(cache_key)
    if variant:
        variant_id = json.loads(variant)
        return variant_id

    query = f"""
    SELECT variant_id FROM device_experiments 
    where device_id = '{device_id}' and experiment_id = '{experiment_id}'
    """

    cursor = connection.cursor()
    cursor.execute(query)
    data = cursor.fetchone()
    cursor.close()
    variant_id = None
    if data:
        variant_id = data[0]
        r.set(cache_key, json.dumps(variant_id))
        r.expire(cache_key, ttl)

    return variant_id


class BaseExp:

    @staticmethod
    def allocate_variant(device_id, experiment_id):
        exp_details = get_experiment_by_id(experiment_id)
        if exp_details.get('is_disabled'):
            return {}

        existing_variant_id = get_device_variant(device_id, experiment_id)
        if existing_variant_id:
            return get_variant_by_id(existing_variant_id)

        variants = get_variants_by_experiment_id(experiment_id)
        if not variants:
            return {}
        chosen_variant = random.choice(variants)

        query = f"""
        INSERT INTO device_experiments (device_id, experiment_id, variant_id) 
        VALUES ('{device_id}', '{experiment_id}', '{chosen_variant}')
        """

        cursor = connection.cursor()
        cursor.execute(query)
        cursor.close()

        chosen_variant_details = get_variant_by_id(chosen_variant)
        return chosen_variant_details


class Exp1(BaseExp):

    def allocate_variant(self, device_id, experiment_id):
        variant = self.allocate_variant(device_id, experiment_id)
        return variant


class Exp2(BaseExp):

    def allocate_variant(self, device_id, experiment_id):
        variant = self.allocate_variant(device_id, experiment_id)
        return variant


class Exp3(BaseExp):

    def allocate_variant(self, device_id, experiment_id):
        variant = self.allocate_variant(device_id, experiment_id)
        return variant


class Exp4(BaseExp):

    def allocate_variant(self, device_id, experiment_id):
        variant = self.allocate_variant(device_id, experiment_id)
        return variant


class Exp5(BaseExp):

    def allocate_variant(self, device_id, experiment_id):
        variant = self.allocate_variant(device_id, experiment_id)
        return variant


def launch(request):
    data = request.GET.dict()

    # Parameters coming in the request
    user_id = data.get('user_id')
    device_id = data.get('device_id')
    platform = data.get('platform')
    response = {}

    if not device_id and not user_id:
        return Response(
            {
                'error': {
                    'message': 'Cannot fetch config'
                }
            },
            status=400
        )

    var1 = Exp1().allocate_variant(device_id, 1)
    var2 = Exp2().allocate_variant(device_id, 2)
    Exp3().allocate_variant(device_id, 3)
    Exp4().allocate_variant(device_id, 4)
    Exp5().allocate_variant(device_id, 5)

    if var1 and var1.get('title', '') == "B":
        response['detail_1'] = 'details1'
        response['detail_2'] = True

    if var2 and var2.get('title', '') == "B":
        response['detail_1'] = 'details2'
        response['detail_2'] = False

    return response


"""
- launch() -> Very high latency API
- Number of redis calls per request is very high
- Redis get calls have high latencies

Redis Cluster -> Single cloud hosted redis cluster

"""

Leave a Comment