npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

gammaray-cli

v1.0.0

Published

CLI for managing apps for the Gammaray application server

Downloads

27

Readme

Gammaray application server engine

A scalable application server engine.

Motivation

Throughout my history of working on 3D games and later MMO games as a backend developer, I realized that for client development there is a lot of engines out there that basically solve all the common problems for you:

  • The whole rendering pipeline with all VSD/HSR techniques
  • Different implementations for the rendering API (OpenGL, WebGL, Direct3D, Metal, ...)
  • Collision detection/response with physics
  • Animations + state machine

and so on. All you have to do is basically just feeding the engine with textures, 3D models and game logic/scripts.

On the backend side however you have to deal with completely different problems:

  • Building the API or protocol
  • State handling
  • Caching
  • Scalability
  • Transaction handling which is highly depending on the specific DBMS
  • Which DBMS to use? Multiple ones?
  • How to address/query for an entity/state? Is a SQL query enough? Do we need an extra search index?
  • Microservices?
  • Which architecture? Domain-Driven-Design?

and so on. You'll come up with very different solutions depending on the specific performance needs. For non-frequent access the solution is stateless where state is always read from the database and uses a SQL database with transactions. For high frequent access (e.g. real time movements of entities in games) you store in memory (stateful) and store it back to a document based DBMS on a lower frequency. Then the requirements change and the SQL solution needs to be high performance as well, and you start asking yourself why you didn't handle all business entities with one generic solution in the first place. Scaling up the SQL database will increase the cost drastically. And when you start dealing with in memory state handling, it opens up a whole new bag of problems:

  • Which concurrency model should be used?
  • Classical lock based which will end up in deadlocks as soon as complexity increases
  • STM which only makes sense when you don't expect too many transactional collisions
  • Isolated mutable state, which requires rewriting the whole existing code since state is accessed in a very different way
  • Nowadays, your backend isn't just one physical machine: On which node is the desired state located at? On the one the client is connected to? But what if it is shared state?

So what we need is something similar to the idea of a Game/3D-Engine - but that solves most of the backend problems instead.

Setting up the application server with docker

In case you don't have any docker system on your local computer yet, please refer to the official documentation / tutorials at docker.com or use a third party one like Rancher Desktop. For local development and demo purposes you should use the docker image gammarayapp/gammaray-demo. This can either be pulled through the respective UI of your docker system or through the command line: docker pull gammarayapp/gammaray-demo

Starting one node of the application server is already enough to run any application: docker run --name=gammaray1 -p 8080:8080 --rm gammarayapp/gammaray-demo

For starting another node you can just change the name and use the next free port e.g.: docker run --name=gammaray2 -p 8081:8080 --rm gammarayapp/gammaray-demo

Both nodes should find each other automatically and form a cluster. Nodes can be started and stopped randomly. As long as at least one node stays up, the application server cluster is fully functional. Of course, when you run multiple nodes locally, each node has to have a different port. If you shut down the first node with port 8080, all the scripts like for deployment and opening swagger won't work anymore and need to be changed to one of the nodes ports that are up.

The demo version uses an in-memory database internally. So all data will be lost after the last node of a cluster is shut down.

The CLI (gmr)

npm install -g gammaray-cli

Create a new application:

gmr new <name>

This creates an application with just one simple helloworld GET endpoint.

Deploy the application

The package script "build-deploy" does what the name suggests. So just run "npm run build-deploy" on the command line in the root directory of your just created application or use any other tool of your choice for running the package scripts (most IDEs for node.js support that). This deploys the application to the appserver that is configured in "deploy.json". There is also another npm script called "swagger" which opens the OpenAPI definition using Swagger. And there is an all-in-one npm script called "start" which builds, deploys and opens swagger with just one click. The deployment only communicates with one node of the cluster, which is enough. The whole cluster will automatically know about the deployed app.

Versioning

This CLI, the gammaray-app package (that is included in each application) and the application server itself have a specific version using the major.minor.patch pattern e.g. "1.1.0".

  • Major: Breaking changes. Existing features and interfaces may have changed. Apps with a different major version of gammaray-app than the application server or this gammaray-cli package might be incompatible to each other.
  • Minor: New features, not affecting any other existing feature or interface. When the application server has a higher number that the app, it is ok since the app just simply will not use those features. But when the app has a higher number than the application server, it might use features that the application server doesn't support.
  • Patch: Internal fixes, that do not affect the compatibility to other applications.

Each minor version always contains all previous fixes e.g. "1.2.0" contains all the fixes of "1.1.3".

CLI global vs. local dev dependency

At least right now all the scripts are based on the globally installed CLI to reduce confusion. Feel free to install this CLI locally as a dev dependency and make all your scripts use that instead.

Writing an app for the Gammaray application server

A gammaray application has to be implemented using the "gammaray-app" definitions / SDK. Every logic or behaviour needs to be written inside defined functions from this SDK. There is no manual database access, you just address one entity by its id and a defined function will be called were you can read, create or modify the entity. The entity should only be touched inside the scope of this function call. You syntactically can of course do wild things with it e.g. passing it to other SDK functions or doing something with it asynchronously, but there is no guarantee for consistency anymore. After "touching" an entity the respective function can return specific things to indicate what should be done e.g. nothing since you just read something from the entity or other indicators that you modified it, or it is supposed to be deleted. No transactions or locking is necessary, nor any caching or performance optimization - this is all handled by the application server. You are basically just writing the business entities and logic.

Most topics are documented in the example projects, but some general rules to note: The payload of every request/response must be JSON. In case any other data structure needs to be transferred, it can just be wrapped as a string inside a JSON object. Don't use async - besides that this is not even possible, it is not necessary since the whole design and processing under the hood is already happening asynchronously, and you won't do any manual I/O access.

For better understanding here are some example projects: https://github.com/gammarayapp/example-apps