Skip to content

Commit

Permalink
Operate with time.Duration offset instead of fixed timestamp.
Browse files Browse the repository at this point in the history
  • Loading branch information
fbarl committed May 29, 2017
1 parent 91802ef commit 865ba9a
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 122 deletions.
4 changes: 2 additions & 2 deletions app/api_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
// Raw report handler
func makeRawReportHandler(rep Reporter) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
report, err := rep.Report(ctx, time.Now())
report, err := rep.Report(ctx, 0)
if err != nil {
respondWith(w, http.StatusInternalServerError, err)
return
Expand All @@ -32,7 +32,7 @@ type probeDesc struct {
// Probe handler
func makeProbeHandler(rep Reporter) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
rpt, err := rep.Report(ctx, time.Now())
rpt, err := rep.Report(ctx, 0)
if err != nil {
respondWith(w, http.StatusInternalServerError, err)
return
Expand Down
5 changes: 2 additions & 3 deletions app/api_topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"sort"
"strings"
"sync"
"time"

log "github.com/Sirupsen/logrus"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -477,7 +476,7 @@ func (r *Registry) walk(f func(APITopologyDesc)) {
// makeTopologyList returns a handler that yields an APITopologyList.
func (r *Registry) makeTopologyList(rep Reporter) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
report, err := rep.Report(ctx, time.Now())
report, err := rep.Report(ctx, 0)
if err != nil {
respondWith(w, http.StatusInternalServerError, err)
return
Expand Down Expand Up @@ -569,7 +568,7 @@ func (r *Registry) captureRenderer(rep Reporter, f rendererHandler) CtxHandlerFu
http.NotFound(w, req)
return
}
rpt, err := rep.Report(ctx, time.Now())
rpt, err := rep.Report(ctx, 0)
if err != nil {
respondWith(w, http.StatusInternalServerError, err)
return
Expand Down
11 changes: 6 additions & 5 deletions app/api_topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,19 @@ func handleWebsocket(
timestamp = time.Now()
)

log.Debugf("BLUBLUBLUBLU")
if timestampStr := r.Form.Get("timestamp"); timestampStr != "" {
const ISO8601UTC = "2006-01-02T15:04:05Z"
timestamp, _ = time.Parse(ISO8601UTC, timestampStr)
log.Debugf("BLUBLUBLUBLU: %s %v", timestampStr, timestamp)
// Override the default current timestamp by the ISO8601 one explicitly provided by the UI.
timestamp, _ = time.Parse(time.RFC3339, timestampStr)
}
// Use the time offset instead of a timestamp here so that the value
// can stay constant when simulating past reports (with normal speed).
timeOffset := time.Since(timestamp)

rep.WaitOn(ctx, wait)
defer rep.UnWait(ctx, wait)

for {
report, err := rep.Report(ctx, timestamp)
report, err := rep.Report(ctx, timeOffset)
if err != nil {
log.Errorf("Error generating report: %v", err)
return
Expand Down
8 changes: 4 additions & 4 deletions app/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const reportQuantisationInterval = 3 * time.Second
// Reporter is something that can produce reports on demand. It's a convenient
// interface for parts of the app, and several experimental components.
type Reporter interface {
Report(context.Context, time.Time) (report.Report, error)
Report(context.Context, time.Duration) (report.Report, error)
WaitOn(context.Context, chan struct{})
UnWait(context.Context, chan struct{})
}
Expand Down Expand Up @@ -118,14 +118,14 @@ func (c *collector) Add(_ context.Context, rpt report.Report, _ []byte) error {

// Report returns a merged report over all added reports. It implements
// Reporter.
func (c *collector) Report(_ context.Context, moment time.Time) (report.Report, error) {
func (c *collector) Report(_ context.Context, timeOffset time.Duration) (report.Report, error) {
c.mtx.Lock()
defer c.mtx.Unlock()

// If the oldest report is still within range,
// and there is a cached report, return that.
if c.cached != nil && len(c.reports) > 0 {
oldest := moment.Add(-c.window)
oldest := mtime.Now().Add(-timeOffset - c.window)
if c.timestamps[0].After(oldest) {
return *c.cached, nil
}
Expand Down Expand Up @@ -191,7 +191,7 @@ type StaticCollector report.Report

// Report returns a merged report over all added reports. It implements
// Reporter.
func (c StaticCollector) Report(context.Context, time.Time) (report.Report, error) {
func (c StaticCollector) Report(context.Context, time.Duration) (report.Report, error) {
return report.Report(c), nil
}

Expand Down
4 changes: 2 additions & 2 deletions app/multitenant/aws_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,9 @@ func (c *awsCollector) getReports(ctx context.Context, reportKeys []string) ([]r
return reports, nil
}

func (c *awsCollector) Report(ctx context.Context, pointInTime time.Time) (report.Report, error) {
func (c *awsCollector) Report(ctx context.Context, timeOffset time.Duration) (report.Report, error) {
var (
end = pointInTime
end = time.Now().Add(-timeOffset)
start = end.Add(-c.window)
rowStart = start.UnixNano() / time.Hour.Nanoseconds()
rowEnd = end.UnixNano() / time.Hour.Nanoseconds()
Expand Down
3 changes: 1 addition & 2 deletions app/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"net/url"
"strings"
"sync"
"time"

"github.com/PuerkitoBio/ghost/handlers"
log "github.com/Sirupsen/logrus"
Expand Down Expand Up @@ -180,7 +179,7 @@ func NewVersion(version, downloadURL string) {

func apiHandler(rep Reporter) CtxHandlerFunc {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
report, err := rep.Report(ctx, time.Now())
report, err := rep.Report(ctx, 0)
if err != nil {
respondWith(w, http.StatusInternalServerError, err)
return
Expand Down
2 changes: 1 addition & 1 deletion client/app/scripts/charts/nodes-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ export function doLayout(immNodes, immEdges, opts) {
const cacheId = buildTopologyCacheId(options.topologyId, options.topologyOptions);

// one engine and node and edge caches per topology, to keep renderings similar
if (options.noCache || !topologyCaches[cacheId]) {
if (true || options.noCache || !topologyCaches[cacheId]) {
topologyCaches[cacheId] = {
nodeCache: makeMap(),
edgeCache: makeMap(),
Expand Down
90 changes: 90 additions & 0 deletions client/app/scripts/components/editable-time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import moment from 'moment';
import { connect } from 'react-redux';

import { ENTER_KEY_CODE } from '../constants/key-codes';
import { changeTopologyTimestamp } from '../actions/app-actions';


class EditableTime extends React.PureComponent {
constructor(props, context) {
super(props, context);

this.state = {
value: '',
editing: false,
timeOffset: 0,
};

this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.handleChange = this.handleChange.bind(this);
this.saveInputRef = this.saveInputRef.bind(this);
}

componentDidMount() {
this.timer = setInterval(() => {
if (!this.state.editing) {
this.setState(this.getFreshState());
}
}, 100);
}

componentWillUnmount() {
clearInterval(this.timer);
}

handleFocus() {
this.setState({ editing: true });
}

handleBlur() {
this.setState({ editing: false });
}

handleChange(ev) {
this.setState({ value: ev.target.value });
}

handleKeyUp(ev) {
if (ev.keyCode === ENTER_KEY_CODE) {
const { value } = this.state;
const timeOffset = moment.duration(moment().diff(value));
this.props.changeTopologyTimestamp(value);

this.setState({ timeOffset });
this.inputRef.blur();

console.log('QUERY TOPOLOGY AT: ', value, timeOffset);
}
}

saveInputRef(ref) {
this.inputRef = ref;
}

getFreshState() {
const { timeOffset } = this.state;
const time = moment().utc().subtract(timeOffset);
return { value: time.toISOString() };
}

render() {
return (
<span className="running-time">
<input
type="text"
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onKeyUp={this.handleKeyUp}
onChange={this.handleChange}
value={this.state.value}
ref={this.saveInputRef}
/>
</span>
);
}
}

export default connect(null, { changeTopologyTimestamp })(EditableTime);
33 changes: 1 addition & 32 deletions client/app/scripts/components/footer.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import React from 'react';
import { connect } from 'react-redux';
import moment from 'moment';

import Plugins from './plugins';
import { getUpdateBufferSize } from '../utils/update-buffer-utils';
import { trackMixpanelEvent } from '../utils/tracking-utils';
import {
clickDownloadGraph,
clickForceRelayout,
clickPauseUpdate,
clickResumeUpdate,
toggleHelp,
toggleTroubleshootingMenu,
setContrastMode
Expand Down Expand Up @@ -38,38 +34,18 @@ class Footer extends React.Component {
}

render() {
const { hostname, updatePausedAt, version, versionUpdate, contrastMode } = this.props;
const { hostname, version, versionUpdate, contrastMode } = this.props;

const otherContrastModeTitle = contrastMode
? 'Switch to normal contrast' : 'Switch to high contrast';
const forceRelayoutTitle = 'Force re-layout (might reduce edge crossings, '
+ 'but may shift nodes around)';

// pause button
const isPaused = updatePausedAt !== null;
const updateCount = getUpdateBufferSize();
const hasUpdates = updateCount > 0;
const pausedAgo = moment(updatePausedAt).fromNow();
const pauseTitle = isPaused
? `Paused ${pausedAgo}` : 'Pause updates (freezes the nodes in their current layout)';
const pauseAction = isPaused ? this.props.clickResumeUpdate : this.props.clickPauseUpdate;
const pauseClassName = isPaused ? 'footer-icon footer-icon-active' : 'footer-icon';
let pauseLabel = '';
if (hasUpdates && isPaused) {
pauseLabel = `Paused +${updateCount}`;
} else if (hasUpdates && !isPaused) {
pauseLabel = `Resuming +${updateCount}`;
} else if (!hasUpdates && isPaused) {
pauseLabel = 'Paused';
}

const versionUpdateTitle = versionUpdate
? `New version available: ${versionUpdate.version}. Click to download`
: '';

return (
<div className="footer">

<div className="footer-status">
{versionUpdate && <a
className="footer-versionupdate"
Expand All @@ -89,10 +65,6 @@ class Footer extends React.Component {
</div>

<div className="footer-tools">
<a className={pauseClassName} onClick={pauseAction} title={pauseTitle}>
{pauseLabel !== '' && <span className="footer-label">{pauseLabel}</span>}
<span className="fa fa-pause" />
</a>
<a
className="footer-icon"
onClick={this.handleRelayoutClick}
Expand Down Expand Up @@ -122,7 +94,6 @@ class Footer extends React.Component {
function mapStateToProps(state) {
return {
hostname: state.get('hostname'),
updatePausedAt: state.get('updatePausedAt'),
topologyViewMode: state.get('topologyViewMode'),
version: state.get('version'),
versionUpdate: state.get('versionUpdate'),
Expand All @@ -135,8 +106,6 @@ export default connect(
{
clickDownloadGraph,
clickForceRelayout,
clickPauseUpdate,
clickResumeUpdate,
toggleHelp,
toggleTroubleshootingMenu,
setContrastMode
Expand Down
Loading

0 comments on commit 865ba9a

Please sign in to comment.