Skip to content

Commit a1f5a6a

Browse files
committed
WIP: refactor to use instance id
1 parent 62eee3d commit a1f5a6a

File tree

2 files changed

+59
-41
lines changed

2 files changed

+59
-41
lines changed

components/ws-proxy/pkg/proxy/infoprovider.go

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ type WorkspaceInfo struct {
7878
// (parsed from URL)
7979
IDEPublicPort string
8080

81-
Ports []PortInfo
82-
Auth *wsapi.WorkspaceAuthentication
83-
StartedAt time.Time
81+
Ports []PortInfo
82+
Auth *wsapi.WorkspaceAuthentication
83+
Phase wsapi.WorkspacePhase
8484
}
8585

8686
// PortInfo contains all information ws-proxy needs to know about a workspace port
@@ -266,7 +266,7 @@ func (p *RemoteWorkspaceInfoProvider) listen(client wsapi.WorkspaceManagerClient
266266
}
267267

268268
if status.Phase == wsapi.WorkspacePhase_STOPPED {
269-
p.cache.Delete(status.Metadata.MetaId, status.Metadata.StartedAt.AsTime())
269+
p.cache.Delete(status.Metadata.MetaId, status.Id)
270270
} else {
271271
info := mapWorkspaceStatusToInfo(status)
272272
p.cache.Insert(info)
@@ -310,11 +310,11 @@ func mapWorkspaceStatusToInfo(status *wsapi.WorkspaceStatus) *WorkspaceInfo {
310310
IDEPublicPort: getPortStr(status.Spec.Url),
311311
Ports: portInfos,
312312
Auth: status.Auth,
313-
StartedAt: status.Metadata.StartedAt.AsTime(),
313+
Phase: status.Phase,
314314
}
315315
}
316316

317-
// WorkspaceInfo return the WorkspaceInfo avaiable for the given workspaceID.
317+
// WorkspaceInfo return the WorkspaceInfo available for the given workspaceID.
318318
// Callers should make sure their context gets canceled properly. For good measure
319319
// this function will timeout by itself as well.
320320
func (p *RemoteWorkspaceInfoProvider) WorkspaceInfo(ctx context.Context, workspaceID string) *WorkspaceInfo {
@@ -360,7 +360,7 @@ func getPortStr(urlStr string) string {
360360
// workspaceInfoCache stores WorkspaceInfo in a manner which is easy to query for WorkspaceInfoProvider
361361
type workspaceInfoCache struct {
362362
// WorkspaceInfos indexed by workspaceID
363-
infos map[string]*WorkspaceInfo
363+
infos map[string][]*WorkspaceInfo
364364
// WorkspaceCoords indexed by public (proxy) port (string)
365365
coordsByPublicPort map[string]*WorkspaceCoords
366366

@@ -373,7 +373,7 @@ type workspaceInfoCache struct {
373373
func newWorkspaceInfoCache() *workspaceInfoCache {
374374
var mu sync.RWMutex
375375
return &workspaceInfoCache{
376-
infos: make(map[string]*WorkspaceInfo),
376+
infos: make(map[string][]*WorkspaceInfo),
377377
coordsByPublicPort: make(map[string]*WorkspaceCoords),
378378
mu: &mu,
379379
cond: sync.NewCond(&mu),
@@ -384,7 +384,7 @@ func (c *workspaceInfoCache) Reinit(infos []*WorkspaceInfo) {
384384
c.cond.L.Lock()
385385
defer c.cond.L.Unlock()
386386

387-
c.infos = make(map[string]*WorkspaceInfo, len(infos))
387+
c.infos = make(map[string][]*WorkspaceInfo, len(infos))
388388
c.coordsByPublicPort = make(map[string]*WorkspaceCoords, len(c.coordsByPublicPort))
389389

390390
for _, info := range infos {
@@ -402,19 +402,17 @@ func (c *workspaceInfoCache) Insert(info *WorkspaceInfo) {
402402
}
403403

404404
func (c *workspaceInfoCache) doInsert(info *WorkspaceInfo) {
405-
existing := c.infos[info.WorkspaceID]
406-
if existing != nil {
407-
// Do not insert if the current workspace is newer
408-
// This works around issues when a workspace is deleted then restarted in quick succession
409-
// and the stopping event occurs after the replocement pod is started
410-
if !existing.StartedAt.IsZero() && !info.StartedAt.IsZero() {
411-
if existing.StartedAt.After(info.StartedAt) {
412-
log.WithField("workspaceID", existing.WorkspaceID).Debug("ignoring insert of older workspace")
413-
return
414-
}
405+
existingInfos := c.infos[info.WorkspaceID]
406+
// Only insert if this is a new instance
407+
for i, existingInfo := range existingInfos {
408+
if existingInfo.InstanceID == info.InstanceID {
409+
c.infos[info.WorkspaceID][i] = info
410+
log.WithField("info", info).Debug("ignoring insert of existing instance for workspace")
411+
return
415412
}
416413
}
417-
c.infos[info.WorkspaceID] = info
414+
log.WithField("workspaceID", info.WorkspaceID).WithField("instanceID", info.InstanceID).Debug("adding instance for workspace")
415+
c.infos[info.WorkspaceID] = append(c.infos[info.WorkspaceID], info)
418416
c.coordsByPublicPort[info.IDEPublicPort] = &WorkspaceCoords{
419417
ID: info.WorkspaceID,
420418
}
@@ -427,34 +425,57 @@ func (c *workspaceInfoCache) doInsert(info *WorkspaceInfo) {
427425
}
428426
}
429427

430-
func (c *workspaceInfoCache) Delete(workspaceID string, startedAt time.Time) {
428+
func (c *workspaceInfoCache) Delete(workspaceID string, instanceID string) {
431429
c.cond.L.Lock()
432430
defer c.cond.L.Unlock()
433431

434-
info, present := c.infos[workspaceID]
435-
if !present || info == nil {
432+
infos, present := c.infos[workspaceID]
433+
if !present || infos == nil {
436434
return
437435
}
438-
// Do not delete if the current workspace info is newer
439-
// This works around issues when a workspace is deleted then restarted in quick succession
440-
// and the delete event occurs after the replocement pod is started
441-
if !startedAt.IsZero() && !info.StartedAt.IsZero() {
442-
if info.StartedAt.After(startedAt) {
443-
log.WithField("workspaceID", workspaceID).WithField("startedAt", startedAt).WithField("info", info).Debug("ignoring delete of older workspace")
436+
// Delete only the matching instance ID
437+
for _, info := range infos {
438+
if info.InstanceID == instanceID {
439+
log.WithField("workspaceID", workspaceID).WithField("instanceID", instanceID).Debug("deleting instance for workspace")
440+
delete(c.infos, instanceID)
441+
delete(c.coordsByPublicPort, info.IDEPublicPort)
444442
return
445443
}
446444
}
447-
delete(c.coordsByPublicPort, info.IDEPublicPort)
448-
delete(c.infos, workspaceID)
445+
log.WithField("workspaceID", workspaceID).WithField("instanceID", instanceID).Debug("ignoring delete of missing instance for workspace")
449446
}
450447

451448
// WaitFor waits for workspace info until that info is available or the context is canceled.
452449
func (c *workspaceInfoCache) WaitFor(ctx context.Context, workspaceID string) (w *WorkspaceInfo, ok bool) {
453450
c.mu.RLock()
454-
w, ok = c.infos[workspaceID]
451+
existing, ok := c.infos[workspaceID]
455452
c.mu.RUnlock()
453+
454+
getCandidate := func(infos []*WorkspaceInfo) *WorkspaceInfo {
455+
// Bail early...
456+
if len(infos) == 1 {
457+
return infos[0]
458+
}
459+
// Find the *best* candidate i.e. prefer any instance ramping up over stopping/stopped
460+
// TODO(rl): make this a smarter container? i.e. priorised by (smart) status
461+
candidate := infos[0]
462+
for _, info := range infos[1:] {
463+
if info.Phase < wsapi.WorkspacePhase_INTERRUPTED {
464+
if candidate.Phase > info.Phase {
465+
candidate = info
466+
}
467+
} else {
468+
// Prefer interrupted
469+
if candidate.Phase < info.Phase {
470+
candidate = info
471+
}
472+
}
473+
}
474+
return candidate
475+
}
476+
456477
if ok {
457-
return
478+
return getCandidate(existing), true
458479
}
459480

460481
inc := make(chan *WorkspaceInfo)
@@ -469,12 +490,12 @@ func (c *workspaceInfoCache) WaitFor(ctx context.Context, workspaceID string) (w
469490
return
470491
}
471492

472-
info, ok := c.infos[workspaceID]
493+
infos, ok := c.infos[workspaceID]
473494
if !ok {
474495
continue
475496
}
476497

477-
inc <- info
498+
inc <- getCandidate(infos)
478499
return
479500
}
480501
}()

components/ws-proxy/pkg/proxy/infoprovider_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/google/go-cmp/cmp"
1616
"github.com/google/go-cmp/cmp/cmpopts"
1717
"google.golang.org/grpc"
18-
"google.golang.org/protobuf/types/known/timestamppb"
1918

2019
wsapi "github.com/gitpod-io/gitpod/ws-manager/api"
2120
wsmock "github.com/gitpod-io/gitpod/ws-manager/api/mock"
@@ -168,7 +167,7 @@ func TestRemoteInfoProvider(t *testing.T) {
168167
if step.Action != nil {
169168
act := step.Action(t, prov)
170169
if diff := cmp.Diff(step.Expectation, act, cmpopts.IgnoreUnexported(wsapi.PortSpec{}, wsapi.WorkspaceAuthentication{})); diff != "" {
171-
t.Errorf("Expectation mismatch (-want +got):\n%s", diff)
170+
t.Errorf("%s Expectation mismatch (-want +got):\n%s", t.Name(), diff)
172171
}
173172
}
174173
})
@@ -179,12 +178,10 @@ func TestRemoteInfoProvider(t *testing.T) {
179178
}
180179

181180
var (
182-
startedAt = time.Date(2021, 1, 1, 00, 00, 00, 00, time.UTC)
183181
testWorkspaceStatus = &wsapi.WorkspaceStatus{
184182
Id: "e63cb5ff-f4e4-4065-8554-b431a32c0000",
185183
Metadata: &wsapi.WorkspaceMetadata{
186-
MetaId: "e63cb5ff-f4e4-4065-8554-b431a32c2714",
187-
StartedAt: timestamppb.New(startedAt),
184+
MetaId: "e63cb5ff-f4e4-4065-8554-b431a32c2714",
188185
},
189186
Auth: &wsapi.WorkspaceAuthentication{
190187
Admission: wsapi.AdmissionLevel_ADMIT_OWNER_ONLY,
@@ -219,6 +216,6 @@ var (
219216
},
220217
URL: testWorkspaceStatus.Spec.Url,
221218
WorkspaceID: testWorkspaceStatus.Metadata.MetaId,
222-
StartedAt: startedAt,
219+
Phase: wsapi.WorkspacePhase_RUNNING,
223220
}
224221
)

0 commit comments

Comments
 (0)