DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `cache`.`cache_calc_start`(OUT `v_calc` INT, INOUT `v_refresh` INT, IN `v_cache_name` VARCHAR(50), IN `v_params` VARCHAR(100))
proc: BEGIN
	DECLARE v_valid BOOL;
	DECLARE v_lock_id VARCHAR(100);
	DECLARE v_cache_id INT;
	DECLARE v_expires DATETIME;
	DECLARE v_clean_time DATETIME;
	DECLARE vLastRefresh DATETIME;

	DECLARE EXIT HANDLER FOR SQLEXCEPTION
	BEGIN
		IF v_lock_id IS NOT NULL THEN
			DO RELEASE_LOCK(v_lock_id);
		END IF;

		RESIGNAL;
	END;

	SET v_params = IFNULL(v_params, '');

	-- Si el servidor se ha reiniciado invalida todos los calculos.

	SELECT COUNT(*) > 0 INTO v_valid FROM cache_valid;

	IF !v_valid
	THEN
		DELETE FROM cache_calc;
		INSERT INTO cache_valid (valid) VALUES (TRUE);
	END IF;

	-- Obtiene un bloqueo exclusivo para que no haya problemas de concurrencia.

	SET v_lock_id = CONCAT_WS('/', v_cache_name, v_params);

	IF !GET_LOCK(v_lock_id, 30)
	THEN
		SET v_calc = NULL;
		SET v_refresh = FALSE;
		LEAVE proc;
	END IF;

	-- Comprueba si el calculo solicitado existe y esta actualizado.

	SELECT c.id, ca.id, ca.expires, ca.last_refresh
		INTO v_cache_id, v_calc, v_expires, vLastRefresh
		FROM cache c
			LEFT JOIN cache_calc ca
				ON ca.cache_id = c.id AND ca.params = v_params COLLATE 'utf8_general_ci' 
		WHERE c.name = v_cache_name COLLATE 'utf8_general_ci';
				
	-- Si existe una calculo valido libera el bloqueo y devuelve su identificador.

	IF !v_refresh AND NOW() < v_expires AND vLastRefresh >= CURDATE() 
	THEN
		DO RELEASE_LOCK(v_lock_id);
		SET v_refresh = FALSE;
		LEAVE proc;
	END IF;

	-- Si el calculo no existe le crea una entrada en la tabla de calculos.

	IF v_calc IS NULL
	THEN
		INSERT INTO cache_calc SET
			cache_id = v_cache_id,
			cacheName = v_cache_name,
			params = v_params,
			last_refresh = NULL,
			expires = NULL,
			connection_id = CONNECTION_ID();

		SET v_calc = LAST_INSERT_ID();
	ELSE
		UPDATE cache_calc
			SET
				last_refresh = NULL,
				expires = NULL,
				connection_id = CONNECTION_ID()
			WHERE id = v_calc;
	END IF;

	-- Si se debe recalcular mantiene el bloqueo y devuelve su identificador.

	SET v_refresh = TRUE;
END$$
DELIMITER ;