Cuando comencé a desarrollar este proyecto, no había ningún requisito para generar archivos de gran tamaño, sin embargo, ahora es un entregable.Cómo generar archivos de gran tamaño (PDF y CSV) utilizando AppEngine y Datastore?
En pocas palabras, GAE simplemente no funciona bien con cualquier manipulación de datos a gran escala o generación de contenido. La falta de almacenamiento de archivos a un lado, incluso algo tan simple como generar un pdf con ReportLab con 1500 registros parece llegar a DeadlineExceededError. Esto es solo un pdf simple compuesto por una tabla.
estoy usando el siguiente código:
self.response.headers['Content-Type'] = 'application/pdf'
self.response.headers['Content-Disposition'] = 'attachment; filename=output.pdf'
doc = SimpleDocTemplate(self.response.out, pagesize=landscape(letter))
elements = []
dataset = Voter.all().order('addr_str')
data = [['#', 'STREET', 'UNIT', 'PROFILE', 'PHONE', 'NAME', 'REPLY', 'YS', 'VOL', 'NOTES', 'MAIN ISSUE']]
i = 0
r = 1
s = 100
while (i < 1500):
voters = dataset.fetch(s, offset=i)
for voter in voters:
data.append([voter.addr_num, voter.addr_str, voter.addr_unit_num, '', voter.phone, voter.firstname+' '+voter.middlename+' '+voter.lastname ])
r = r + 1
i = i + s
t=Table(data, '', r*[0.4*inch], repeatRows=1)
t.setStyle(TableStyle([('ALIGN',(0,0),(-1,-1),'CENTER'),
('INNERGRID', (0,0), (-1,-1), 0.15, colors.black),
('BOX', (0,0), (-1,-1), .15, colors.black),
('FONTSIZE', (0,0), (-1,-1), 8)
]))
elements.append(t)
doc.build(elements)
Nada particularmente elegante, pero ahoga. ¿Hay una mejor manera de hacer esto? Si pudiera escribir en algún tipo de sistema de archivos y generar el archivo en bits, y luego volver a unirme a ellos podría funcionar, pero creo que el sistema lo impide.
Necesito hacer lo mismo para un archivo CSV, sin embargo, el límite obviamente es un poco más alto ya que es solo resultado bruto.
self.response.headers['Content-Type'] = 'application/csv'
self.response.headers['Content-Disposition'] = 'attachment; filename=output.csv'
dataset = Voter.all().order('addr_str')
writer = csv.writer(self.response.out,dialect='excel')
writer.writerow(['#', 'STREET', 'UNIT', 'PROFILE', 'PHONE', 'NAME', 'REPLY', 'YS', 'VOL', 'NOTES', 'MAIN ISSUE'])
i = 0
s = 100
while (i < 2000):
last_cursor = memcache.get('db_cursor')
if last_cursor:
dataset.with_cursor(last_cursor)
voters = dataset.fetch(s)
for voter in voters:
writer.writerow([voter.addr_num, voter.addr_str, voter.addr_unit_num, '', voter.phone, voter.firstname+' '+voter.middlename+' '+voter.lastname])
memcache.set('db_cursor', dataset.cursor())
i = i + s
memcache.delete('db_cursor')
Cualquier sugerencia sería muy apreciada.
Editar:
Por encima de que había documentado tres posibles soluciones sobre la base de mi investigación, además de sugerencias, etc.
No son necesariamente excluyentes entre sí, y podría haber una ligera variación o combinación de cualquiera de los tres, sin embargo, la esencia de las soluciones está ahí. Déjame saber cuál crees que tiene más sentido y podría funcionar mejor.
Solución A: utilizando mapreduce (o tareas), serialice cada registro y cree una entrada de Memcache para cada registro individual con la clave keyname. Luego procese estos elementos individualmente en el archivo pdf/xls. (use get_multi y set_multi)
Solución B: utilizando tareas, serialice grupos de registros y cárguelos en la base de datos como un blob. A continuación, active una tarea una vez que se hayan procesado todos los registros que cargarán cada blob, los deserializarán y luego cargarán los datos en el archivo final.
Solución C: utilizando mapreduce, recupere los nombres de los clave y guárdelos como una lista o blob serializado. A continuación, cargue los registros por clave, lo que sería más rápido que el método de carga actual. Si tuviera que hacer esto, que sería mejor, almacenarlos como una lista (y cuáles serían las limitaciones ... supongo que una lista de 100.000 estaría más allá de las capacidades del almacén de datos) o como un blob serializado (o pequeño). trozos que luego concatenar o procesar)
Gracias de antemano por cualquier consejo.
Probablemente una ineficiencia menor, pero data.append ([...]) será mucho más eficiente que data + = [[...]]. –
He editado el código para reflejar esto. ¡Gracias por el consejo! – etc