static void ipc_record_alert_receive_delay(ngx_uint_t delay) {
  delayed_received_alerts_count ++;
  delayed_received_alerts_delay += delay;
  nchan_update_stub_status(ipc_total_receive_delay, delay);
  if(!receive_alert_delay_log_timer.timer_set && !ngx_exiting && !ngx_quit) {
    ngx_add_timer(&receive_alert_delay_log_timer, 1000);
  }
}
static void ipc_read_handler(ngx_event_t *ev) {
  DBG("IPC channel handler");
  //copypasta from os/unix/ngx_process_cycle.c (ngx_channel_handler)
  ngx_int_t          n;
  ipc_alert_t        alert;
  ngx_connection_t  *c;
  if (ev->timedout) {
    ev->timedout = 0;
    return;
  }
  c = ev->data;
  
  while(1) {
    n = ipc_read_socket(c->fd, &alert, ev->log);
    if (n == NGX_ERROR) {
      ERR("IPC_READ_SOCKET failed: bad connection. This should never have happened, yet here we are...");
      assert(0);
      return;
    }
    if (n == NGX_AGAIN) {
      return;
    }
    //ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "nchan: channel command: %d", ch.command);
    
    assert(n == sizeof(alert));
    if(alert.worker_generation < memstore_worker_generation) {
      ERR("Got IPC alert for previous generation's worker. discarding.");
    }
    else {
#if DEBUG_DELAY_IPC_RECEIVE_ALERT_MSEC
      delayed_alert_glob_t   *glob = ngx_alloc(sizeof(*glob), ngx_cycle->log);
      if (NULL == glob) {
          ERR("Couldn't allocate memory for alert glob data.");
          return;
      }
      ngx_memzero(&glob->timer, sizeof(glob->timer));
      nchan_init_timer(&glob->timer, fake_ipc_alert_delay_handler, glob);
      
      glob->alert = alert;
      glob->ipc = (ipc_t *)c->data;
      ngx_add_timer(&glob->timer, DEBUG_DELAY_IPC_RECEIVE_ALERT_MSEC);
#else
      if(ngx_time() - alert.time_sent >= 2) {
        ipc_record_alert_receive_delay(ngx_time() - alert.time_sent);
      }
      nchan_update_stub_status(ipc_total_alerts_received, 1);
      ((ipc_t *)c->data)->handler(alert.src_slot, alert.code, alert.data);
#endif
    }
  }
}
Exemple #3
0
static void nchan_publisher_post_request(ngx_http_request_t *r, ngx_str_t *content_type, size_t content_length, ngx_chain_t *request_body_chain, ngx_str_t *channel_id, nchan_loc_conf_t *cf) {
  ngx_buf_t                      *buf;
  nchan_msg_t                    *msg;
  ngx_str_t                      *eventsource_event;
  
  safe_request_ptr_t             *pd;

#if FAKESHARD
  memstore_pub_debug_start();
#endif
  if((msg = ngx_pcalloc(r->pool, sizeof(*msg))) == NULL) {
    nchan_log_request_error(r, "can't allocate msg in request pool");
    nchan_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    return; 
  }
  msg->storage = NCHAN_MSG_POOL;
  
  
  if(cf->eventsource_event.len > 0) {
    msg->eventsource_event = &cf->eventsource_event;
  }
  else if((eventsource_event = nchan_get_header_value(r, NCHAN_HEADER_EVENTSOURCE_EVENT)) != NULL) {
    msg->eventsource_event = eventsource_event;
  }
  
  //content type
  if(content_type) {
    msg->content_type = content_type;
  }
  
  if(content_length == 0) {
    buf = ngx_create_temp_buf(r->pool, 0);
  }
  else if(request_body_chain!=NULL) {
    buf = nchan_chain_to_single_buffer(r->pool, request_body_chain, content_length);
  }
  else {
    nchan_log_request_error(r, "unexpected publisher message request body buffer location. please report this to the nchan developers.");
    nchan_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    return;
  }
  
  msg->id.time = 0;
  msg->id.tag.fixed[0] = 0;
  msg->id.tagactive = 0;
  msg->id.tagcount = 1;
  
  msg->buf = *buf;
#if NCHAN_MSG_LEAK_DEBUG
  msg->lbl = r->uri;
#endif
#if NCHAN_BENCHMARK
  nchan_request_ctx_t            *ctx = ngx_http_get_module_ctx(r, ngx_nchan_module);
  msg->start_tv = ctx->start_tv;
#endif
  nchan_deflate_message_if_needed(msg, cf, r, r->pool);
  if((pd = nchan_set_safe_request_ptr(r)) == NULL) {
    return;
  }
  
  cf->storage_engine->publish(channel_id, msg, cf, (callback_pt) &publish_callback, pd);
  nchan_update_stub_status(total_published_messages, 1);
#if FAKESHARD
  memstore_pub_debug_end();
#endif
}
ngx_int_t ipc_alert(ipc_t *ipc, ngx_int_t slot, ngx_uint_t code, void *data, size_t data_size) {
  DBG("IPC send alert code %i to slot %i", code, slot);
  
  if(data_size > IPC_DATA_SIZE) {
    ERR("IPC_DATA_SIZE too small. wanted %i, have %i", data_size, IPC_DATA_SIZE);
    assert(0);
  }
  nchan_update_stub_status(ipc_total_alerts_sent, 1);
#if (FAKESHARD)
  
  ipc_alert_t         alert = {0};
  
  alert.src_slot = memstore_slot();
  alert.time_sent = ngx_time();
  alert.worker_generation = memstore_worker_generation;
  alert.code = code;
  ngx_memcpy(alert.data, data, data_size);
  
  //switch to destination
  memstore_fakeprocess_push(slot);
  ipc->handler(alert.src_slot, alert.code, alert.data);
  memstore_fakeprocess_pop();
  //switch back  
  
#else
  
  ipc_process_t      *proc = &ipc->process[slot];
  ipc_writebuf_t     *wb = &proc->wbuf;
  ipc_alert_t        *alert;
  
  assert(proc->active);
  
  nchan_update_stub_status(ipc_queue_size, 1);
  
  if(wb->n < IPC_WRITEBUF_SIZE) {
    alert = &wb->alerts[(wb->first + wb->n++) % IPC_WRITEBUF_SIZE];
  }
  else { //overflow
    ipc_writebuf_overflow_t  *overflow;
    DBG("writebuf overflow, allocating memory");
    if((overflow = ngx_alloc(sizeof(*overflow), ngx_cycle->log)) == NULL) {
      ERR("can't allocate memory for IPC write buffer overflow");
      return NGX_ERROR;
    }
    overflow->next = NULL;
    alert= &overflow->alert;
    
    if(wb->overflow_first == NULL) {
      wb->overflow_first = overflow;
    }
    if(wb->overflow_last) {
      wb->overflow_last->next = overflow;
    }
    wb->overflow_last = overflow;
  
    wb->overflow_n++;
  }
  
  alert->src_slot = ngx_process_slot;
  alert->time_sent = ngx_time();
  alert->code = code;
  alert->worker_generation = memstore_worker_generation;
  ngx_memcpy(&alert->data, data, data_size);
  
  ipc_write_handler(proc->c->write);
  
  //ngx_handle_write_event(ipc->c[slot]->write, 0);
  //ngx_add_event(ipc->c[slot]->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT);
  
#endif

  return NGX_OK;
}
static void ipc_write_handler(ngx_event_t *ev) {
  ngx_connection_t        *c = ev->data;
  ngx_socket_t             fd = c->fd;
  
  ipc_process_t           *proc = (ipc_process_t *) c->data;
  ipc_alert_t             *alerts = proc->wbuf.alerts;
  
  int                      n = proc->wbuf.n;
  int                      i, first = proc->wbuf.first, last = first + n;
  uint8_t                  write_aborted = 0;
  
  //DBG("%i alerts to write, with %i in overflow", proc->wbuf.n, proc->wbuf.overflow_n);
  
  if(!memstore_ready()) {
#if nginx_version >= 1008000
    ev->cancelable = 1;
#endif
    ngx_add_timer(ev, 1000);
    return;
  }
#if nginx_version >= 1008000
  else {
    ev->cancelable = 0;
  }
#endif
  
  for(i = first; i < last; i++) {
    //ERR("send alert at %i", i % IPC_WRITEBUF_SIZE );
    if(ipc_write_alert_fd(fd, &alerts[i % IPC_WRITEBUF_SIZE]) != NGX_OK) {
      write_aborted = 1;
      //DBG("write aborted at %i iter. first: %i, n: %i", i - first, first, proc->wbuf.n);
      break;
    }
    /*
    else {
      DBG("wrote alert at %i", i % IPC_WRITEBUF_SIZE);
    }
    */
  }
  
  if(i==last) { //sent all outstanding alerts
    //DBG("finished writing %i alerts.", proc->wbuf.n);
    proc->wbuf.first = 0; // for debugging and stuff
    proc->wbuf.n = 0;
  }
  else {
    proc->wbuf.first = i;
    proc->wbuf.n -= (i - first);
    //DBG("first now at %i, %i alerts remain", i, proc->wbuf.n);
  }
  
  nchan_update_stub_status(ipc_queue_size, proc->wbuf.n - n);
  
  if(proc->wbuf.overflow_n > 0 && i - first > 0) {
    ipc_writebuf_overflow_t  *of;
    first = proc->wbuf.first + proc->wbuf.n;
    last = first + (IPC_WRITEBUF_SIZE - proc->wbuf.n);
    //DBG("try to squeeze in overflow between %i and %i", first, last);
    for(i = first; i < last; i++) {
      of = proc->wbuf.overflow_first;
      ///DBG("looking at overflow %p next %p", of, of->next);
      alerts[i % IPC_WRITEBUF_SIZE] = of->alert;
      proc->wbuf.overflow_n--;
      proc->wbuf.n++;
      assert(proc->wbuf.overflow_n >= 0);
      
      proc->wbuf.overflow_first = of->next;
      
      ngx_free(of);
      
      //DBG("squeezed in overflow at %i, %i overflow remaining", i, proc->wbuf.overflow_n);
      
      if(proc->wbuf.overflow_first == NULL) {
        proc->wbuf.overflow_last = NULL;
        break;
      }
    }
    
    if(!write_aborted) {
      //retry
      //DBG("retry write after squeezing in overflow");
      ipc_write_handler(ev);
      return;
    }
    
  }
  
  if(write_aborted) {
    //DBG("re-add event because the write failed");
    ngx_handle_write_event(c->write, 0);
  }
}