Well the images are ready now to populate the directories that will be uploaded to the production server.
Populate directories
There is a directory named website inside the $DEPLOY directory. This directory is to be used to host your static content: images, CSS files, javascripts, etc. But as we are going to proxy requeste from the webserver to the Seaside images, there must be a way for the webserver to know what to proxy and what not to. Requests for static content must not be proxied to the Seaside server and the easiest way for the webserver to recognize those requests is by means of the URL path of the request. So we will have a rule in the webserver configuration so that everything that has a path that begins with ‘/resources/’ will not be proxied but served directly by the webserver. The Seaside images never get those requests. The only generates the html that asks the webserver for that static content. So if a request arrives to the webserver with the ‘/resources/’ path in it, the webserver will try to find it on its document root. As the webserver will have its document root pointing to the website directory, a path of ‘/resources/’ will search for a directory resources in the website/ directory. So if a request arrives asking for:
http://seaside.example.com/resources/somefile.jpg
the webserver will try to serve the file located on:
website/resources/somefile.jpg
So create a directory named resources/ on the $DEPLOY/website directory:
mkdir $DEPLOY/website/resources/
cp website_files/* $DEPLOY/website/
and copy to it the files you want to be served directly by the webserver (website_files/ is the directory that currently has your content).
The SeasideProxyTester doesn’t have a lot of static content but it will show the Pharo logo, so lets download the Pharo logo from:
http://gforge.inria.fr/frs/download.php/22781/pharocard.jpg
and save it to $DEPLOY/website/resources/ as pharocard.jpg. This will be the static content for the SeasideProxyTester.
Now copy the scripts and images from the $WORK directory to the correct location on $DEPLOY:
cp $WORK/magma-run.st $DEPLOY/scripts
cp $WORK/seaside-run.st $DEPLOY/scripts
cp $WORK/start_app.sh $DEPLOY/scripts
cp $WORK/SqueakV39.sources $DEPLOY/pharo
cp $WORK/magma.image $DEPLOY/pharo
cp $WORK/magma.changes $DEPLOY/pharo
cp $WORK/seaside.image $DEPLOY/pharo
cp $WORK/seaside.changes $DEPLOY/pharo
Create the magma repository
Next step, create the magma repository. First load the magma image:
cd $DEPLOY/pharo
/opt/pharo/squeak magma.image
open a workspace and “DoIt” the following Smalltalk code:
“Create the repository”
MagmaRepositoryController
create: ‘../magma/’
root: Dictionary new.
“Start the server”
MagmaServerConsole new
open: ‘../magma/’;
processOn: 51969;
inspect.
“Save and quit the image”
SmalltalkImage current snapshot: true andQuit: true.
This do several things. First it creates the magma database in the $DEPLOY/magma directory, by using relative paths. It uses a Dictionary as the root of the repository. Inside this root we will put our application root object (our domain root). Next, it starts the server console listening on the port 51969 for incoming connections. This port will be used by the Magma seasideHelper code to connect the Seaside application to the Magma server. Finally it saves the image and quits. Next time that you start this image, the Magma server will automatically start and bound to the port, ready to accept connections. If you open the image, quit WITHOUT saving.
We are almost ready to test the application. We have already finished the directory setup. This $DEPLOY directory can be uploaded to the production server but before that we must configure the webserver to do load balancing and proxy to our Seaside images.
We are going to deploy a Seaside application that uses Magma seasideHelper to connect to a Magma database in a production server. The production server will have an image for the Magma server and serveral Seaside images with the application code. All the images will be PharoCore images with just the necessary packages loaded and nothing else. The users’ sessions will be load balanced and proxied by a web server. The Magma image, the Seaside images and the webserver will be hosted on the production server. For this tutorial we will use the SeasideProxyTester application from the
SeasideExamples project on SqueakSource.com. You can use your own code given that you do a few minor modifications that I will detail later.
Although this tutorial host everything on a single production machine, you can use as many servers as you want. For example, you can use a server for Magma, a group of servers for the Seaside images and a hardware load balancer instead of the webserver. Is up to you. I want to keep things simple here and I show a setup that involves the minimal set of parts but that explains the way to deploy a Seaside app to production.
You can follow this instructions directly on the production server and get exactly the same results but I recommend to first do it on your development machine and, when is working correctly, stop it, zip it, copy it to the production server, unzip it and start it. But as you want. I will do it on my development machine and the last part, the copying to production server, is left as an exercise for you (but it is very simple, really).
I will work with a development machine that has Debian GNU/Linux Testing (Squeeze), but my production server has Debian GNU/Linux 5.0 (Lenny). The instructions apply to both unchanged.
You can install and configure your production server following
this instructions. So to the first step.
Install the Squeak VM
The version of squeak installed on Debian Lenny and Debian Squeak with the following command:
# aptitude install squeak-vm
installs a vm that is not closure enabled (for explanations search the Squeak or Pharo mailing lists). As we are going to use the PharoCore we need VM that support closures. So we won’t install squeak from the Debian repositories (at least until they ship a closure vm, in which case I will update this instructions) but we will download the VM from the Pharo project
download page. There you’ll find a section named Virtual Machines. Download the Linux/Unix VM. Currently is
pharo-vm-0.15.2d-linux.zip
but any newer version will be ok.
Now, as the root user, copy the zip file to /opt, unzip it and make a symbolic link:
cd /opt
unzip pharo-vm-0.15.2d-linux.zip
ln -s pharo-vm-0.15.2d-linux pharo
Back as your normal user, test it:
export VM=/opt/pharo/squeak
$VM -help
This will show the help of the squeak vm executable. Read it carefully to understand the options we will use to start our images.
We have already populated the $DEPLOY directory with the images and scripts that will go to the production server, but before, we need to configure the webserver.
Copy deploy directory to final location
As root, copy the $DEPLOY directory to the /srv directory. From there it will be served to the world. Note that the $DEPLOY environment variable was defined for your normal user, not for root so you must use the full path to the $DEPLOY directory (/home/miguel/example/ in this example):
mv /home/miguel/example /srv/
Now, configure the start script:
vi /srv/example/script/start_app.sh
Now the explanation of this script. The script defines some variables. The important ones are:
HOME=”/srv/example”
Defines the “final” location of the $DEPLOY directory (/srv/example in this example).
VM=”/opt/pharo/squeak -mmap 100m -vm-sound-null -vm-display-null”
Defines the VM location and the options passed to the virtual machine. If you read the text on the squeak vm help you’ll know that this options limits the memory that an image can use (if not specified, will grow without limits and in a production server with several images running it is better to limit them). The vm-sound-null and vm-display-null will start the images headless. In a server you don’t need the graphics or the sound. Also, we have the VNC server to connect to the Magma server on demand.
START_PORT=9001
END_PORT=9004
This variables defines how many Seaside images will be launched. Only one magma image will be launched. The webserver configuration depends on the values you set here. Also, you can launch as many Seaside images as you want as long as you don’t use ALL the RAM of the production server. Otherwise it will begin swapping and the performance will drop. The number of images that you can launch depends on RAM and on the value of mmap you use on the VM variable.
For example if you are deploying to a dedicated server with 4 GB of RAM and you don’t plan to have other services you can safely reserve 500MB to OS processes and use 3500 MB of RAM for your images. If you choose a mmap of 200MB you can put 15 Seaside images + 1 Magma image, 16 images * 200 MB/image = 3400 MB and use almost all the RAM. If you use mmap=100M then you can put 30 Seaside images and 1 Magma image for a total of 3100MB that is more reasonable.
Anyway, you must adapt it to your server and *do* load testing while monitoring not only request/second but also OS performance, RAM usage, disk usage and a lot of things. That’s your homework.
Remember the value of mmap limits how much the image can grow and in consequence, the number of Seaside sessions you can create in a single Seaside image. Same goes for the magma image. If the image needs to grow for some operation, the mmap will limit it. Again, test your setup and adapt it as needed.
Well enough advise.
Each image launched will write its output to a file that is stored on the logs/ directory. Also the processes are launched by means of nohup so that they remain running even in when you disconnect from the production server.
So we are now ready to launch the images.
Start the images
As your normal user execute:
sh /srv/example/scripts/start_app.sh
This will start on the ports you especified in the start_app.sh script. If you try to point your web browser to one of the port you may or may not get a fully functional application. This depends on the way you emit URLs in your app. When we reach to the SeasideProxyTester explanation I will detail the settings that you need to configure in your app in order to prepare it for a proxy setup. More on this later.
So before you can test the images, lets configure the webserver.
lighttpd.conf:
##
# Core
##
server.pid-file = “/var/run/lighttpd.pid”
server.errorlog = “/var/log/lighttpd/error.log”
server.username = “www-data”
server.groupname = “www-data”
server.document-root = “/var/www/”
server.dir-listing = “disable”
index-file.names = ( “index.html”, “index.php” )
static-file.exclude-extensions = ( “.fcgi”, “.php”, “.rb”, “~”, “.inc” )
server.modules = ( “mod_access”, “mod_accesslog”, “mod_compress”, “mod_redirect”, “mod_proxy”, “mod_rewrite”, “mod_alias” )
##
# debug options
##
#debug.log-request-header = “enable”
#debug.log-request-handling = “enable”
proxy.debug = 1
##
# dirlisting module (loaded by default)
##
dir-listing.encoding = “utf-8”
##
# access module
##
url.access-deny = ( “~”, “.inc” )
$HTTP[“url”] =~ “/.svn/” {
url.access-deny = (“”)
}
##
# accesslog module
##
accesslog.filename = “/var/log/lighttpd/access.log”
##
# compress module
##
compress.cache-dir = “/var/cache/lighttpd/compress/”
compress.filetype = ( “text/plain”, “text/html”, “application/x-javascript”, “text/css” )
##
# Global settings
##
$HTTP[“host”] =~ “^www.(.*)” {
# no www for domains
url.redirect = ( “^/(.*)” => “http://%1/$1” )
}
##
# othersite.com
##
$HTTP[“host”] == “othersite.com” {
server.document-root = “/srv/www/othersite.com/”
}
$HTTP[“host”] == “seaside.example.com” {
server.document-root = “/srv/example/website/”
# We’ll use the resources directory to host static files: images, styles, etc
# Rewrite the URL
url.rewrite-once = (
”^/resources/(.*)” => “$0”, # Unaltered
”^/(.*)” => “/seaside/seasideProxyTester$1” # Rewritten
)
# Anything with seaside/seasideProxyTester proxied to Seaside
# No cookie, then proxy to a random port
$HTTP[“cookie”] !~ “server=app” {
proxy.balance = “round-robin”
proxy.server = (
“/seaside/seasideProxyTester” => (
( “host” => “127.0.0.1”, “port” => 9001),
( “host” => “127.0.0.1”, “port” => 9002),
( “host” => “127.0.0.1”, “port” => 9003),
( “host” => “127.0.0.1”, “port” => 9004)
)
)
} else $HTTP[“cookie”] =~ “app9001” { # Has a cookie, proxy to the corresponding port
proxy.server = ( “/seaside/seasideProxyTester” => (( “host” => “127.0.0.1”, “port” => 9001)))
} else $HTTP[“cookie”] =~ “app9002” {
proxy.server = ( “/seaside/seasideProxyTester” => (( “host” => “127.0.0.1”, “port” => 9002)))
} else $HTTP[“cookie”] =~ “app9003” {
proxy.server = ( “/seaside/seasideProxyTester” => (( “host” => “127.0.0.1”, “port” => 9003)))
} else $HTTP[“cookie”] =~ “app9004” {
proxy.server = ( “/seaside/seasideProxyTester” => (( “host” => “127.0.0.1”, “port” => 9004)))
}
}
$HTTP[“host”] == “magma.example.com” {
server.document-root = “/srv/example/website/”
# We’ll use the resources directory to host static files: images, styles, etc
# Rewrite the URL
url.rewrite-once = (
”^/resources/(.*)” => “$0”, # Unaltered
”^/(.*)” => “/seaside/magmaProxyTester$1” # Rewritten
)
# Anything with seaside/magmaProxyTester proxied to Seaside
# No cookie, then proxy to a random port
$HTTP[“cookie”] !~ “server=app” {
proxy.balance = “round-robin”
proxy.server = (
“/seaside/magmaProxyTester” => (
( “host” => “127.0.0.1”, “port” => 9001),
( “host” => “127.0.0.1”, “port” => 9002),
( “host” => “127.0.0.1”, “port” => 9003),
( “host” => “127.0.0.1”, “port” => 9004)
)
)
} else $HTTP[“cookie”] =~ “app9001” { # Has a cookie, proxy to the corresponding port
proxy.server = ( “/seaside/magmaProxyTester” => (( “host” => “127.0.0.1”, “port” => 9001)))
} else $HTTP[“cookie”] =~ “app9002” {
proxy.server = ( “/seaside/magmaProxyTester” => (( “host” => “127.0.0.1”, “port” => 9002)))
} else $HTTP[“cookie”] =~ “app9003” {
proxy.server = ( “/seaside/magmaProxyTester” => (( “host” => “127.0.0.1”, “port” => 9003)))
} else $HTTP[“cookie”] =~ “app9004” {
proxy.server = ( “/seaside/magmaProxyTester” => (( “host” => “127.0.0.1”, “port” => 9004)))
}
}
#### external configuration files
## mimetype mapping
include_shell “/usr/share/lighttpd/create-mime.assign.pl”
## load enabled configuration files,
## read /etc/lighttpd/conf-available/README first
include_shell “/usr/share/lighttpd/include-conf-enabled.pl”
Some things must be explained though. First, it declares a section for a host named othersite.com with its document root in /srv/www/othersite.com. This permits you serve other sites from the same webserver. Then declares two sections for the example.com subdomains. They are identical in its structure. The differences are in the entry points proxied to by lighttpd. In the case of seaside.example.com it proxies to a Seaside image using the entry point seasideProxyTester, conserving all the parameters (_s and _k). In the case of magma.example.com it rewrites the request and proxies to the magmaProxyTester. Additionally it verifies for paths beginning with ‘/resources’. Those requests are left unchanged. The rewrite rules trigger before the lighttpd engine processes the url so is after the rewrite that the cookies check is done. lighttpd checks for a cookie named appXXXX where XXXX is some of the ports we are using for the Seaside images. If it founds one, proxy to the corresponding Seaside image listening on that port. If it hasn’t a cookie like this, it uses a round-robin algorithm to choose a Seaside image to proxy the request to.
This is how it works. A new user beginning the session in your app will not have the cookie as part of the request. So lighttpd will proxy the request to a random Seaside image. When the selected Seaside image receive this request will add the cookie (using its own port name as the value) to the response so that the next request will include it. This way the next request will arrive to lighttpd with the cookie established and the webserver will correctly proxy to the same image that received the first request of the session.
The net result is that lighttpd is doing a kind of sticky session (meaning that the request of a session always goes to the same server being proxied) for Seaside.
If none of the rules is triggered, the webserver tries to match the requested path to some file in the document root. This is what happens to the requests having ‘/resources’ at the beginning of the path. They don’t match any rule and are served from the document root.
That is what lighttpd is doing in order to proxy to the Seaside images. The same explanation applies for the two subdomains.
Also note that you can serve several applications from the same Seaside images. You just proxy to a different entry point in order to Seaside correctly route the incoming request to the right application. This could or couldn’t be wise. The performance will degrade for all the apps if they are intensive. But again, test your setup and make a decision.
So, finally, as root, put the lighttpd.conf file on /etc/lighttpd/ and restart the server
/etc/init.d/lighttpd restart
We’re almost done. Last thing you must do is to add the example.com domain to your
/etc/hosts
file so that you can test it on localhost. Add a line like the following or edit the existing pointing to localhost (or 127.0.0.1)
127.0.0.1 laptop.localdomain laptop seaside.example.com magma.example.com
the part in bold is what you must add in order to resolve seaside.example.com and magma.example.com to the address 127.0.0.1. As I have lighttpd installed in localhost (my development image) the request will be redirected to it and then will proxy to the Seaside images.
Now finally we can test the setup.
Launch your web browser and write:
http://seaside.example.com
or
http://magma.example.com
The browser should resolve those domains to 127.0.0.1 and you should be greeted with the initial page of the SeasideProxyTester. Each reload of the page will increment the counters and you should see what Seaside image is serving your session requests.
Try to delete your cookies and try again. You should see that a different Seaside server is serving your requests.
So, you have a Seaside application running in a group of images, being proxied and load balanced by a webserver in sticky session way. Cool, isn’t?
This is the first of a several post about
Seaside.
The goal: to deploy a Seaside application to the real world.
Ingredients:
I must say that this is not the only way to do this kind of deployment. Ramon Leon’s
post explain other setup and also the Seaside
book has a section on scaling Seaside. This is the way I do it.
Some things to discuss first. I will be using Debian GNU/Linux Squeeze but the procedure is the same for other versions of Debian and for other distributions. I think that this setup can be done in Windows too but I don’t plan to check. I will be using Seaside 2.8 because is the current stable version. When the 3.0 release is announced I will update the instructions accordingly. The Magma version will be the 1.0r42 (server) that was recently released and that since this version runs also in Pharo. I will be using Magma seasideHelper that although for the moment isn’t being mantained, the version used here works very well. This is used for easy integration of Magma and Seaside. I will use lighttpd because that is the webserver I use on my sites but the equivalent Apache configuration it is very easy and sometimes shorter that the ones I show. nginx or pound can be used too. The webserver will be used to proxy the requests from the users to the Seaside server images. It will be doing load balancing too. In order to test the setup a simple Seaside application has been developed and uploaded to the
SeasideExamples project on SqueakSource. This can be used to verify that the webserver is doing a sticky session kind of load balancing. This is necessary because for a setup of proxied Seaside images a session must be routed always to the same image where it was created. This is not necessary on applications deployed to Gemstone/S or
GLASS because on them the session data is persisted to the OODB and restored on subsecuent request no matter what stone the request is handled by. But, in a setup that doesn’t use Gemstone/S the proxy/load balancing server must guarantee that a session is always routed to the same image. Of course, on the first request of a session, the server choose a random image to handle the session so that the users are distributed uniformly between the images.
The setup will be tested with ab, the tool from the apache project for load testing websites. This is a very simple test, as only request the first page of the application. This shows how many new sessions can be created in the cluster of Seaside images. It doesn’t test inner navigation of the application. Of course, the same setup can be tested with tools like
Selenium, JMeter, httperf, siege or jcrawler. This is left as an exercise to the reader. One cool feature of ab is that can output the results in a format understood by GNUPlot so that they can be plotted and visualized more easily.
Scripts to automate image setup are provided too. They update a PharoCore image and prepare Magma and Seaside images with just the required packages. Also, they configure an RFBServer (VNC server) so you can connect to the Magma image to do routine tasks as database backup (this at least until you integrate that tasks to your app so you can launch them from Seaside without entering the image).
Finally a script to start the images and procedures to stop them on demand are provided.
So, a lot of steps, but at the end you’ll have a fully deployed app in a server using a domain name and suitable of scaling by adding more images or physical servers behind the proxy/load balancing server. Also, with the current
high availability features of Magma you can also increase the persistence performance by adding more magma servers to the setup.
Ready? Lets go… to the Seaside.
Pharo is a new FOSS Smalltalk. It is a fork of
Squeak and since has had a lot of improvements. It was forked after lenghty and sometimes harsh discussions in the squeak mailing list. Looking backwards it seems that was a good decision, but at the time it should be hard to decide to fork a project that has so many hackers/programmers contributing to it and spending effort and energy coding in it. This of course gave the forkers the stigma of traitors, even if not with these words. The fact is that they forked it. The reasons? Among other things, the perceived impossibility to reach a real agreement over important aspects and the sometimes inflexible positions with respect to changes affecting more than the superficial levels of squeak. I don’t claim that these are the main or the principals reasons to fork squeak, but to me, are the most visible ones that improved in the Pharo fork. I’ll explain, but from now on I will call it Pharo, as that is its name. The fork part, well, it is less important each day.
So, the number of changes that Pharo has experienced is amazing, just check the
bugtrack and you’ll see that 1125 bugs have been reported to date. From those, more than 900 have been fixed and closed. In fact for the upcoming 1.0 release only 25 remains open. Most of these bugs show that it really were a lot of things that the squeak community wanted to change. Pharo is the place where this is happening. The most visible of all of this things is the user interface. There are a lot of opinions here, but to many people, the colorful, sometimes childish and maybe playschool look & feel of Squeak was a factor against the adoption of Squeak in more “enterprise” (whatever that means) environments. Pharo doesn’t look as a toy anymore. It has a look more akin to the MacOS X look & feel, and that by itself, avoids pointless questions the first time you open an image in front of new potential users. But the changes are not only in the surface but also in the very foundations of the image. Pharo had full closures way before Squeak decided to include them (in fact they are not included in the official image, but in the ongoing unstable image, the trunk image) and, until squeak decides to release a new version, Pharo will be the only (of the two) with full closures available to the masses. Other thing that is remarkable is the policy of unloading all but the fundamental packages from the image and load them only when necessary. This makes the Pharo image very slim (8 MB in the current PharoCore image) and ideally suited to deploy your apps with it. Other things that were purged from the image are the etoys part that not only lack a mantainer, but that already forked years ago to build their own distribution as squeakland.org.
But I don’t want to bash Squeak here, as they are currently having a lot of activity and a lot of things will improve. What I want to say is that Pharo is a refreshing, clean, lean, small and propositive option to develop Smalltalk projects.
Pharo has to versions, PharoCore and Pharo properly. Both of them are Pharo, but they have different audiences.
PharoCore is the image that the Pharo core developers work with and improve. This is the minimal image that runs (by loading the necessary dependencies first) your Smalltalk application. This is also the image that receives updates by means of the Software Update option on the menu inside the image. The test are run over this image also and the results are published on the pharo page for comparison and regression testing. This is the image that the Pharo project works on everyday.
Pharo is an image built using PharoCore as base and loading several handy and useful packages in order to create an appealing, nice, useful product for the people to use. There are several distributions of Pharo that load different packages aimed to distinct people. There is the pharo-dev that includes syntax-coloring, autocompletition, several browsers for the source code and in general packages aimed to developers of Smalltalk applications. There is also the pharo-web that is a pharo-dev but also adds packages as Seaside and Pier and is intended to web developers. These two are built by Damien Cassou. Other new distribution is the one from Torsten Bergmann, the pharo ready made setup that tries to ease the user experience by downloading just one archive to test and try Pharo (currently only for windows users). Also, in the works is a one-click distribution that can run in Windows, GNU/Linux and MacOS X. All of these can be downloaded from the
pharo download page.
The ParoCore is, as I already said, perfect for deploying applications to production, as it has a very small footprint. In subsecuent posts I will show how to deploy an app in a PharoCore image with just the required dependencies and nothing else.
Pharo, despite the number of changes that has integrated, it is very stable. As the 1.0 release aproaches, the pharo core developers focus in bug fixing and aren’t allowing new drastic changes. They are reserved for the 1.1 release, that will begin to prepare the next day following the 1.0 announce.
Other good sign is that the
Seaside core developers are already working daily on Pharo and some of then even have production sites running on Pharo without any problem. So if you are developing Seaside applications, as I am, this shows that the integration of Seaside with Pharo is very well tested.
Also, a book for Pharo will be announced in the next days, called Pharo by Example, by the same authors of
Squeak by Example.
Don’t wait for the upcoming announce of the 1.0 release of
Pharo go there right now and become a Pharoer.
So much time since the last post. Well, a lot of things happened. But now I have more free time and will update this more frecuently.
Yes, it is official.
We are going to have another baby.
The last weekend we went to the doctor and we saw the baby for the first time. He/she is 3 cm long and we heard also the sound of the heart. According to the doctor words everything is ok and we are very happy.
Aline is very tired all days and Tristán has more energy than ever but the happiness is bigger.
And we have the first pictures of the baby too :)
Un ciclo se ha cerrado para Aline, ayer fue su examen de titulación y, tal y como yo lo esperaba y Aline insistía en dudar, le fue muy bien. Más que bien, diría yo, ya que, de manera sorpresiva y sin buscarlo ni esperarlo, obtuvo una calificación aprobatoria con mención honorífica.
No hay palabras para expresar nuestra sorpresa con el anuncio. Literalmente todos abrimos los ojos al escuchar las palabras del jurado. Bueno, una excelente culminación para tan prolongado esfuerzo de su parte.
Pero este evento significa mucho más para la familia Cobá. Mucha presión, tensión y frustraciones mágicamente se dispersan y se convierten en recuerdos que día a día se volverán más tenues y desprovistos de la fuerza que en su momento tuvieron. Unas cuantas palabras del jurado marcan un antes y un después para Aline. Un antes que le pesaba constantemente como una cadena atada a su pie. Ahora ya no es así. Ya es libre, literalmente, de olvidarse de la filosofía, por decirlo de alguna manera, y de dedicarse a cosas nuevas, principalmente a lo que, en los últimos meses, se ha convertido en una nueva motivación: la curaduría.
Y yo estoy más que feliz, no sólo por el hecho de que haya terminado esa etapa de su vida, sino porque iniciamos ahora una nueva etapa juntos, los tres, Tristán incluido, que promete mucha felicidad.
Adelante nos esperan: curso de curaduría para Aline, de creación literaria para Aline y para mi, de verano para Tristán; tiempo para estar juntos y ver películas, ir al cine, ir por nuestros acostumbrados cafés, ir a los viveros, a Coyoacán, a Puebla, a Tepoztlán, otra vez a Cancún, a New York; solicitar las becas para Europa, las becas de arte para Aline; y lo que vaya saliendo. En fin, una atadura menos al pasado, mucha fuerza para seguir caminando juntos.
Por eso es que este evento es tan significativo. Por eso estamos celebrando. Por eso estamos felices.
Es más que obvio que en términos blog-ísticos, estoy en temporada de sequía.
Tengo tanto que decir y tan poco que decir. Una paradoja infranqueable que, cual muro de tres metros de alto, no le permite a mi pobre cerebro saltarla sin ayuda de una buena escalera.
Seguiremos esperando a que el foco prenda, o al menos, que alguien cambie los fusibles quemados.
Uff
Yo tuve muchos libros.
Hace tiempo ya de eso. Ya ni siquiera recuerdo cuántos eran ni de si eran buenos. Yo creo que sí, si no no los hubiera tenido. Tenía de todo. Historias reales, historias ficticias. Historias tristes e historias alegres. Historias de hombres y de bestias, de magia y de tragedia. Hasta de ciencia y de poesía. Y si no mal recuerdo, hasta tenía algunos de literatura erótica y de fotos de mujeres desnudas.
Maravillosos los libros que te arrancan de tu silla y te llevan a otros mundos. Te absorben de tu sillón y te llevan a llorar con otros y por otros. Te asustan en las noches, te convierten en heroe y villano, en guerrero y en cobarde. Provocan sensaciones en tu cuerpo que quizá nunca experimentarás en la vida real. Son tus aliadas y tus acusadoras. Pueden hacerte ver tus propias debilidades y deseos, tus perversiones y aspiraciones. Son denuncias públicas de tus secretos en palabras escritas por una persona que nunca conociste ni conocerás, pero que te conoce mejor de lo que tú te conoces.
Yo tuve muchos libros.
Ahora no tengo nada.
Ahora estoy desnudo, despojado, condenado. Ahora soy menos hombre porque ya no puedo leerlos. Ahora una parte de mí ha muerto, una parte del mundo no existe. Ahora sólo tengo recuerdos. Ahora sólo puedo tocarlos, abrirlos, olerlos y cerrarlos.
Ahora sólo tengo recuerdos.
Ahora estoy ciego y ya no tengo libros, sólo hojas secas manchadas de tinta incomprensible.