Voedger Internals
  • Introduction
  • 💡Concepts
    • Event Sourcing & CQRS
    • Editions (deprecated)
      • Community Edition
      • Standart Edition
      • Standart Edition (v1)
  • 🚀Server
    • Overview (Server)
    • Design
      • Query Processor
      • API Gateway implementation
      • N1 Cluster
      • N3 Cluster
      • N5 Cluster
      • Orchestration
      • Sequences
      • Packages overview
        • sys
        • registry
    • Features
      • API Gateway
        • API v2
          • Conventions
            • API URL
            • HTTP methods and processors
            • Naming conventions
            • Query constraints
            • Error handling
          • Documents and records
            • Create document or record
            • Update document or record
            • Deactivate document or record
            • Read document or record
            • Read from CDoc collection
          • Queries
            • Read from query
          • Views
            • Read from view
          • Commands
            • Execute command
          • BLOBs
            • Create BLOB
            • Read BLOB
          • Temporary BLOBs
            • Create temporary BLOB
            • Read temporary BLOB
          • Schemas
            • List app workspaces
            • List workspace roles
            • Read workspace role schema
        • API v1
          • API Conventions
          • BLOBs
      • Admin Endpoint
      • Clusters
        • Bootstrap
        • Monitoring
        • Secure prometheus and grafana
        • Alerting
        • Maintenance
          • SELECT, UPDATE
      • VVMs
      • Applications
        • Deploy Application
        • Sidecar Applications
      • AuthNZ
        • Issue Principal Token
        • Refresh Principal Token
        • Enrich Principal Token
        • ACL Rules
        • Global Roles
      • Data types
        • Core types
        • Small integers
        • Uniques With Multiple Fields
        • Verifiable Fields
      • Workspaces
        • Create Workspace
        • Deactivate Workspace
        • See also (Workspaces)
      • Invites
        • Invite to Workspace
        • Join Workspace
        • Leave Workspace
        • Cancel sent Invite
        • Cancel accepted Invite
        • Update Invite roles
      • Users
        • Create a new user
        • Change user password
        • Send Email
        • Reset password
        • Change Email
      • Notifications
        • Heartbeats
      • Devices
        • Create a new device
        • Join device to workspace
      • Jobs
      • DMBS Drivers
        • AmazonDB Driver
      • Frozen
        • Ephemeral Storage
        • Storage Extensions
  • 🛠️Framework
    • Overview (Framework)
    • Features
      • vpm
      • vpm init
      • vpm tidy
      • vpm baseline
      • vpm orm
      • vpm build
      • API for testing
  • Development
    • Requirements Management
    • Requirements Management (Overview)
Powered by GitBook
On this page
  • Content
  • Principles
  • Create Login
  • Create ChildWorkspace
  • c.sys.InitChildWorkspace()
  • aproj.sys.InvokeCreateWorkspaceID()
  • c.sys.CreateWorkspaceID()
  • aproj.sys.InvokeCreateWorkspace()
  • c.sys.CreateWorkspace()
  • aproj.sys.InitializeWorkspace()
  • UpdateOwner(wsParams, new.WSID, wsDescr.initError)
  • Notes
  • See also

Was this helpful?

Edit on GitHub
  1. 🚀Server
  2. Features
  3. Workspaces

Create Workspace

PreviousWorkspacesNextDeactivate Workspace

Last updated 2 months ago

Was this helpful?

Create User/Login/App Workspaces

Content

Principles and Processes

Commands

Projectors

Additional Information

Principles

  • AppWorkspaces are created by system when an App is deployed to a Cluster.

  • Workspaces of other kinds must be explicitely created (and initialized).

    • It is not possible to work with uninitialized workspaces.

  • Client calls c.registry.CreateLogin using pseudo WS calculated as (main cluster, crc16(login))

  • If router sees that baseWSID of WSID is < MaxPseudoBaseWSID then it replaces that pseudo base WSID with app base WSID:

    • (main cluser, (baseWSID %% numAppWorkspaces) + FirstBaseAppWSID)

  • crc16 = crc32.ChecksumIEEE & (MaxUint32 >> 16)

  • cdoc.registry.Login stores login hash only

Create Login

  • FIXME: cdoc.sys.WorkspaceID is a large collection, must be wdoc.sys.WorkspaceID

entity
app
ws
cluster

c.registry.CreateLogin()

sys/registry

pseudoWS

main

cdoc.registry.Login (owner) aproj.sys.InvokeCreateWorkspaceID

sys/registry

app ws

main

c.sys.CreateWorkspaceID() cdoc.sys.WorkspaceID aproj.sys.InvokeCreateWorkspace()

Target App

(Target Cluster, base App WSID)

Target Cluster

c.sys.CreateWorkspace() cdoc.sys.WorkspaceDescriptor cdoc.sys.UserProfile/DeviceProfile aproj.sys.InitializeWorkspace()

Target App

new WSID

Target Cluster

Create ChildWorkspace

entity
app
ws
cluster

c.sys.InitChildWorkspace() cdoc.sys.ChildWorkspace (owner) aproj.sys.InvokeCreateWorkspaceID()

Tagret App

Profile

Profile Cluster

c.sys.CreateWorkspaceID() cdoc.sys.WorkspaceID aproj.sys.InvokeCreateWorkspace()

Target App

(Target Cluster, CRC16(ownerWSID+"/"+wsName))

Target Cluster

c.sys.CreateWorkspace() cdoc.sys.WorkspaceDescriptor cdoc.$wsKind (air.Restaurant) aproj.sys.InitializeWorkspace()

Target App

new WSID

Target Cluster

c.sys.InitChildWorkspace()

  • AuthZ: Owner

    • PrincipalToken in header

    • PrincipalToken.ProfileWSID == Request.WSID

  • Params: wsName, wsKind, wsKindInitializationData, templateName, templateParams (JSON), wsClusterID

  • Check that wsName does not exist yet: View{pk: dummy, cc: wsName, value: idOfChildWorkspace}

    • 409 conflict

  • Create CDoc {wsName, wsKind, wsKindInitializationData, templateName, templateParams, wsClusterID, /* Updated aftewards by UpdateOwner*/ WSID, wsError}

    • Trigger Projector<A, InvokeCreateWorkspaceID>

    • Trigger Projector

Subject:

  • Call WS[Subject.ProfileWSID].InitChildWorkspace()

    • wsName

    • wsKind

    • wsKindInitializationData // JSON

    • wsClusterID // Ideally user should be asked which cluster to use

  • Call WS[Subject.ProfileWSID].q.QueryChildWorkspaceByName() until (WSID || wsError)

    • Returns all fields of CDoc

aproj.sys.InvokeCreateWorkspaceID()

  • Triggered by CDoc

  • PseudoWSID = NewWSID(wsClusterID, CRC32(wsName))

  • // PseudoWSID is needed to avoid WSID generation bottlenecks

  • Call WS[$PseudoWSID].c.CreateWorkspaceID()

c.sys.CreateWorkspaceID()

  • AuthZ: System

    • SystemToken in header

  • Params: (wsParams) ownerWSID, ownerQName2, ownerID, wsName, wsKind, wsKindInitializationData, templateName, templateParams (JSON), wsClusterID

    • ownerWSID

      • For profiles: PseudoWSID is calculated (from login) by client as NewWSID(1, ISO CRC32(login))

      • For subject workspaces: ProfileWSID

  • Check that ownerWSID + wsName does not exist yet: View to deduplication

    • pk: ownerWSID

    • cc: wsName

    • val: WSID

  • Get new WSID from View[NextBaseWSID]

  • Create WDoc[WorkspaceID]{wsParams, WSID: $NewWSID}

    • Triggers Projector[A, InvokeCreateWorkspace]

    • Triggers Projector[WorkspaceIDIdx]

aproj.sys.InvokeCreateWorkspace()

  • Triggered by WDoc

  • WS[new.WSID].c.CreateWorkspace()

c.sys.CreateWorkspace()

  • AuthZ: System

    • SystemToken in header

  • Params: wsParams, WSID

  • Check that CDoc<sys.WorkspaceDescriptor> does not exist yet (IRecords.GetSingleton())

    • return ok otherwise

  • if wsKindInitializationData is not valid

    • error = "Invalid workspaced descriptor data: ???"

  • Create CDoc<sys.WorkspaceDescriptor>{wsParams, WSID, createError: error, createdAtMs int64, /* Updated aftewards */ initStartedAtMs int64, initError, initCompletedAtMs int64}

    • Trigger Projector<A, InitializeWorkspace>

  • if not error

    • Create CDoc{wsKindInitializationData}

aproj.sys.InitializeWorkspace()

  • // error handling: just return

  • // Triggered by CDoc<sys.WorkspaceDescriptor>

  • If updated return // We do NOT react on update since we update record from projector

  • If len(new.createError) > 0

    • UpdateOwner(wsParams, new.WSID, new.createError)

    • return

  • // Must exist

  • wsDescr = GetSingleton(CDoc<sys.WorkspaceDescriptor>)

  • if wsDecr.initStartedAtMs == 0

    • WS[currentWS].c.sys.CUD(wsDescr.ID, initStartedAtMs)

    • err = workspace.buildWorkspace() // to init data

    • if err != nil: error = ("Workspace data initialization failed: v", err)

    • WS[currentWS].c.sys.CUD(wsDescr.ID, initError: error, initCompletedAtMs)

    • UpdateOwner(wsParams, new.WSID, error)

    • return

  • else if wsDecr.initCompletedAtMs == 0

    • error = "Workspace data initialization was interrupted"

    • WS[currentWS].c.sys.CUD(wsDescr.ID, initError: error, initCompletedAtMs)

    • UpdateOwner(wsParams, new.WSID, error)

    • return

  • else // initCompletedAtMs > 0

    • UpdateOwner(wsParams, new.WSID, wsDescr.initError)

    • return

UpdateOwner(wsParams, new.WSID, wsDescr.initError)

  • WS[wsParams.ownerWSID].c.sys.CUD(wsParams.ownerID, WSID, wsError)

Notes

  • Unable to work at AppWS because it is located in sys/registry app whereas we are logged in a target app. "token issued for another application" error will be result

  • Workspace initialized check is made in command processor only. Query processor just returns empty result because there is no data in non-inited workspace

    • c.sys.CreateWorkspace or c.sys.CreateWorkspaceID or (c.sys.CUD + System Principal) -> ok

    • cdoc.WorkspaceDescriptor exists ->

      • .initCompletedAtMs > 0 && len(.initError) == 0 -> ok

      • c.sys.CUD -> will check after parseCUDs stage if we are updating cdoc.WorkspaceDescriptor or WDoc<BLOB> now

        • there is update only of (cdoc.WorkspaceDescriptor or WDoc<BLOB>) only among CUDs -> ok

    • -> 403 forbidden + workspace is not initialized

  • aproj.sys.InitializeWorkspace: wsKind == registry.AppWorkspace -> self-initialized already, skip further work

  • App Workspace has cdoc.WorkspaceDescriptor only, there is no cdoc.$wsKind

  • cdoc.WorkspaceDescriptor, cdoc.WorkspaceID, c.sys.CeateWorkspace, c.sys.CreateWorkspaceID:

    • ownerID, ownerQName2, ownerWSID fields are made non-required becuase they are empty in App Workspace

    • ownerApp field added to know in which app to update the owner

    • for each app

      • PLog and WLog offsets are starting from istructs.FirstOffset

      • for each App WS Number

        • AppWSID = (mainClusterID, wsNum + FirstBaseAppWSID)

        • cdoc.WorkspaceDescriptor exists already at AppWSID -> skip

        • generate new Sync Raw Event

        • add CUD create cdoc.WorkspaceDescriptor to the Event

        • put PLog

        • apply PLog event to records

        • put WLog

        • incr PLog and WLog offsets

    • //TODO AppWorkspaces must be created when application is deployed

  • App Workspaces amount is defined per app, default 10

See also

  • Originated from launchpad: Create Workspace v2, 21010

AppWorkspaces are after wiring the VVM before launch

initialized automatically
Principles
Create Login Process
Create ChildWorkspace Process
c.sys.InitChildWorkspace()
c.sys.CreateWorkspaceID()
c.sys.CreateWorkspace()
aproj.sys.InvokeCreateWorkspaceID()
aproj.sys.InvokeCreateWorkspace()
aproj.sys.InitiateWorkspace()
Implementation Notes
See Also