github.com/matrixorigin/matrixone@v1.2.0/pkg/udf/pythonservice/service.go (about) 1 // Copyright 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package pythonservice 16 17 import ( 18 "fmt" 19 "github.com/matrixorigin/matrixone/pkg/logutil" 20 "io" 21 "os" 22 "os/exec" 23 "path" 24 "path/filepath" 25 "strconv" 26 "sync" 27 "sync/atomic" 28 ) 29 30 type service struct { 31 cfg Config 32 process *os.Process 33 log io.WriteCloser 34 mutex sync.Mutex 35 } 36 37 func NewService(cfg Config) (PythonUdfServer, error) { 38 err := cfg.Validate() 39 if err != nil { 40 return nil, err 41 } 42 return &service{cfg: cfg}, nil 43 } 44 45 var severNo int32 = 0 46 47 func (s *service) Start() error { 48 s.mutex.Lock() 49 defer s.mutex.Unlock() 50 51 if s.process == nil { 52 var err error 53 var file string 54 55 ex, err := os.Executable() 56 if err != nil { 57 return err 58 } 59 exePath := filepath.Dir(ex) 60 61 if path.IsAbs(file) { 62 file = path.Join(s.cfg.Path, "server.py") 63 } else { 64 file = path.Join(exePath, s.cfg.Path, "server.py") 65 } 66 _, err = os.Stat(file) 67 if err != nil { 68 return err 69 } 70 71 no := strconv.Itoa(int(atomic.AddInt32(&severNo, 1))) 72 s.log, err = os.OpenFile(path.Join(exePath, "pyserver"+no+".log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) 73 if err != nil { 74 return err 75 } 76 defer func() { 77 if err != nil { 78 s.log.Close() 79 } 80 }() 81 executable := s.cfg.Python 82 if executable == "" { 83 executable = "python" 84 } 85 86 cmd := exec.Command(executable, "-u", file, "--address="+s.cfg.Address) 87 cmd.Stdout = s.log 88 cmd.Stderr = s.log 89 go func(cmd *exec.Cmd) { 90 logutil.Infof("start python server process, command: %s", cmd.String()) 91 err := cmd.Run() 92 // panic the launch process if python server exit with an error 93 if err != nil { 94 panic(fmt.Sprintf("python server exit with error: %v", err)) 95 } 96 }(cmd) 97 98 s.process = cmd.Process 99 } 100 101 return nil 102 } 103 104 func (s *service) Close() error { 105 s.mutex.Lock() 106 defer func() { 107 s.mutex.Unlock() 108 if s.log != nil { 109 s.log.Close() 110 } 111 }() 112 113 if s.process != nil { 114 err := s.process.Kill() 115 if err != nil { 116 return err 117 } 118 s.process = nil 119 } 120 121 return nil 122 }