Monday, June 4, 2007

Cache

Por razões de desempenho, para fazer menos requisições e execuções de querys na DB, surgio a necessidade de fazer cache, mas como manter os filtros e os resultado de diversas querys distintas? Tive a idéia de fazer serialização dos DataTable, e para isto fiz uma classe, que pode além de DataTable, fazer serialização de qualquer objecto que suporte serialization.

Exemplo do uso da classe Cache:

[CODE]
Cache cache = new Cache(WebConfigurationManager.AppSettings["CacheFolder"]);
if (cache.PleaseSaveMe(query))
{
cache.Save(query, dv.Table, WebConfigurationManager.AppSettings["CacheTime"]);
}
else
{
(DataTable)cache.Load(query);
}
[/CODE]

Neste caso usei a query para servir de ID para o Cache, ou seja sempre que for uma query diferente é gravado em Cache o DataTable gerado por esta query, ou se for uma query já executada então o DataTable é recuperado.

Descrição:

[CODE]
new Cache("CACHE_FOLDER");
if (cache.PleaseSaveMe("CACHE_ID"))
{
cache.Save("CACHE_ID", CACHE_OBJECT, "CACHE_TIME");
}
else
{
(CACHE_OBJECT)cache.Load("CACHE_ID")
}
[/CODE]

CACHE_FOLDER é o caminho para a pasta que vai conter os arquivos de cache.

CACHE_ID é o ID de referência de um objeto em cache, é a chave para poder recupera-lo.

CACHE_OBJECT é o objecto que será gravado em cache, lembrando que tem o objeto tem que suportar serialization.

CACHE_TIME define o tempo em que o objeto ficará em cache, pode ser passado no seguinte formato (também é possível em long):
  - "2W" = 2 semanas
  - "5D" = 5 dias
  - "1H" = 1 hora
  - "15M" = 15 minutos
  - "50S" = 50 segundos

cache.PleaseSaveMe verifica se é necessário gravar o cache.

cache.Save grava um novo ou sobrepõe o objeto em cache.

cache.Load recupera o objeto que esta em cache.

Para cada objeto em cache é gerado 3 arquivos.

  - [GUID].id.cache identificador do cache, o tal ID que é passado no save, e que é usado para recuperar o objeto.

  - [GUID].obj.cache arquivo do objeto serializado.

  - [GUID].time.cache tempo que esta em cache.

Código da Class Cache:

[CODE]
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
public class Cache
{
public const string ID_EXT = ".id.cache";
public const string TIME_EXT = ".time.cache";
public const string OBJ_EXT = ".obj.cache";
 
private string folder = "";
 
public const long WEEK = 6048000000000L;
public const long DAY = 864000000000L;
public const long HOUR = 36000000000L;
public const long MINUTE = 600000000L;
public const long SECOND = 10000000L;
 
public Cache(string _folder)
{
folder = _folder;
}
 
public string GetGuid(string id)
{
string[] files = Directory.GetFiles(folder);
string guid = "";
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
StreamReader srID = new StreamReader(file_id);
if (id == srID.ReadToEnd())
{
guid = file_id.Replace(folder, "").Replace(ID_EXT, "");
}
srID.Close();
if (guid != "")
{
break;
}
}
}
return guid;
}
 
public object Load(string id)
{
string fileOBJ = folder + GetGuid(id) + OBJ_EXT;
FileStream fsOBJ = new FileStream(fileOBJ, FileMode.Open);
object obj = new BinaryFormatter().Deserialize(fsOBJ);
fsOBJ.Close();
return obj;
}
 
public void Save(string id, object obj, long lifeTime)
{
Save(id, obj, lifeTime.ToString(), true);
}
 
public void Save(string id, object obj, string lifeTime)
{
Save(id, obj, lifeTime, false);
}
 
private void Save(string id, object obj, string lifeTime, bool lifeTimeIsLong)
{
Clear(id);
if (!lifeTimeIsLong)
{
string timeType = lifeTime.Substring(lifeTime.Length - 1);
long time = Convert.ToInt64(lifeTime.Substring(0, lifeTime.Length - 1));
switch (timeType.ToUpper())
{
case "W":
lifeTime = ""+ (time * WEEK);
break;
case "D":
lifeTime = ""+ (time * DAY);
break;
case "H":
lifeTime = ""+ (time * HOUR);
break;
case "M":
lifeTime = ""+ (time * MINUTE);
break;
case "S":
lifeTime = ""+ (time * SECOND);
break;
default:
break;
}
}
string file = folder + Guid.NewGuid().ToString();
string file_id = file + ID_EXT;
string file_time = file + TIME_EXT;
string file_obj = file + OBJ_EXT;
StreamWriter swID = File.CreateText(file_id);
swID.Write(id);
swID.Close();
StreamWriter swTIME = File.CreateText(file_time);
swTIME.WriteLine(DateTime.Now.Ticks.ToString());
swTIME.WriteLine(lifeTime);
swTIME.Close();
 
MemoryStream msOBJ = new MemoryStream();
new BinaryFormatter().Serialize(msOBJ, obj);
FileStream fsOBJECT = File.Create(file_obj);
fsOBJECT.Write(msOBJ.ToArray(), 0, (int)msOBJ.Length);
fsOBJECT.Flush();
fsOBJECT.Close();
msOBJ.Close();
}
 
public bool PleaseSaveMe(string id)
{
 
string[] files = Directory.GetFiles(folder);
bool found = false;
bool result = false;
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
string file_time = file_id.Replace(ID_EXT, TIME_EXT);
StreamReader srID = new StreamReader(file_id);
StreamReader srTIME = new StreamReader(file_time);
string _id = srID.ReadToEnd();
long createTime = Convert.ToInt64(srTIME.ReadLine());
long lifeTime = Convert.ToInt64(srTIME.ReadLine());
srID.Close();
srTIME.Close();
if (id == _id)
{
found = true;
if (DateTime.Now.Ticks - lifeTime > createTime)
{
result = true;
}
break;
}
}
}
if (found == false)
{
return true;
}
return result;
}
 
public void Clear()
{
Clear("");
}
 
public void Clear(string id)
{
string[] files = Directory.GetFiles(folder);
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
string file_time = file_id.Replace(ID_EXT, TIME_EXT);
StreamReader srID = new StreamReader(file_id);
StreamReader srTIME = new StreamReader(file_time);
string _id = srID.ReadToEnd();
long createTime = Convert.ToInt64(srTIME.ReadLine());
long lifeTime = Convert.ToInt64(srTIME.ReadLine());
srID.Close();
srTIME.Close();
if (id == _id)
{
File.Delete(file_id);
File.Delete(file_time);
File.Delete(file_id.Replace(ID_EXT, OBJ_EXT));
}
else
{
if (DateTime.Now.Ticks - lifeTime > createTime)
{
File.Delete(file_id);
File.Delete(file_time);
File.Delete(file_id.Replace(ID_EXT, OBJ_EXT));
}
}
}
}
}
}

[/CODE]

Object.clone();

Para entender como funciona a clonagem de objetos em Java fiz este exemplo, basta analizar o codigo e a execuçao, e tirar as proprias conclusões :P

[CODE]

public class RunVehiclesClone {

public static void main(String[] args) {
System.out.println();
System.out.println("===================================");
System.out.println();
run();
System.out.println("===================================");
System.out.println();
runClone();
System.out.println("===================================");
System.out.println();
runCloneDeep();
System.out.println("===================================");
System.out.println();
runCloneSerialization();
System.out.println("===================================");
System.out.println();
}

private static void run() {
System.out.println("-----------------------------------");
System.out.println("NORMAL");
System.out.println("-----------------------------------");
java.util.List<Vehicle> listVehicles = new java.util.ArrayList();

Vehicle vehicle = new Vehicle();
vehicle.setName("Vehicle");

VehicleDescription vehicleDescription = new VehicleDescription();
vehicleDescription.setName("Description");

vehicle.setVehicleDescription(vehicleDescription);

listVehicles.add(vehicle);

Vehicle superBike = vehicle;
superBike.setName("SuperBike");
superBike.getVehicleDescription().setName("Kawasaki Ninja ZXR6");

listVehicles.add(superBike);

Vehicle car = vehicle;
car.setName("Car");
car.getVehicleDescription().setName("Audi A3");

listVehicles.add(car);

System.out.println();
for (Vehicle _vehicle : listVehicles) {
System.out.println(_vehicle);
System.out.println();
}
}

private static void runClone() {
System.out.println("-----------------------------------");
System.out.println("CLONE");
System.out.println("-----------------------------------");
java.util.List<Vehicle> listVehicles = new java.util.ArrayList();

Vehicle vehicle = new Vehicle();
vehicle.setName("Vehicle");

VehicleDescription vehicleDescription = new VehicleDescription();
vehicleDescription.setName("Description");

vehicle.setVehicleDescription(vehicleDescription);

listVehicles.add(vehicle);

Vehicle superBike = (Vehicle)vehicle.clone();
superBike.setName("SuperBike");
superBike.getVehicleDescription().setName("Kawasaki Ninja ZXR6");

listVehicles.add(superBike);

Vehicle car = (Vehicle)vehicle.clone();
car.setName("Car");
car.getVehicleDescription().setName("Audi A3");

listVehicles.add(car);

System.out.println();
for (Vehicle _vehicle : listVehicles) {
System.out.println(_vehicle);
System.out.println();
}
}

private static void runCloneDeep() {
System.out.println("-----------------------------------");
System.out.println("CLONE > DEEP");
System.out.println("-----------------------------------");
java.util.List<Vehicle> listVehicles = new java.util.ArrayList();

Vehicle vehicle = new Vehicle();
vehicle.setName("Vehicle");

VehicleDescription vehicleDescription = new VehicleDescription();
vehicleDescription.setName("Description");

vehicle.setVehicleDescription(vehicleDescription);

listVehicles.add(vehicle);

Vehicle superBike = (Vehicle)vehicle.cloneDeep();
superBike.setName("SuperBike");
superBike.getVehicleDescription().setName("Kawasaki Ninja ZXR6");

listVehicles.add(superBike);

Vehicle car = (Vehicle)vehicle.cloneDeep();
car.setName("Car");
car.getVehicleDescription().setName("Audi A3");

listVehicles.add(car);

System.out.println();
for (Vehicle _vehicle : listVehicles) {
System.out.println(_vehicle);
System.out.println();
}
}

private static void runCloneSerialization() {
System.out.println("-----------------------------------");
System.out.println("CLONE > SERIALIZATION");
System.out.println("-----------------------------------");
java.util.List<Vehicle> listVehicles = new java.util.ArrayList();

Vehicle vehicle = new Vehicle();
vehicle.setName("Vehicle");

VehicleDescription vehicleDescription = new VehicleDescription();
vehicleDescription.setName("Description");

vehicle.setVehicleDescription(vehicleDescription);

listVehicles.add(vehicle);

Vehicle superBike = (Vehicle)vehicle.cloneSerialization();
superBike.setName("SuperBike");
superBike.getVehicleDescription().setName("Kawasaki Ninja ZXR6");

listVehicles.add(superBike);

Vehicle car = (Vehicle)vehicle.cloneSerialization();
car.setName("Car");
car.getVehicleDescription().setName("Audi A3");

listVehicles.add(car);

System.out.println();
for (Vehicle _vehicle : listVehicles) {
System.out.println(_vehicle);
System.out.println();
}
}
}

//////////////////////////////////////////////

class Vehicle implements java.io.Serializable, Cloneable {
private String name = "";
private VehicleDescription vehicleDescription = null;

public Vehicle() {

}

public void setName(String _name) {
name = _name;
}

public String getName() {
return name;
}

public void setVehicleDescription(VehicleDescription _vehicleDescription) {
vehicleDescription = _vehicleDescription;
}

public VehicleDescription getVehicleDescription() {
return vehicleDescription;
}

public Object clone() {
try {
return super.clone();
} catch(CloneNotSupportedException e) {
throw new Error ("Cannot clone this object.");
}
}

public Object cloneDeep() {
try {
Vehicle vehicle = (Vehicle)super.clone();
VehicleDescription _vehicleDescription = new VehicleDescription();
_vehicleDescription.setName(getVehicleDescription().getName());
vehicle.setVehicleDescription(_vehicleDescription);
return vehicle;
} catch(CloneNotSupportedException e) {
throw new Error ("Cannot clone this object.");
}
}

public Object cloneSerialization() {
java.io.ObjectOutputStream oos = null;
java.io.ObjectInputStream ois = null;
try {
java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
oos = new java.io.ObjectOutputStream(bos);
oos.writeObject(this);
oos.flush();
java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(bos.toByteArray());
ois = new java.io.ObjectInputStream(bin);
return ois.readObject();
} catch(Exception e) {
throw new Error ("Cannot clone this object.");
} finally {
try {
oos.close();
ois.close();
} catch(Exception e) {
}
}
}

public String toString() {
return name + " - " + vehicleDescription;
}
}

//////////////////////////////////////////////

class VehicleDescription implements java.io.Serializable {
String name = "";
java.sql.Connection con = null;
public VehicleDescription() {

}

public void setName(String _name) {
name = _name;
}

public String getName() {
return name;
}

public String toString() {
return name;
}
}

[/CODE]