-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sockets: Go rewrite #3536
Sockets: Go rewrite #3536
Conversation
912a397
to
8837727
Compare
I should probably explain the changes I made:
On the Go side of things:
I had to use a rather hacky way to allow compiling the Go files. Rather than using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job on this 👍.
sockets/lib/commands.go
Outdated
const CHANNEL_REMOVE string = "-" | ||
const CHANNEL_BROADCAST string = "#" | ||
const SUBCHANNEL_MOVE string = "." | ||
const SUBCHANNEL_BROADCAST string = ":" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure those could be byte
instead of string
. Also, they could be grouped like this.
const (
SOCKET_CONNECT byte = '*'
SOCKET_DISCONNECT byte = '!'
...
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally I chose string
over byte
so I wouldn't have to cast command tokens to string
in Command.Message
, but looking over the other code dealing with these, it makes no difference which type it is. It'd make more sense for them to be byte
instead
sockets/lib/ipc.go
Outdated
// This must be a byte that stringifies to either a hexadecimal or Unicode | ||
// escape code. Otherwise, it would be possible for someone to send a message | ||
// with the delimiter and break up messages. | ||
const DELIM byte = '\u0003' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
byte
is not Unicode character, you may want to use '\x03'
instead to clarify (this is allowed, because its within byte
bounds, but it's not exactly clear).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make more sense to make it a rune
for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind, I forgot the bufio.Reader
instance requires that Reader.ReadBytes
takes a byte as its delimiter
sockets/lib/ipc.go
Outdated
port: port, | ||
addr: addr, | ||
conn: conn, | ||
listening: false} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably look better if }
would be on its separate line (with trailling comma after false
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it looks better like this, since it doesn't take up as much space
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would extremely strongly prefer it on a separate line, but I'll defer to gofmt
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gofmt
has no opinion on this, and neither does Google's style guide afaik. }
on a separate line it is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand, no multiline struct literal in Go style guide follows this style (singleline do, but not multiline).
(I really feel like gofmt should be able to do more than just modify whitespace, but currently it only can modify whitespace, it doesn't modify tokens in any case)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The closest example I can find is this, which does follow this style https://golang.org/doc/effective_go.html#maps
ba5a655
to
1f84f59
Compare
4cd84e4
to
2b71e64
Compare
Can someone take a look at the refactors I've made to |
9f7b077
to
1ab65e3
Compare
sockets/lib/commands.go
Outdated
// themselves. | ||
switch token { | ||
case SOCKET_DISCONNECT: | ||
count = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably can directly return instead of returning a variable. Also, some cases can be merged to avoid code duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand go test
complains that there's no return statement at the end of the function if I return from the switch cases. I could keep count
, but still merge them so the switch isn't so redundant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. At the same time, receiving unexpected message is a bug, and probably should be reported somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it's possible if a mistake is made with /eval or if new message types are introduced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User still should be informed about it.
sockets/lib/multiplexer.go
Outdated
"sync" | ||
"sync/atomic" | ||
|
||
"github.com/igm/sockjs-go/sockjs" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be using development version of SockJS (as opposed to stable gopkg.in/igm/sockjs-go.v2/sockjs
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stable version doesn't have *session.Request
,yet, which is needed to get the socket's remote address and X-Forwarded-For
headers to pass back to the parent process
sockets/lib/multiplexer.go
Outdated
smux sync.Mutex // nsid and sockets mutex. | ||
channels map[string]Channel // Map of channel (i.e. room) IDs to channels. | ||
cmux sync.Mutex // channels mutex. | ||
scre *regexp.Regexp // Regex for splitting subchannel broadcasts into their three messages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need to be part of a struct, considering scre
is a constant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it fits better in the struct, since only *Multiplexer.subchannelBroadcast
needs to be able to use it, unlike the other constants floating around in the file's scope
sockets/lib/ipc.go
Outdated
listening: false, | ||
} | ||
|
||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to use return
syntax here, when return c, nil
I think would be more clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At one point I was returning err
instead of creating my own errors to return, so it made more sense at the time. It doesn't anymore though
sockets/lib/master.go
Outdated
type worker struct { | ||
wpool chan chan Command // The master's pool of worker command queues. | ||
cmdch chan Command // Queue for incoming commands from CmdQueue. | ||
quit chan bool // Channel used to kill the worker when needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use close
on cmdch
instead of making another channel just for closing purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't that risk deadlocking if *master.Listen
tries to send a command to worker.cmdch
after it has closed?
sockets/lib/commands.go
Outdated
// The multiplexer and the IPC connection both implement this interface. Its | ||
// purpose is solely to allow the two structs to be used in Command. | ||
type CommandIO interface { | ||
Process(Command) (err error) // Invokes one of its methods using the command's token and parametres. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to name output variable name of interface, it's clear that error
type is... an error.
sockets/lib/ipc.go
Outdated
} | ||
|
||
// Final step in evaluating commands targeted at the IPC connection. | ||
func (c *Connection) Process(cmd Command) (err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just use unnamed error
, no need to use obvious output variable names.
pokemon-showdown
Outdated
console.log('The GOPATH environment variable is not set! It is required in order to run the server using Go.'); | ||
process.exit(0); | ||
} | ||
if (!process.env.GOROOT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GOROOT is optional, and probably shouldn't be set, as its default should be fine. Don't require it, it's probably a bad idea to set that variable explicitly anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's a bad idea to set it to something other than /usr/local/go
or whatever the equivalent on Windows is, but Go will still complain about it sometimes. Maybe I'm remembering this from using older versions though. Using go env GOROOT
seems like the safer option to me just to be explicit without making users having to set $GOROOT
manually
pokemon-showdown
Outdated
} catch (e) {} | ||
|
||
if (config && config.golang) { | ||
if (!process.env.GOPATH) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GOPATH is optional in newer versions of Golang. You may want to assume $HOME/go
path if it's not set or run go env GOPATH
to obtain its location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go env GOPATH
sounds like the safer bet
pokemon-showdown
Outdated
let stat; | ||
let needsSrcDir = false; | ||
try { | ||
stat = fs.lstatSync(path.resolve(GOPATH, 'src/github.com/Zarel')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GOPATH
is allowed to be a colon separated list (semicolon on Windows), in such an event, you may want to consider its first element. You can split the list by path.delimiter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, I wasn't aware Go would allow that. When I tested it with colon separated paths, only the first one in the list got used, so doing this seems sound
pokemon-showdown
Outdated
if (!stat || !stat.isSymbolicLink()) { | ||
try { | ||
if (process.platform === 'win32') { | ||
child_process.execSync(`mklink /J ${tarPath} ${srcPath}`, {stdio: 'inherit'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fs.symlinkSync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First time I tried that for Windows symlinks it required admin access, but I didn't realize junctions didn't at the time, which is dumb since I'm already using junctions here.
pokemon-showdown
Outdated
if (process.platform === 'win32') { | ||
child_process.execSync(`mklink /J ${tarPath} ${srcPath}`, {stdio: 'inherit'}); | ||
} else { | ||
child_process.execSync(`ln -sF ${srcPath} ${tarPath}`, {stdio: 'inherit'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why are you using -F
, it's pointless here, as this isn't a hard link.
Also, forgot to mention that |
It doesn't kill the process for you when you try that? It does when I try it. It already throws an error when that happens, but I can change it to log the error to console and exit the process instead. |
It also exits if |
52954fd
to
2ddba56
Compare
It's been a month since my last commit, but I honestly can't think of any glaring issues left to fix. Is there some way to give this a test run on a reasonably active server? I still think it's too soon to be sure that this won't break on main, but any types of bugs left to find are not going to be the type I can find screwing around with a server with a dozen users. |
Sure, rebase it and I'll test it on Smogtours. |
Rebased |
92aab9d
to
d0110b3
Compare
...not sure why I commented and never got around to it. Now it actually is though |
9445c21
to
ddb4153
Compare
Um, we should probably deal with the other sockets pullreq first. |
Rebased |
Apparently still conflicting? |
Also: is it possible for you to pullreq into a new branch instead of directly into master? |
Alright |
|
WIP
This turned into a pretty substantial refactor of the Node version of
sockets-related code to not only make using Go child processes
possible, but to optimize it and make unit testing of it and any code
dependent on it possible to write entirely synchronously.
sockets.js
and
sockets-workers.js
are now written in Typescript, though they won'tbe able to be transpiled until after
repl.js
,crashmonitor.js
,Config
,Users
,Dnsbl
, andMonitor
work with it as well.
Breaking changes:
Fixes #2943