Память BigQuery UDF превысила ошибку в нескольких строках, но отлично работает в одной строке
Я пишу UDF для обработки данных Google Analytics и получаю сообщение об ошибке "UDF out of memory" при попытке обработать несколько строк. Я загрузил необработанные данные, нашел самую большую запись и попытался выполнить свой UDF-запрос по этому вопросу, но с успехом. Некоторые строки имеют до 500 вложенных попаданий, и размер записи попаданий (безусловно, самый большой компонент каждой строки необработанных данных GA), похоже, влияет на количество строк, которые я могу обработать до получения ошибки,
Например, запрос
select
user.ga_user_id,
ga_session_id,
...
from
temp_ga_processing(
select
fullVisitorId,
visitNumber,
...
from [79689075.ga_sessions_20160201] limit 100)
возвращает ошибку, но
from [79689075.ga_sessions_20160201] where totals.hits = 500 limit 1)
не.
У меня сложилось впечатление, что какие-то ограничения памяти были на ряд? Я пробовал несколько методов, таких как установка row = null;
до emit(return_dict);
(где return_dict - обработанные данные), но безрезультатно.
Сам UDF не делает ничего особенного; Я вставил бы это здесь, но это ~45 кБ в длину. По сути, он делает кучу вещей в соответствии с:
function temp_ga_processing(row, emit) {
topic_id = -1;
hit_numbers = [];
first_page_load_hits = [];
return_dict = {};
return_dict["user"] = {};
return_dict["user"]["ga_user_id"] = row.fullVisitorId;
return_dict["ga_session_id"] = row.fullVisitorId.concat("-".concat(row.visitNumber));
for(i=0;i<row.hits.length;i++) {
hit_dict = {};
hit_dict["page"] = {};
hit_dict["time"] = row.hits[i].time;
hit_dict["type"] = row.hits[i].type;
hit_dict["page"]["engaged_10s"] = false;
hit_dict["page"]["engaged_30s"] = false;
hit_dict["page"]["engaged_60s"] = false;
add_hit = true;
for(j=0;j<row.hits[i].customMetrics.length;j++) {
if(row.hits[i].customDimensions[j] != null) {
if(row.hits[i].customMetrics[j]["index"] == 3) {
metrics = {"video_play_time": row.hits[i].customMetrics[j]["value"]};
hit_dict["metrics"] = metrics;
metrics = null;
row.hits[i].customDimensions[j] = null;
}
}
}
hit_dict["topic"] = {};
hit_dict["doctor"] = {};
hit_dict["doctor_location"] = {};
hit_dict["content"] = {};
if(row.hits[i].customDimensions != null) {
for(j=0;j<row.hits[i].customDimensions.length;j++) {
if(row.hits[i].customDimensions[j] != null) {
if(row.hits[i].customDimensions[j]["index"] == 1) {
hit_dict["topic"] = {"name": row.hits[i].customDimensions[j]["value"]};
row.hits[i].customDimensions[j] = null;
continue;
}
if(row.hits[i].customDimensions[j]["index"] == 3) {
if(row.hits[i].customDimensions[j]["value"].search("doctor") > -1) {
return_dict["logged_in_as_doctor"] = true;
}
}
// and so on...
}
}
}
if(row.hits[i]["eventInfo"]["eventCategory"] == "page load time" && row.hits[i]["eventInfo"]["eventLabel"].search("OUTLIER") == -1) {
elre = /(?:onLoad|pl|page):(\d+)/.exec(row.hits[i]["eventInfo"]["eventLabel"]);
if(elre != null) {
if(parseInt(elre[0].split(":")[1]) <= 60000) {
first_page_load_hits.push(parseFloat(row.hits[i].hitNumber));
if(hit_dict["page"]["page_load"] == null) {
hit_dict["page"]["page_load"] = {};
}
hit_dict["page"]["page_load"]["sample"] = 1;
page_load_time_re = /(?:onLoad|pl|page):(\d+)/.exec(row.hits[i]["eventInfo"]["eventLabel"]);
if(page_load_time_re != null) {
hit_dict["page"]["page_load"]["page_load_time"] = parseFloat(page_load_time_re[0].split(':')[1])/1000;
}
}
// and so on...
}
}
row = null;
emit return_dict;
}
Идентификатор задания является реальным:bquijob_4c30bd3d_152fbfcd7fd
3 ответа
Обновление Авг 2016: Мы выпустили обновление, которое позволит работнику JavaScript использовать вдвое больше оперативной памяти. Мы продолжим отслеживать задания, потерпевшие неудачу с помощью JS OOM, чтобы выяснить, не нужно ли еще больше; А пока, пожалуйста, сообщите нам, если у вас не получится работать с OOM. Спасибо!
Обновление: эта проблема была связана с ограничениями, которые у нас были на размер кода UDF. Похоже, что проход V8 для оптимизации + перекомпиляции кода UDF генерирует сегмент данных, который был больше наших пределов, но это происходило только тогда, когда UDF пробегает "достаточное" количество строк. На этой неделе я встречаюсь с командой V8, чтобы углубиться в детали.
@Grayson - мне удалось успешно выполнить вашу работу за всем столом 20160201; выполнение запроса занимает 1-2 минуты. Не могли бы вы убедиться, что это работает на вашей стороне?
Мы получили несколько сообщений о похожих проблемах, которые, похоже, связаны с обработкой # строк. Я извиняюсь за беспокойство; Я проведу некоторое профилирование в нашей среде выполнения JavaScript, чтобы попытаться выяснить, происходит ли утечка памяти. Оставайтесь с нами для анализа.
В то же время, если вы сможете выделить какие-либо конкретные строки, которые вызывают ошибку, это также будет очень полезно.
UDF не будет работать ни с чем, кроме очень маленьких наборов данных, если у него много уровней if/then, таких как:
если () {
.... если() {
.........если () {
так далее
Мы должны были отследить и удалить самое глубокое утверждение if/then.
Но этого недостаточно. Кроме того, когда вы передаете данные в UDF, запустите "GROUP EACH BY" для всех переменных. Это заставит BQ отправить вывод нескольким "рабочим". Иначе это тоже не получится.
Я потратил 3 дня своей жизни на эту досадную ошибку. Argh.
Мне нравится концепция парсинга моих логов в BigQuery, но у меня та же проблема, я получаю
Ошибка: ресурсы превышены во время выполнения запроса.
Идентификатор задания выглядит так:bquijob_260be029_153dd96cfdb, если это вообще помогает.
Я написал очень простой парсер, который выполняет простое сопоставление и возвращает строки. Прекрасно работает с набором данных из 10К строк, но у меня не хватает ресурсов, когда я пытаюсь запустить файл журнала из 3М строк.
Любые предложения для обхода?
Вот код JavaScript.
function parseLogRow(row, emit) {
r = (row.logrow ? row.logrow : "") + (typeof row.l2 !== "undefined" ? " " + row.l2 : "") + (row.l3 ? " " + row.l3 : "")
ts = null
category = null
user = null
message = null
db = null
found = false
if (r) {
m = r.match(/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d (\+|\-)\d\d\d\d) \[([^|]*)\|([^|]*)\|([^\]]*)\] :: (.*)/ )
if( m){
ts = new Date(m[1])/1000
category = m[3] || null
user = m[4] || null
db = m[5] || null
message = m[6] || null
found = true
}
else {
message = r
found = false
}
}
emit({
ts: ts,
category: category,
user: user,
db: db,
message: message,
found: found
});
}
bigquery.defineFunction(
'parseLogRow', // Name of the function exported to SQL
['logrow',"l2","l3"], // Names of input columns
[
{'name': 'ts', 'type': 'timestamp'}, // Output schema
{'name': 'category', 'type': 'string'},
{'name': 'user', 'type': 'string'},
{'name': 'db', 'type': 'string'},
{'name': 'message', 'type': 'string'},
{'name': 'found', 'type': 'boolean'},
],
parseLogRow // Reference to JavaScript UDF
);