BigQuery UDF内存超出了多行的错误,但在单行上工作正常

时间:2016-02-20 02:17:46

标签: google-bigquery

我正在编写UDF来处理Google Analytics数据,并在尝试处理多行时收到“UDF内存不足”错误消息。我下载了原始数据并找到了最大的记录,并尝试运行我的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 kB。它基本上做了很多事情:

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;
}

作业ID是真实的:bquijob_4c30bd3d_152fbfcd7fd

3 个答案:

答案 0 :(得分:1)

2016年8月更新:我们推出了一项更新,允许JavaScript工作人员使用两倍的RAM。我们将继续监控JS OOM失败的工作,看看是否需要增加更多工作;在此期间,如果您有其他工作因OOM失败,请告诉我们。谢谢!

更新:此问题与我们对UDF代码大小的限制有关。看起来V8的UDF代码的优化+重新编译通道会生成一个比我们的限制更大的数据段,但这只发生在UDF运行时足够的时候#34;行数。我本周会与V8团队会面,进一步深入了解细节。

@Grayson - 我能够成功地在整个20160201表上运行您的工作;查询需要1-2分钟才能执行。你能否证实这对你有用?

我们收到了一些类似问题的报告,这些问题似乎与处理过的#行有关。对不起,我很抱歉;我将对我们的JavaScript运行时进行一些分析,以尝试查找内存是否泄漏以及泄漏的位置。请继续关注分析。

与此同时,如果您能够隔离导致错误的任何特定行,那么这也会非常有用。

答案 1 :(得分:0)

如果UDF具有大量的if / then级别,UDF将在除了非常小的数据集之外的任何内容上失败,例如:
if(){
.... if(){
......... if(){

我们必须追踪并删除最深的if / then语句。

但是,这还不够。此外,当您将数据传递到UDF时,在所有变量上运行“GROUP EACH BY”。这将迫使BQ将输出发送给多个“工人”。否则它也会失败。

我浪费了3天的时间来讨论这个烦人的错误。哎呀。

答案 2 :(得分:0)

我喜欢在BigQuery中解析我的日志的概念,但是我遇到了同样的问题,我得到了

  

错误:查询执行期间超出了资源。

作业ID是bigquery-looker:bquijob_260be029_153dd96cfdb,如果这一点有帮助。

我写了一个非常基本的解析器做了一个简单的匹配并返回行。在10K行数据集上工作正常,但在尝试针对3M行日志文件运行时,我的资源就会耗尽。

有关解决方案的任何建议吗?

这是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
);