Skip to content

Commit 92abdf6

Browse files
TSultanovdamccorm
andauthored
[Playground] Resolve issue with SCIO examples failing on start due to a timeout (#24946)
* Initialize SBT cache during SCIO playground container build Run sbt tool during container build to let it download Scala dependencies form Maven during build time instead of having to wait for downloading all dependencies during first run of examples in container * Fix issue with reading of GRPC_TIMEOUT environment variables in CI/CD scripts * Fix cleanup of execution environment for Scala examples * Fix panic in preparers when an empty file is passed * Use better name for SCIO project directory * Run "sbt compile" during container build to fetch all Scala dependencies * Disable forking JVM in SBT to significantly reduce memory usage * Impose memory limits on local deployments of SCIO runner container to better imitate real deployments * Fine-tune Java GC to improve performance and memory usage of SCIO examples * Remove large blobs of text from common_test.go * Add `sbt` to the list of development dependencies * Clarify running of backend tests in Playground * Clarify local running of backend * Improve consistency in code blocks in backend Readme * Fixing trailing whitespace * Update playground/backend/README.md Co-authored-by: Danny McCormick <dannymccormick@google.com> * Update playground/backend/internal/utils/preparers_utils_test.go Co-authored-by: Danny McCormick <dannymccormick@google.com> --------- Co-authored-by: Danny McCormick <dannymccormick@google.com>
1 parent 545cadc commit 92abdf6

16 files changed

Lines changed: 384 additions & 184 deletions

File tree

playground/README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,44 @@ The following requirements are needed for development, testing, and deploying.
3535
- [Docker Compose](https://docs.docker.com/compose/install/)
3636
- [gcloud CLI](https://cloud.google.com/sdk/docs/install)
3737
- [gcloud Beta Commands](https://cloud.google.com/sdk/gcloud/reference/components/install)
38-
- [Cloud Datastore Emulator](https://cloud.google.com/sdk/gcloud/reference/components/install)
38+
- [Cloud Datastore Emulator](https://cloud.google.com/datastore/docs/tools/datastore-emulator)
39+
- [sbt](https://www.scala-sbt.org/)
40+
41+
### Google Cloud Shell Prerequisites Installation
42+
Google Cloud Shell already has most of the prerequisites installed. Only few tools need to be installed separately
43+
44+
#### Flutter
45+
```shell
46+
git config --global --add safe.directory /google/flutter
47+
flutter doctor
48+
```
49+
50+
#### Protobuf
51+
```shell
52+
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
53+
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
54+
dart pub global activate protoc_plugin
55+
npm install -g @bufbuild/buf
56+
```
57+
#### sbt
58+
```shell
59+
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
60+
echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
61+
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo -H gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/scalasbt-release.gpg --import
62+
sudo chmod 644 /etc/apt/trusted.gpg.d/scalasbt-release.gpg
63+
sudo apt-get update
64+
sudo apt-get install sbt
65+
```
66+
### Additional tools
67+
Google Cloud shell machines do not have `netcat` and `lsof` preinstalled. Install them using:
68+
```shell
69+
sudo apt install netcat lsof
70+
```
3971

4072
# Available Gradle Tasks
4173

4274
## Perform overall pre-commit checks
75+
> **Google Cloud Shell note:** run `unset GOOGLE_CLOUD_PROJECT` before running tests so they would use locally running datastore emulator.
4376
4477
```
4578
cd beam

playground/backend/README.md

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,84 @@ no setup.
2727

2828
## Getting Started
2929

30-
See [playground/README.md](../README.md) for details on requirements and setup.
30+
See [playground/README.md](../README.md) for details on installing development dependencies.
3131

3232
This section describes what is needed to run the backend application.
3333

3434
- Go commands to run/test the backend locally
3535
- Set up environment variables to run the backend locally
3636
- Running the backend via Docker
3737

38-
### Go commands to run/test application locally
38+
## Go commands to run/test application locally
3939

40+
### Prerequisite
41+
42+
> **Google Cloud Shell note:** `start_datastore_emulator.sh` script makes use of `nc` and `lsof` commands which are not installed on Google Cloud Shell machines. You can install them using `sudo apt install netcat lsof`.
43+
44+
> **Google Cloud Shell note:** run `unset GOOGLE_CLOUD_PROJECT` before running tests so they would use locally running datastore emulator.
45+
46+
Start datastore emulator
47+
```shell
48+
bash start_datastore_emulator.sh
49+
```
50+
51+
After you have finished running tests
52+
```shell
53+
bash stop_datastore_emulator.sh
54+
```
55+
56+
### Run/build
4057
Go to the backend directory:
4158

4259
```shell
43-
$ cd backend
60+
cd backend
4461
```
4562

46-
The following command is used to build and serve the backend locally:
63+
To run backend server on development machine without using docker you'll need first to prepare a working directory anywhere outside of Beam source tree:
64+
```shell
65+
mkdir ~/path/to/workdir
66+
```
67+
and then copy `datasets/` and `configs/` and `logging.properties` from [`playground/backend/`](/playground/backend/) directory:
68+
```shell
69+
cp -r {logging.properties,datasets/,configs/} ~/path/to/workdir
70+
```
4771

72+
In case if you want to start backend for Go SDK you additionally will also need to create a prepared mod dir and export an additional environment variable:
4873
```shell
49-
$ go run ./cmd/server/server.go
74+
export PREPARED_MOD_DIR=~/path/to/workdir/prepared_folder
75+
SDK_TAG=2.44.0 bash ./containers/go/setup_sdk.sh $PREPARED_MOD_DIR
5076
```
5177

78+
The following command will build and serve the backend locally:
79+
80+
```shell
81+
SERVER_PORT=<port> \
82+
BEAM_SDK=<beam_sdk_type> \
83+
APP_WORK_DIR=<path_to_workdir> \
84+
DATASTORE_EMULATOR_HOST=127.0.0.1:8888 \
85+
DATASTORE_PROJECT_ID=test \
86+
SDK_CONFIG=../sdks-emulator.yaml \
87+
go run ./cmd/server
88+
```
89+
90+
where `<port>` should be the value of port on which you want to have the backend server available; `<beam_sdk_type>` is a value of desired Beam SDK, possible values are `SDK_UNSPECIFIED`, `SDK_JAVA`, `SDK_PYTHON`, `SDK_GO`, `SDK_SCIO`; `<path_to_workdir>` should be set to path to your work dir, e.g. `~/path/to/workdir`.
91+
5292
Run the following command to generate a release build file:
5393

5494
```shell
55-
$ go build ./cmd/server/server.go
95+
go build ./cmd/server/server.go
5696
```
5797

98+
### Test
5899
Playground tests may be run using this command:
59100

60101
```shell
61-
$ go test ... -v
102+
go test ./... -v
62103
```
63104

64105
The full list of commands can be found [here](https://pkg.go.dev/cmd/go).
65106

66-
### Set up environment variables to run the backend locally
107+
## Set up environment variables to run the backend locally
67108

68109
These environment variables should be set to run the backend locally:
69110

@@ -96,7 +137,7 @@ default value and there is no need to set them up to launch locally:
96137
- `PROPERTY_PATH` - is the application properties path (default value = `.`)
97138
- `CACHE_REQUEST_TIMEOUT` - is the timeout to request data from cache (default value = `5 sec`)
98139

99-
### Application properties
140+
## Application properties
100141

101142
These properties are stored in `backend/properties.yaml` file:
102143

@@ -106,7 +147,7 @@ These properties are stored in `backend/properties.yaml` file:
106147
- `removing_unused_snippets_cron` - is the cron expression for the scheduled task to remove unused snippets.
107148
- `removing_unused_snippets_days` - is the number of days after which a snippet becomes unused.
108149

109-
### Running the server app via Docker
150+
## Running the server app via Docker
110151

111152
To run the server using Docker images there are `Docker` files in the `containers` folder for Java, Python and Go
112153
languages. Each of them processes the corresponding SDK, so the backend with Go SDK will work with Go

playground/backend/containers/scio/Dockerfile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ COPY --from=build /go/src/playground/backend/configs /opt/playground/backend/con
4848
COPY --from=build /go/src/playground/backend/logging.properties /opt/playground/backend/
4949
COPY --from=build /go/src/playground/backend/new_scio_project.sh /opt/playground/backend/
5050
COPY --from=build /go/src/playground/backend/internal/fs_tool/ExampleData.scala /opt/playground/backend/
51+
RUN chmod +x /opt/playground/backend/new_scio_project.sh
5152

5253
# Install sbt
5354
RUN echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | tee /etc/apt/sources.list.d/sbt.list &&\
@@ -64,8 +65,6 @@ RUN mkdir /opt/mitmproxy &&\
6465
mkdir /usr/local/share/ca-certificates/extra
6566
COPY allow_list_proxy.py /opt/mitmproxy/
6667
COPY allow_list.py /opt/mitmproxy/
67-
ENV HTTP_PROXY="http://127.0.0.1:8081"
68-
ENV HTTPS_PROXY="http://127.0.0.1:8081"
6968

7069
COPY src/properties.yaml /opt/playground/backend/properties.yaml
7170
COPY entrypoint.sh /
@@ -90,4 +89,18 @@ RUN chown -R appuser:appgroup /opt/playground/backend/executable_files/ \
9089
# Switch to appuser
9190
USER appuser
9291

92+
# Let sbt download files from Maven
93+
RUN mkdir -p /tmp/sbt-initialize
94+
WORKDIR /tmp/sbt-initialize
95+
RUN /opt/playground/backend/new_scio_project.sh
96+
WORKDIR /tmp/sbt-initialize/scio
97+
RUN sbt "+compile"
98+
WORKDIR /
99+
RUN rm -r /tmp/sbt-initialize
100+
101+
# Enable mitmproxy
102+
ENV HTTP_PROXY="http://127.0.0.1:8081"
103+
ENV HTTPS_PROXY="http://127.0.0.1:8081"
104+
ENV SBT_OPTS="-Xmx512M -XX:+UseG1GC -XX:+UseStringDeduplication"
105+
93106
ENTRYPOINT ["/entrypoint.sh"]

playground/backend/internal/code_processing/code_processing.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,21 +424,21 @@ func readGraphFile(pipelineLifeCycleCtx, backgroundCtx context.Context, cacheSer
424424
case <-ticker.C:
425425
if _, err := os.Stat(graphFilePath); err == nil {
426426
ticker.Stop()
427-
graph, err := utils.ReadFile(pipelineId, graphFilePath)
427+
graph, err := os.ReadFile(graphFilePath)
428428
if err != nil {
429429
logger.Errorf("%s: Error during saving graph to the file: %s", pipelineId, err.Error())
430430
}
431-
_ = utils.SetToCache(backgroundCtx, cacheService, pipelineId, cache.Graph, graph)
431+
_ = utils.SetToCache(backgroundCtx, cacheService, pipelineId, cache.Graph, string(graph))
432432
}
433433
// in case of timeout or cancel
434434
case <-pipelineLifeCycleCtx.Done():
435435
ticker.Stop()
436436
if _, err := os.Stat(graphFilePath); err == nil {
437-
graph, err := utils.ReadFile(pipelineId, graphFilePath)
437+
graph, err := os.ReadFile(graphFilePath)
438438
if err != nil {
439439
logger.Errorf("%s: Error during saving graph to the file: %s", pipelineId, err.Error())
440440
}
441-
_ = utils.SetToCache(backgroundCtx, cacheService, pipelineId, cache.Graph, graph)
441+
_ = utils.SetToCache(backgroundCtx, cacheService, pipelineId, cache.Graph, string(graph))
442442
}
443443
return
444444
}

playground/backend/internal/fs_tool/fs.go

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@ package fs_tool
1818
import (
1919
"beam.apache.org/playground/backend/internal/logger"
2020
"fmt"
21-
"io"
21+
"github.com/google/uuid"
2222
"io/fs"
2323
"os"
24-
"path/filepath"
25-
26-
"github.com/google/uuid"
2724

2825
pb "beam.apache.org/playground/backend/internal/api/v1"
2926
"beam.apache.org/playground/backend/internal/db/entity"
@@ -114,37 +111,6 @@ func (lc *LifeCycle) CreateSourceCodeFiles(sources []entity.FileEntity) error {
114111
return nil
115112
}
116113

117-
// CopyFile copies a file with fileName from sourceDir to destinationDir.
118-
func (lc *LifeCycle) CopyFile(fileName, sourceDir, destinationDir string) error {
119-
absSourcePath := filepath.Join(sourceDir, fileName)
120-
absDestinationPath := filepath.Join(destinationDir, fileName)
121-
sourceFileStat, err := os.Stat(absSourcePath)
122-
if err != nil {
123-
return err
124-
}
125-
126-
if !sourceFileStat.Mode().IsRegular() {
127-
return fmt.Errorf("%s is not a regular file", fileName)
128-
}
129-
130-
sourceFile, err := os.Open(absSourcePath)
131-
if err != nil {
132-
return err
133-
}
134-
defer sourceFile.Close()
135-
136-
destinationFile, err := os.Create(absDestinationPath)
137-
if err != nil {
138-
return err
139-
}
140-
defer destinationFile.Close()
141-
_, err = io.Copy(destinationFile, sourceFile)
142-
if err != nil {
143-
return err
144-
}
145-
return nil
146-
}
147-
148114
func (lc *LifeCycle) GetPreparerParameters() map[string]string {
149115
if lc.emulatorMockCluster == nil {
150116
return map[string]string{}

playground/backend/internal/fs_tool/fs_test.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,7 @@ func TestLifeCycle_CopyFile(t *testing.T) {
135135
}
136136
for _, tt := range tests {
137137
t.Run(tt.name, func(t *testing.T) {
138-
l := &LifeCycle{
139-
folderGlobs: tt.fields.folderGlobs,
140-
Paths: tt.fields.Paths,
141-
}
142-
if err := l.CopyFile(tt.args.fileName, tt.args.sourceDir, tt.args.destinationDir); (err != nil) != tt.wantErr {
138+
if err := utils.CopyFilePreservingName(tt.args.fileName, tt.args.sourceDir, tt.args.destinationDir); (err != nil) != tt.wantErr {
143139
t.Errorf("CopyFile() error = %v, wantErr %v", err, tt.wantErr)
144140
}
145141
})

playground/backend/internal/setup_tools/life_cycle/life_cycle_setuper.go

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
"beam.apache.org/playground/backend/internal/db/entity"
3333
"beam.apache.org/playground/backend/internal/fs_tool"
3434
"beam.apache.org/playground/backend/internal/logger"
35-
"beam.apache.org/playground/backend/internal/utils"
35+
utils "beam.apache.org/playground/backend/internal/utils"
3636
)
3737

3838
const (
@@ -41,13 +41,11 @@ const (
4141
javaLogFilePlaceholder = "{logFilePath}"
4242
goModFileName = "go.mod"
4343
goSumFileName = "go.sum"
44-
scioProjectName = "y"
44+
bashCmd = "bash"
45+
scioProjectName = "scio"
4546
scioProjectPath = scioProjectName + "/src/main/scala/" + scioProjectName
4647
logFileName = "logs.log"
4748
defaultExampleInSbt = "WordCount.scala"
48-
shCmd = "sh"
49-
rmCmd = "rm"
50-
cpCmd = "cp"
5149
scioProject = "new_scio_project.sh"
5250
scioCommonConstants = "ExampleData.scala"
5351
)
@@ -128,11 +126,11 @@ func Setup(sdk pb.Sdk, sources []entity.FileEntity, pipelineId uuid.UUID, workin
128126
// prepareGoFiles prepares file for Go environment.
129127
// Copy go.mod and go.sum file from /path/to/preparedModDir to /path/to/workingDir/pipelinesFolder/{pipelineId}
130128
func prepareGoFiles(lc *fs_tool.LifeCycle, preparedModDir string, pipelineId uuid.UUID) error {
131-
if err := lc.CopyFile(goModFileName, preparedModDir, lc.Paths.AbsoluteBaseFolderPath); err != nil {
129+
if err := utils.CopyFilePreservingName(goModFileName, preparedModDir, lc.Paths.AbsoluteBaseFolderPath); err != nil {
132130
logger.Errorf("%s: error during copying %s file: %s\n", pipelineId, goModFileName, err.Error())
133131
return err
134132
}
135-
if err := lc.CopyFile(goSumFileName, preparedModDir, lc.Paths.AbsoluteBaseFolderPath); err != nil {
133+
if err := utils.CopyFilePreservingName(goSumFileName, preparedModDir, lc.Paths.AbsoluteBaseFolderPath); err != nil {
136134
logger.Errorf("%s: error during copying %s file: %s\n", pipelineId, goSumFileName, err.Error())
137135
return err
138136
}
@@ -144,7 +142,7 @@ func prepareGoFiles(lc *fs_tool.LifeCycle, preparedModDir string, pipelineId uui
144142
//
145143
// and update this file according to pipeline.
146144
func prepareJavaFiles(lc *fs_tool.LifeCycle, workingDir string, pipelineId uuid.UUID) error {
147-
err := lc.CopyFile(javaLogConfigFileName, workingDir, lc.Paths.AbsoluteBaseFolderPath)
145+
err := utils.CopyFilePreservingName(javaLogConfigFileName, workingDir, lc.Paths.AbsoluteBaseFolderPath)
148146
if err != nil {
149147
logger.Errorf("%s: error during copying logging.properties file: %s\n", pipelineId, err.Error())
150148
return err
@@ -194,7 +192,7 @@ func updateJavaLogConfigFile(paths fs_tool.LifeCyclePaths) error {
194192
}
195193

196194
func prepareSbtFiles(lc *fs_tool.LifeCycle, pipelineFolder string, workingDir string) (*fs_tool.LifeCycle, error) {
197-
cmd := exec.Command(shCmd, filepath.Join(workingDir, scioProject))
195+
cmd := exec.Command(bashCmd, filepath.Join(workingDir, scioProject))
198196
cmd.Dir = pipelineFolder
199197
_, err := cmd.Output()
200198
if err != nil {
@@ -210,30 +208,29 @@ func prepareSbtFiles(lc *fs_tool.LifeCycle, pipelineFolder string, workingDir st
210208
projectFolder, _ := filepath.Abs(filepath.Join(pipelineFolder, scioProjectName))
211209
executableName := lc.Paths.ExecutableName
212210

213-
_, err = exec.Command(rmCmd, filepath.Join(absFileFolderPath, defaultExampleInSbt)).Output()
211+
err = os.Remove(filepath.Join(absFileFolderPath, defaultExampleInSbt))
214212
if err != nil {
215213
return lc, err
216214
}
217215

218-
_, err = exec.Command(cpCmd, filepath.Join(workingDir, scioCommonConstants), absFileFolderPath).Output()
216+
err = utils.CopyFilePreservingName(scioCommonConstants, workingDir, absFileFolderPath)
219217
if err != nil {
220218
return lc, err
221219
}
222220

223-
lc = &fs_tool.LifeCycle{
224-
Paths: fs_tool.LifeCyclePaths{
225-
SourceFileName: fileName,
226-
AbsoluteSourceFileFolderPath: absFileFolderPath,
227-
AbsoluteSourceFilePath: absFilePath,
228-
ExecutableFileName: fileName,
229-
AbsoluteExecutableFileFolderPath: absFileFolderPath,
230-
AbsoluteExecutableFilePath: absFilePath,
231-
AbsoluteBaseFolderPath: absFileFolderPath,
232-
AbsoluteLogFilePath: absLogFilePath,
233-
AbsoluteGraphFilePath: absGraphFilePath,
234-
ProjectDir: projectFolder,
235-
},
236-
}
237-
lc.Paths.ExecutableName = executableName
221+
lc.Paths = fs_tool.LifeCyclePaths{
222+
SourceFileName: fileName,
223+
AbsoluteSourceFileFolderPath: absFileFolderPath,
224+
AbsoluteSourceFilePath: absFilePath,
225+
ExecutableFileName: fileName,
226+
AbsoluteExecutableFileFolderPath: absFileFolderPath,
227+
AbsoluteExecutableFilePath: absFilePath,
228+
AbsoluteBaseFolderPath: absFileFolderPath,
229+
AbsoluteLogFilePath: absLogFilePath,
230+
AbsoluteGraphFilePath: absGraphFilePath,
231+
ProjectDir: projectFolder,
232+
ExecutableName: executableName,
233+
}
234+
238235
return lc, nil
239236
}

0 commit comments

Comments
 (0)